Mercurial > hg > AnnotationManagerN4J
annotate src/main/java/de/mpiwg/itgroup/annotations/restlet/AnnotatorResourceImpl.java @ 70:2b1e6df5e21a
added lgpl_v3 license information.
author | casties |
---|---|
date | Thu, 06 Mar 2014 15:09:04 +0100 |
parents | c48435e7f312 |
children | 4c2cea836bc0 |
rev | line source |
---|---|
3 | 1 /** |
2 * Base class for Annotator resource classes. | |
3 */ | |
4 package de.mpiwg.itgroup.annotations.restlet; | |
5 | |
70 | 6 /* |
7 * #%L | |
8 * AnnotationManager | |
9 * %% | |
10 * Copyright (C) 2012 - 2014 MPIWG Berlin | |
11 * %% | |
12 * This program is free software: you can redistribute it and/or modify | |
13 * it under the terms of the GNU Lesser General Public License as | |
14 * published by the Free Software Foundation, either version 3 of the | |
15 * License, or (at your option) any later version. | |
16 * | |
17 * This program is distributed in the hope that it will be useful, | |
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 * GNU General Lesser Public License for more details. | |
21 * | |
22 * You should have received a copy of the GNU General Lesser Public | |
23 * License along with this program. If not, see | |
24 * <http://www.gnu.org/licenses/lgpl-3.0.html>. | |
25 * #L% | |
26 */ | |
27 | |
3 | 28 import java.io.UnsupportedEncodingException; |
29 import java.security.InvalidKeyException; | |
30 import java.security.SignatureException; | |
5 | 31 import java.text.SimpleDateFormat; |
3 | 32 import java.util.ArrayList; |
5 | 33 import java.util.Calendar; |
16 | 34 import java.util.HashSet; |
3 | 35 import java.util.List; |
16 | 36 import java.util.Set; |
3 | 37 import java.util.regex.Matcher; |
38 import java.util.regex.Pattern; | |
39 | |
40 import net.oauth.jsontoken.Checker; | |
41 import net.oauth.jsontoken.JsonToken; | |
42 import net.oauth.jsontoken.JsonTokenParser; | |
43 import net.oauth.jsontoken.SystemClock; | |
44 import net.oauth.jsontoken.crypto.HmacSHA256Verifier; | |
45 import net.oauth.jsontoken.crypto.Verifier; | |
46 | |
47 import org.apache.commons.codec.binary.Base64; | |
48 import org.apache.log4j.Logger; | |
49 import org.json.JSONArray; | |
50 import org.json.JSONException; | |
51 import org.json.JSONObject; | |
52 import org.restlet.data.Form; | |
53 import org.restlet.data.Status; | |
54 import org.restlet.representation.Representation; | |
55 import org.restlet.resource.Options; | |
56 import org.restlet.resource.ServerResource; | |
57 | |
9 | 58 import de.mpiwg.itgroup.annotations.Actor; |
4 | 59 import de.mpiwg.itgroup.annotations.Annotation; |
60 import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes; | |
10 | 61 import de.mpiwg.itgroup.annotations.Group; |
62 import de.mpiwg.itgroup.annotations.Person; | |
48
0e00bf8e27fb
targets and resources of Annotation object are objects now.
casties
parents:
40
diff
changeset
|
63 import de.mpiwg.itgroup.annotations.Resource; |
0e00bf8e27fb
targets and resources of Annotation object are objects now.
casties
parents:
40
diff
changeset
|
64 import de.mpiwg.itgroup.annotations.Target; |
4 | 65 import de.mpiwg.itgroup.annotations.neo4j.AnnotationStore; |
3 | 66 |
67 /** | |
68 * Base class for Annotator resource classes. | |
69 * | |
70 * @author dwinter, casties | |
71 * | |
72 */ | |
73 public abstract class AnnotatorResourceImpl extends ServerResource { | |
74 | |
4 | 75 protected static Logger logger = Logger.getLogger(AnnotatorResourceImpl.class); |
76 | |
77 private AnnotationStore store; | |
3 | 78 |
79 protected String getAllowedMethodsForHeader() { | |
80 return "OPTIONS,GET,POST"; | |
81 } | |
82 | |
4 | 83 protected AnnotationStore getAnnotationStore() { |
84 if (store == null) { | |
19 | 85 store = ((BaseRestlet) getApplication()).getAnnotationStore(); |
4 | 86 } |
87 return store; | |
88 } | |
89 | |
3 | 90 public String encodeJsonId(String id) { |
64 | 91 if (id == null) |
92 return null; | |
3 | 93 try { |
94 return Base64.encodeBase64URLSafeString(id.getBytes("UTF-8")); | |
95 } catch (UnsupportedEncodingException e) { | |
96 return null; | |
97 } | |
98 } | |
99 | |
100 public String decodeJsonId(String id) { | |
64 | 101 if (id == null) |
102 return null; | |
3 | 103 try { |
104 return new String(Base64.decodeBase64(id), "UTF-8"); | |
105 } catch (UnsupportedEncodingException e) { | |
106 return null; | |
107 } | |
108 } | |
109 | |
110 /** | |
111 * Handle options request to allow CORS for AJAX. | |
112 * | |
113 * @param entity | |
114 */ | |
115 @Options | |
116 public void doOptions(Representation entity) { | |
117 logger.debug("AnnotatorResourceImpl doOptions!"); | |
118 setCorsHeaders(); | |
119 } | |
120 | |
121 /** | |
122 * set headers to allow CORS for AJAX. | |
123 */ | |
124 protected void setCorsHeaders() { | |
125 Form responseHeaders = (Form) getResponse().getAttributes().get("org.restlet.http.headers"); | |
126 if (responseHeaders == null) { | |
127 responseHeaders = new Form(); | |
128 getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders); | |
129 } | |
130 responseHeaders.add("Access-Control-Allow-Methods", getAllowedMethodsForHeader()); | |
131 // echo back Origin and Request-Headers | |
132 Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers"); | |
133 String origin = requestHeaders.getFirstValue("Origin", true); | |
134 if (origin == null) { | |
135 responseHeaders.add("Access-Control-Allow-Origin", "*"); | |
136 } else { | |
137 responseHeaders.add("Access-Control-Allow-Origin", origin); | |
138 } | |
139 String allowHeaders = requestHeaders.getFirstValue("Access-Control-Request-Headers", true); | |
140 if (allowHeaders != null) { | |
141 responseHeaders.add("Access-Control-Allow-Headers", allowHeaders); | |
142 } | |
143 responseHeaders.add("Access-Control-Allow-Credentials", "true"); | |
144 responseHeaders.add("Access-Control-Max-Age", "60"); | |
145 } | |
146 | |
147 /** | |
148 * returns if authentication information from headers is valid. | |
149 * | |
150 * @param entity | |
151 * @return | |
152 */ | |
153 public boolean isAuthenticated(Representation entity) { | |
154 return (checkAuthToken(entity) != null); | |
155 } | |
156 | |
157 /** | |
57
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
158 * Checks Annotator Auth plugin authentication information from headers. |
61 | 159 * Returns userId if successful. Returns "anonymous" in non-authorization |
160 * mode. | |
3 | 161 * |
162 * @param entity | |
163 * @return | |
164 */ | |
165 public String checkAuthToken(Representation entity) { | |
166 Form requestHeaders = (Form) getRequest().getAttributes().get("org.restlet.http.headers"); | |
167 String authToken = requestHeaders.getFirstValue("x-annotator-auth-token", true); | |
57
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
168 if (authToken == null) { |
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
169 if (!((BaseRestlet) getApplication()).isAuthorizationMode()) { |
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
170 return "anonymous"; |
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
171 } |
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
172 return null; |
4efb21cf0ce0
new non-authorized mode without tokens. enabled by default. configured with annotationmanager.authorization=false property.
casties
parents:
52
diff
changeset
|
173 } |
3 | 174 // decode token first to get consumer key |
175 JsonToken token = new JsonTokenParser(null, null).deserialize(authToken); | |
176 String userId = token.getParamAsPrimitive("userId").getAsString(); | |
177 String consumerKey = token.getParamAsPrimitive("consumerKey").getAsString(); | |
178 // get stored consumer secret for key | |
18 | 179 BaseRestlet restServer = (BaseRestlet) getApplication(); |
3 | 180 String consumerSecret = restServer.getConsumerSecret(consumerKey); |
181 logger.debug("requested consumer key=" + consumerKey + " secret=" + consumerSecret); | |
182 if (consumerSecret == null) { | |
183 return null; | |
184 } | |
185 // logger.debug(String.format("token=%s tokenString=%s signatureAlgorithm=%s",token,token.getTokenString(),token.getSignatureAlgorithm())); | |
186 try { | |
187 List<Verifier> verifiers = new ArrayList<Verifier>(); | |
188 // we only do HS256 yet | |
189 verifiers.add(new HmacSHA256Verifier(consumerSecret.getBytes("UTF-8"))); | |
190 // verify token signature(should really be static...) | |
191 new JsonTokenParser(new SystemClock(), null, (Checker[]) null).verify(token, verifiers); | |
192 } catch (SignatureException e) { | |
193 // TODO Auto-generated catch block | |
194 e.printStackTrace(); | |
195 } catch (InvalidKeyException e) { | |
196 // TODO Auto-generated catch block | |
197 e.printStackTrace(); | |
198 } catch (UnsupportedEncodingException e) { | |
199 // TODO Auto-generated catch block | |
200 e.printStackTrace(); | |
201 } | |
202 // must be ok then | |
203 logger.debug("auth OK! user=" + userId); | |
204 return userId; | |
205 } | |
206 | |
207 /** | |
208 * creates Annotator-JSON from an Annotation object. | |
209 * | |
210 * @param annot | |
61 | 211 * @param forAnonymous |
212 * TODO | |
3 | 213 * @return |
214 */ | |
14
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
215 public JSONObject createAnnotatorJson(Annotation annot, boolean forAnonymous) { |
5 | 216 // return user as a JSON object (otherwise just as string) |
3 | 217 boolean makeUserObject = true; |
218 JSONObject jo = new JSONObject(); | |
219 try { | |
4 | 220 jo.put("text", annot.getBodyText()); |
221 jo.put("uri", annot.getTargetBaseUri()); | |
40
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
222 if (annot.getResourceUri() != null) { |
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
223 jo.put("resource", annot.getResourceUri()); |
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
224 } |
3 | 225 |
16 | 226 /* |
227 * user | |
228 */ | |
64 | 229 Actor creator = annot.getCreator(); |
230 if (creator != null) { | |
231 if (makeUserObject) { | |
232 // create user object | |
233 JSONObject userObject = new JSONObject(); | |
234 // save creator as uri | |
235 userObject.put("uri", creator.getUri()); | |
236 // make short user id | |
237 String userId = creator.getIdString(); | |
238 // set as id | |
239 userObject.put("id", userId); | |
240 // get full name | |
241 String userName = creator.getName(); | |
242 if (userName == null) { | |
243 BaseRestlet restServer = (BaseRestlet) getApplication(); | |
244 userName = restServer.getFullNameFromLdap(userId); | |
245 } | |
246 userObject.put("name", userName); | |
247 // save user object | |
248 jo.put("user", userObject); | |
249 } else { | |
250 // save user as string | |
251 jo.put("user", annot.getCreatorUri()); | |
5 | 252 } |
3 | 253 } |
254 | |
16 | 255 /* |
256 * ranges | |
257 */ | |
4 | 258 if (annot.getTargetFragment() != null) { |
3 | 259 // we only look at the first xpointer |
4 | 260 List<String> fragments = new ArrayList<String>(); |
261 fragments.add(annot.getTargetFragment()); | |
262 FragmentTypes xt = annot.getFragmentType(); | |
263 if (xt == FragmentTypes.XPOINTER) { | |
264 jo.put("ranges", transformToRanges(fragments)); | |
265 } else if (xt == FragmentTypes.AREA) { | |
61 | 266 jo.put("shapes", transformToShapes(fragments)); |
3 | 267 } |
268 } | |
61 | 269 |
16 | 270 /* |
271 * permissions | |
272 */ | |
10 | 273 JSONObject perms = new JSONObject(); |
274 jo.put("permissions", perms); | |
275 // admin | |
276 JSONArray adminPerms = new JSONArray(); | |
277 perms.put("admin", adminPerms); | |
278 Actor adminPerm = annot.getAdminPermission(); | |
279 if (adminPerm != null) { | |
280 adminPerms.put(adminPerm.getIdString()); | |
14
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
281 } else if (forAnonymous) { |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
282 // set something because its not allowed for anonymous |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
283 adminPerms.put("not-you"); |
10 | 284 } |
285 // delete | |
286 JSONArray deletePerms = new JSONArray(); | |
287 perms.put("delete", deletePerms); | |
288 Actor deletePerm = annot.getDeletePermission(); | |
289 if (deletePerm != null) { | |
290 deletePerms.put(deletePerm.getIdString()); | |
14
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
291 } else if (forAnonymous) { |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
292 // set something because its not allowed for anonymous |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
293 deletePerms.put("not-you"); |
10 | 294 } |
295 // update | |
296 JSONArray updatePerms = new JSONArray(); | |
297 perms.put("update", updatePerms); | |
298 Actor updatePerm = annot.getUpdatePermission(); | |
299 if (updatePerm != null) { | |
300 updatePerms.put(updatePerm.getIdString()); | |
14
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
301 } else if (forAnonymous) { |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
302 // set something because its not allowed for anonymous |
629e15b345aa
permissions mostly work. need more server-side checking.
casties
parents:
10
diff
changeset
|
303 updatePerms.put("not-you"); |
10 | 304 } |
305 // read | |
306 JSONArray readPerms = new JSONArray(); | |
307 perms.put("read", readPerms); | |
308 Actor readPerm = annot.getReadPermission(); | |
309 if (readPerm != null) { | |
310 readPerms.put(readPerm.getIdString()); | |
311 } | |
61 | 312 |
16 | 313 /* |
314 * tags | |
315 */ | |
61 | 316 Set<String> tagset = annot.getTags(); |
16 | 317 if (tagset != null) { |
318 JSONArray tags = new JSONArray(); | |
319 jo.put("tags", tags); | |
320 for (String tag : tagset) { | |
321 tags.put(tag); | |
322 } | |
323 } | |
61 | 324 |
16 | 325 /* |
326 * id | |
327 */ | |
3 | 328 // encode Annotation URL (=id) in base64 |
4 | 329 String annotUrl = annot.getUri(); |
3 | 330 String annotId = encodeJsonId(annotUrl); |
331 jo.put("id", annotId); | |
332 return jo; | |
333 } catch (JSONException e) { | |
61 | 334 logger.error("Unable to create AnnotatorJSON!", e); |
3 | 335 } |
336 return null; | |
337 } | |
338 | |
339 private JSONArray transformToRanges(List<String> xpointers) { | |
340 JSONArray ja = new JSONArray(); | |
341 Pattern rg = Pattern | |
4 | 342 .compile("xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)/range-to\\(end-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)\\)"); |
343 Pattern rg1 = Pattern.compile("xpointer\\(start-point\\(string-range\\(\"([^\"]*)\",([^,]*),1\\)\\)\\)"); | |
3 | 344 try { |
345 for (String xpointer : xpointers) { | |
10 | 346 // String decoded = URLDecoder.decode(xpointer, "utf-8"); |
5 | 347 String decoded = xpointer; |
3 | 348 Matcher m = rg.matcher(decoded); |
349 if (m.find()) { | |
61 | 350 JSONObject jo = new JSONObject(); |
351 jo.put("start", m.group(1)); | |
352 jo.put("startOffset", m.group(2)); | |
353 jo.put("end", m.group(3)); | |
354 jo.put("endOffset", m.group(4)); | |
355 ja.put(jo); | |
3 | 356 } |
357 m = rg1.matcher(xpointer); | |
358 if (m.find()) { | |
359 JSONObject jo = new JSONObject(); | |
360 jo.put("start", m.group(1)); | |
361 jo.put("startOffset", m.group(2)); | |
362 ja.put(jo); | |
363 } | |
364 } | |
365 } catch (JSONException e) { | |
61 | 366 logger.error("Unable to transform to ranges!", e); |
3 | 367 } |
368 return ja; | |
369 } | |
370 | |
61 | 371 private JSONArray transformToShapes(List<String> xpointers) { |
3 | 372 JSONArray ja = new JSONArray(); |
61 | 373 Pattern rg = Pattern.compile("xywh=(\\w*):([\\d\\.]+),([\\d\\.]+),([\\d\\.]+),([\\d\\.]+)"); |
3 | 374 try { |
375 for (String xpointer : xpointers) { | |
5 | 376 String decoded = xpointer; |
3 | 377 Matcher m = rg.matcher(decoded); |
378 if (m.find()) { | |
61 | 379 String units = m.group(1); |
380 float x = getFloat(m.group(2)); | |
381 float y = getFloat(m.group(3)); | |
382 float width = getFloat(m.group(4)); | |
383 float height = getFloat(m.group(5)); | |
384 JSONObject shape = new JSONObject(); | |
385 JSONObject geom = new JSONObject(); | |
386 geom.put("units", units); | |
387 geom.put("x", x); | |
388 geom.put("y", y); | |
389 if (width == 0 || height == 0) { | |
390 shape.put("type", "point"); | |
391 shape.put("geometry", geom); | |
392 } else { | |
393 shape.put("type", "rectangle"); | |
394 geom.put("width", width); | |
395 geom.put("height", height); | |
396 shape.put("geometry", geom); | |
3 | 397 } |
61 | 398 ja.put(shape); |
3 | 399 } |
400 } | |
401 } catch (JSONException e) { | |
61 | 402 logger.error("Unable to transform to shapes!", e); |
3 | 403 } |
404 return ja; | |
405 } | |
406 | |
61 | 407 protected String parseShape(JSONObject shape) throws JSONException { |
408 String fragment = null; | |
409 String type = shape.getString("type"); | |
410 JSONObject geom = shape.getJSONObject("geometry"); | |
411 if (type.equalsIgnoreCase("point")) { | |
412 String x = geom.getString("x"); | |
413 String y = geom.getString("y"); | |
414 fragment = String.format("xywh=fraction:%s,%s,0,0", x, y); | |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
415 } else if (type.equalsIgnoreCase("rectangle")) { |
61 | 416 String x = geom.getString("x"); |
417 String y = geom.getString("y"); | |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
418 String width = geom.getString("width"); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
419 String height = geom.getString("height"); |
61 | 420 fragment = String.format("xywh=fraction:%s,%s,%s,%s", x, y, width, height); |
421 } else { | |
422 logger.error("Unable to parse this shape: " + shape); | |
423 } | |
424 return fragment; | |
425 } | |
426 | |
5 | 427 protected String parseArea(JSONObject area) throws JSONException { |
4 | 428 String x = area.getString("x"); |
429 String y = area.getString("y"); | |
430 String width = "0"; | |
431 String height = "0"; | |
432 if (area.has("width")) { | |
433 width = area.getString("width"); | |
434 height = area.getString("height"); | |
435 } | |
5 | 436 String fragment = String.format("xywh=fraction:%s,%s,%s,%s", x, y, width, height); |
4 | 437 return fragment; |
438 } | |
439 | |
5 | 440 protected String parseRange(JSONObject range) throws JSONException { |
4 | 441 String start = range.getString("start"); |
442 String end = range.getString("end"); | |
443 String startOffset = range.getString("startOffset"); | |
444 String endOffset = range.getString("endOffset"); | |
5 | 445 String fragment = String.format( |
4 | 446 "xpointer(start-point(string-range(\"%s\",%s,1))/range-to(end-point(string-range(\"%s\",%s,1))))", start, |
5 | 447 startOffset, end, endOffset); |
4 | 448 return fragment; |
449 } | |
450 | |
3 | 451 /** |
5 | 452 * Creates an Annotation object with data from JSON. |
3 | 453 * |
4 | 454 * uses the specification from the annotator project: {@link https |
455 * ://github.com/okfn/annotator/wiki/Annotation-format} | |
3 | 456 * |
4 | 457 * The username will be transformed to an URI if not given already as URI, |
458 * if not it will set to the MPIWG namespace defined in | |
3 | 459 * de.mpiwg.itgroup.annotationManager.Constants.NS |
460 * | |
461 * @param jo | |
462 * @return | |
463 * @throws JSONException | |
4 | 464 * @throws UnsupportedEncodingException |
3 | 465 */ |
4 | 466 public Annotation createAnnotation(JSONObject jo, Representation entity) throws JSONException, UnsupportedEncodingException { |
3 | 467 return updateAnnotation(new Annotation(), jo, entity); |
468 } | |
469 | |
5 | 470 /** |
471 * Updates an Annotation object with data from JSON. | |
472 * | |
473 * uses the specification from the annotator project: {@link https | |
474 * ://github.com/okfn/annotator/wiki/Annotation-format} | |
475 * | |
476 * The username will be transformed to an URI if not given already as URI, | |
477 * if not it will set to the MPIWG namespace defined in | |
478 * de.mpiwg.itgroup.annotationManager.Constants.NS | |
479 * | |
480 * @param annot | |
481 * @param jo | |
482 * @return | |
483 * @throws JSONException | |
484 * @throws UnsupportedEncodingException | |
485 */ | |
4 | 486 public Annotation updateAnnotation(Annotation annot, JSONObject jo, Representation entity) throws JSONException, |
487 UnsupportedEncodingException { | |
16 | 488 /* |
489 * target uri | |
490 */ | |
3 | 491 if (jo.has("uri")) { |
48
0e00bf8e27fb
targets and resources of Annotation object are objects now.
casties
parents:
40
diff
changeset
|
492 annot.setTarget(new Target(jo.getString("uri"))); |
3 | 493 } |
16 | 494 /* |
40
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
495 * resource uri |
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
496 */ |
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
497 if (jo.has("resource")) { |
48
0e00bf8e27fb
targets and resources of Annotation object are objects now.
casties
parents:
40
diff
changeset
|
498 annot.setResource(new Resource(jo.getString("resource"))); |
40
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
499 } |
03e0f7574224
saving and loading resource targets should work now (no searching yet)
casties
parents:
22
diff
changeset
|
500 /* |
16 | 501 * annotation text |
502 */ | |
3 | 503 if (jo.has("text")) { |
4 | 504 annot.setBodyText(jo.getString("text")); |
3 | 505 } |
16 | 506 /* |
507 * check authentication | |
508 */ | |
3 | 509 String authUser = checkAuthToken(entity); |
510 if (authUser == null) { | |
4 | 511 /* |
512 * // try http auth User httpUser = getHttpAuthUser(entity); if | |
513 * (httpUser == null) { | |
514 */ | |
515 setStatus(Status.CLIENT_ERROR_FORBIDDEN); | |
516 return null; | |
517 /* | |
518 * } authUser = httpUser.getIdentifier(); | |
519 */ | |
3 | 520 } |
16 | 521 /* |
522 * get or create creator object | |
523 */ | |
9 | 524 Actor creator = annot.getCreator(); |
525 if (creator == null) { | |
10 | 526 creator = new Person(); |
9 | 527 annot.setCreator(creator); |
528 } | |
3 | 529 // username not required, if no username given authuser will be used |
530 String username = null; | |
10 | 531 String userUri = creator.getUri(); |
3 | 532 if (jo.has("user")) { |
533 if (jo.get("user") instanceof String) { | |
534 // user is just a String | |
535 username = jo.getString("user"); | |
10 | 536 creator.setId(username); |
3 | 537 // TODO: what if username and authUser are different? |
538 } else { | |
539 // user is an object | |
540 JSONObject user = jo.getJSONObject("user"); | |
541 if (user.has("id")) { | |
10 | 542 String id = user.getString("id"); |
543 creator.setId(id); | |
544 username = id; | |
3 | 545 } |
546 if (user.has("uri")) { | |
547 userUri = user.getString("uri"); | |
548 } | |
549 } | |
550 } | |
551 if (username == null) { | |
552 username = authUser; | |
553 } | |
5 | 554 // try to get full name |
9 | 555 if (creator.getName() == null && username != null) { |
18 | 556 BaseRestlet restServer = (BaseRestlet) getApplication(); |
5 | 557 String fullName = restServer.getFullNameFromLdap(username); |
9 | 558 creator.setName(fullName); |
5 | 559 } |
560 // userUri should be a URI, if not it will set to the MPIWG namespace | |
3 | 561 if (userUri == null) { |
562 if (username.startsWith("http")) { | |
563 userUri = username; | |
564 } else { | |
58 | 565 userUri = BaseRestlet.PERSONS_URI_PREFIX + username; |
3 | 566 } |
567 } | |
568 // TODO: should we overwrite the creator? | |
9 | 569 if (creator.getUri() == null) { |
570 creator.setUri(userUri); | |
5 | 571 } |
16 | 572 /* |
573 * creation date | |
574 */ | |
5 | 575 if (annot.getCreated() == null) { |
576 // set creation date | |
577 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); | |
578 String ct = format.format(Calendar.getInstance().getTime()); | |
579 annot.setCreated(ct); | |
580 } | |
3 | 581 |
16 | 582 /* |
61 | 583 * create fragment from the first range/area |
16 | 584 */ |
52 | 585 try { |
586 if (jo.has("ranges")) { | |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
587 JSONArray ranges = jo.getJSONArray("ranges"); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
588 if (ranges.length() > 0) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
589 JSONObject range = ranges.getJSONObject(0); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
590 annot.setFragmentType(FragmentTypes.XPOINTER); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
591 String fragment = parseRange(range); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
592 annot.setTargetFragment(fragment); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
593 } |
52 | 594 } |
595 } catch (JSONException e) { | |
596 // nothing to do | |
3 | 597 } |
52 | 598 try { |
61 | 599 if (jo.has("shapes")) { |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
600 JSONArray shapes = jo.getJSONArray("shapes"); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
601 if (shapes.length() > 0) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
602 JSONObject shape = shapes.getJSONObject(0); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
603 annot.setFragmentType(FragmentTypes.AREA); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
604 String fragment = parseShape(shape); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
605 annot.setTargetFragment(fragment); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
606 } |
61 | 607 } |
608 } catch (JSONException e) { | |
609 // nothing to do | |
610 } | |
611 // deprecated areas type | |
612 try { | |
52 | 613 if (jo.has("areas")) { |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
614 JSONArray areas = jo.getJSONArray("areas"); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
615 if (areas.length() > 0) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
616 JSONObject area = areas.getJSONObject(0); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
617 annot.setFragmentType(FragmentTypes.AREA); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
618 String fragment = parseArea(area); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
619 annot.setTargetFragment(fragment); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
620 } |
52 | 621 } |
622 } catch (JSONException e) { | |
623 // nothing to do | |
3 | 624 } |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
625 // no fragment is an error |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
626 if (annot.getFragmentType() == null || annot.getTargetFragment() == null) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
627 throw new JSONException("Annotation has no valid target fragment!"); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
628 } |
64 | 629 |
16 | 630 /* |
631 * permissions | |
632 */ | |
10 | 633 if (jo.has("permissions")) { |
634 JSONObject permissions = jo.getJSONObject("permissions"); | |
635 if (permissions.has("admin")) { | |
636 JSONArray perms = permissions.getJSONArray("admin"); | |
637 Actor actor = getActorFromPermissions(perms); | |
638 annot.setAdminPermission(actor); | |
639 } | |
640 if (permissions.has("delete")) { | |
641 JSONArray perms = permissions.getJSONArray("delete"); | |
642 Actor actor = getActorFromPermissions(perms); | |
643 annot.setDeletePermission(actor); | |
644 } | |
645 if (permissions.has("update")) { | |
646 JSONArray perms = permissions.getJSONArray("update"); | |
647 Actor actor = getActorFromPermissions(perms); | |
648 annot.setUpdatePermission(actor); | |
649 } | |
650 if (permissions.has("read")) { | |
651 JSONArray perms = permissions.getJSONArray("read"); | |
652 Actor actor = getActorFromPermissions(perms); | |
653 annot.setReadPermission(actor); | |
654 } | |
655 } | |
656 | |
16 | 657 /* |
658 * tags | |
659 */ | |
660 if (jo.has("tags")) { | |
661 HashSet<String> tagset = new HashSet<String>(); | |
662 JSONArray tags = jo.getJSONArray("tags"); | |
663 for (int i = 0; i < tags.length(); ++i) { | |
664 tagset.add(tags.getString(i)); | |
665 } | |
666 annot.setTags(tagset); | |
667 } | |
668 | |
4 | 669 return annot; |
3 | 670 } |
671 | |
61 | 672 @SuppressWarnings("unused") |
673 // i in for loop | |
10 | 674 protected Actor getActorFromPermissions(JSONArray perms) throws JSONException { |
675 Actor actor = null; | |
676 for (int i = 0; i < perms.length(); ++i) { | |
677 String perm = perms.getString(i); | |
678 if (perm.toLowerCase().startsWith("group:")) { | |
679 String groupId = perm.substring(6); | |
680 actor = new Group(groupId); | |
681 } else { | |
682 actor = new Person(perm); | |
683 } | |
684 // we just take the first one | |
685 break; | |
686 } | |
687 return actor; | |
688 } | |
689 | |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
690 public static float getFloat(String s) { |
61 | 691 try { |
692 return Float.parseFloat(s); | |
693 } catch (NumberFormatException e) { | |
694 } | |
695 return 0f; | |
696 } | |
63
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
697 |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
698 public static int getInt(String s) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
699 try { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
700 return Integer.parseInt(s); |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
701 } catch (NumberFormatException e) { |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
702 } |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
703 return 0; |
9f8c9611848a
fixed bug with new rectangle shapes. added limit, offset and sortBy parameters to annotator/ and annotator/search.
casties
parents:
61
diff
changeset
|
704 } |
3 | 705 } |