source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java

Last change on this file was 38431, checked in by anupama, 5 months ago
  1. For editing user comments with the document editor, I want to set metamode to override just once instead of for each docid (that is, for each docrecord). This didn't work before because the perl code didn't allow it as optional argument, although the actual set-metadata-array() perl subroutines were coded for it. 2. The Java code needs to allow an array of document records, without requiring that each docrecord contains a metatable field (a sub array of meta records). This is because the perl code set-metadata-array() subroutines allowed for this, but when set-metadata functionality started needing authentication/user editing powers (authentication goes through java, which then relays the command to perl), this pre-existing, optional way of setting metadata in the perl code wasn't opened up through java.
  • Property svn:keywords set to Author Date Id Revision
File size: 49.1 KB
Line 
1/*
2 * GS2Construct.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.service;
20
21import java.io.BufferedWriter;
22import java.io.File;
23import java.io.FileWriter;
24import java.io.Serializable;
25import java.io.UnsupportedEncodingException;
26import java.net.URLEncoder;
27import java.util.Collections;
28import java.util.Iterator;
29import java.util.Map.Entry;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Set;
33import java.util.regex.Matcher;
34import java.util.regex.Pattern;
35
36import org.apache.log4j.Logger;
37import org.greenstone.gsdl3.build.GS2PerlConstructor;
38import org.greenstone.gsdl3.build.GS2PerlListener;
39import org.greenstone.gsdl3.util.Dictionary;
40import org.greenstone.gsdl3.util.GSFile;
41import org.greenstone.gsdl3.util.GSParams;
42import org.greenstone.gsdl3.util.GSPath;
43import org.greenstone.gsdl3.util.GSStatus;
44import org.greenstone.gsdl3.util.GSXML;
45import org.greenstone.gsdl3.util.OID;
46import org.greenstone.gsdl3.util.SimpleCollectionDatabase;
47import org.greenstone.gsdl3.util.UserContext;
48import org.greenstone.gsdl3.util.XMLConverter;
49
50// https://developer.android.com/reference/org/json/JSONObject.html
51// https://developer.android.com/reference/org/json/JSONArray.html
52import org.json.JSONArray;
53import org.json.JSONException;
54import org.json.JSONObject;
55
56
57import org.w3c.dom.Document;
58import org.w3c.dom.Element;
59import org.w3c.dom.Node;
60import org.w3c.dom.Text;
61
62/**
63 * A Services class for building collections provides a wrapper around the old
64 * perl scripts
65 *
66 */
67public class GS2Construct extends ServiceRack
68{
69
70 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.GS2Construct.class.getName());
71
72 // default error message
73 private static final String NO_PERMISSIONS_ERROR = "auth.error.no_permissions";
74 private static final String NO_METADATA_PERMISSIONS_ERROR = "auth.error.no_metadata_permissions";
75
76 // services offered
77 private static final String NEW_SERVICE = "NewCollection";
78 private static final String ADD_DOC_SERVICE = "AddDocument";
79 private static final String IMPORT_SERVICE = "ImportCollection";
80 private static final String BUILD_SERVICE = "BuildCollection";
81 private static final String ACTIVATE_SERVICE = "ActivateCollection";
82 private static final String BUILD_AND_ACTIVATE_SERVICE = "BuildAndActivateCollection";
83 private static final String DELETE_SERVICE = "DeleteCollection";
84 private static final String RELOAD_SERVICE = "ReloadCollection";
85 private static final String MODIFY_METADATA_SERVICE = "ModifyMetadata"; // set or remove metadata
86
87
88 // params used
89 private static final String COL_PARAM = "collection";
90 private static final String NEW_COL_TITLE_PARAM = "collTitle";
91 private static final String NEW_COL_ABOUT_PARAM = "collAbout";
92 private static final String CREATOR_PARAM = "creator";
93 private static final String NEW_FILE_PARAM = "newfile";
94 private static final String PROCESS_ID_PARAM = GSParams.PROCESS_ID;
95 private static final String BUILDTYPE_PARAM = "buildType";
96 private static final String BUILDTYPE_MG = "mg";
97 private static final String BUILDTYPE_MGPP = "mgpp";
98
99 protected static String DATABASE_TYPE = null;
100 protected SimpleCollectionDatabase coll_db = null;
101
102 // the list of the collections - store between some method calls
103 private String[] collection_list = null;
104
105 // set of listeners for any construction commands
106 protected Map<String, GS2PerlListener> listeners = null;
107 protected HashMap<String, Boolean> collectionOperationMap = new HashMap<String, Boolean>();
108
109 public GS2Construct()
110 {
111 this.listeners = Collections.synchronizedMap(new HashMap<String, GS2PerlListener>());
112 }
113
114
115 /** returns a specific service description */
116 protected Element getServiceDescription(Document doc, String service, String lang, String subset)
117 {
118
119 Element description = doc.createElement(GSXML.SERVICE_ELEM);
120 description.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
121 description.setAttribute(GSXML.NAME_ATT, service);
122 if (subset == null || subset.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
123 {
124 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_NAME, getTextString(service + ".name", lang)));
125 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_DESCRIPTION, getTextString(service + ".description", lang)));
126 description.appendChild(GSXML.createDisplayTextElement(doc, GSXML.DISPLAY_TEXT_SUBMIT, getTextString(service + ".submit", lang)));
127 }
128 if (subset == null || subset.equals(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER))
129 {
130 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
131 description.appendChild(param_list);
132
133 if (service.equals(NEW_SERVICE))
134 {
135
136 Element param = GSXML.createParameterDescription(doc, NEW_COL_TITLE_PARAM, getTextString("param." + NEW_COL_TITLE_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
137 param_list.appendChild(param);
138 param = GSXML.createParameterDescription(doc, CREATOR_PARAM, getTextString("param." + CREATOR_PARAM, lang), GSXML.PARAM_TYPE_STRING, null, null, null);
139 param_list.appendChild(param);
140 param = GSXML.createParameterDescription(doc, NEW_COL_ABOUT_PARAM, getTextString("param." + NEW_COL_ABOUT_PARAM, lang), GSXML.PARAM_TYPE_TEXT, null, null, null);
141 param_list.appendChild(param);
142 String[] types = { BUILDTYPE_MGPP, BUILDTYPE_MG };
143 String[] type_texts = { getTextString("param." + BUILDTYPE_PARAM + "." + BUILDTYPE_MGPP, lang), getTextString("param." + BUILDTYPE_PARAM + "." + BUILDTYPE_MG, lang) };
144
145 param = GSXML.createParameterDescription(doc, BUILDTYPE_PARAM, getTextString("param." + BUILDTYPE_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, BUILDTYPE_MGPP, types, type_texts);
146 param_list.appendChild(param);
147 }
148 else if (service.equals(ACTIVATE_SERVICE) || service.equals(IMPORT_SERVICE) || service.equals(BUILD_SERVICE) || service.equals(RELOAD_SERVICE) || service.equals(DELETE_SERVICE) || service.equals(MODIFY_METADATA_SERVICE))
149 {
150
151 Element param = GSXML.createParameterDescription(doc, COL_PARAM, getTextString("param." + COL_PARAM, lang), GSXML.PARAM_TYPE_ENUM_SINGLE, null, this.collection_list, this.collection_list);
152 param_list.appendChild(param);
153 }
154 else
155 {
156 // invalid service name
157 return null;
158 }
159 }
160 return description;
161 }
162
163 // each service must have a method "process<New service name>"
164
165 protected Element processNewCollection(Element request)
166 {
167
168 if (!userHasCollectionEditPermissions(request)) {
169 String lang = request.getAttribute(GSXML.LANG_ATT);
170 return errorResponse("processNewCollection", NO_PERMISSIONS_ERROR, lang);
171 }
172 return runCommand(request, GS2PerlConstructor.NEW);
173 }
174
175 /** TODO:implement this */
176 protected Element processAddDocument(Element request)
177 {
178 if (!userHasCollectionEditPermissions(request)) {
179 String lang = request.getAttribute(GSXML.LANG_ATT);
180 return errorResponse("processAddDocument", NO_PERMISSIONS_ERROR, lang);
181 }
182
183 Document result_doc = XMLConverter.newDOM();
184 // decode the file name, add it to the import directory
185 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
186 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
187 response.setAttribute(GSXML.FROM_ATT, name);
188 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
189 response.appendChild(status);
190 //String lang = request.getAttribute(GSXML.LANG_ATT);
191 //String request_type = request.getAttribute(GSXML.TYPE_ATT);
192 Text t = result_doc.createTextNode("AddDocument: not implemented yet");
193 status.appendChild(t);
194 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
195 return response;
196 }
197
198 protected Element processBuildAndActivateCollection(Element request)
199 {
200 // check permissions
201 if (!userHasCollectionEditPermissions(request)) {
202 String lang = request.getAttribute(GSXML.LANG_ATT);
203 return errorResponse("processBuildAndActivateCollection", NO_PERMISSIONS_ERROR, lang);
204 }
205
206
207 waitUntilReady(request);
208 Element buildResponse = processBuildCollection(request);
209 if (buildResponse.getElementsByTagName(GSXML.ERROR_ELEM).getLength() > 0)
210 {
211 signalReady(request);
212 return buildResponse;
213 }
214
215 Element statusElem = (Element) buildResponse.getElementsByTagName(GSXML.STATUS_ELEM).item(0);
216 String id = statusElem.getAttribute("pid");
217
218 GS2PerlListener currentListener = this.listeners.get(id);
219 int statusCode = currentListener.getStatus();
220 while (!GSStatus.isCompleted(statusCode))
221 {
222 // wait for the process, and keep checking the status code
223 // there is probably a better way to do this.
224 try
225 {
226 Thread.currentThread().sleep(100);
227 }
228 catch (Exception e)
229 { // ignore
230 }
231 statusCode = currentListener.getStatus();
232 }
233
234 Element activateResponse = processActivateCollection(request);
235 signalReady(request);
236 return activateResponse;
237 }
238
239 protected Element processImportCollection(Element request)
240 {
241 if (!userHasCollectionEditPermissions(request)) {
242 String lang = request.getAttribute(GSXML.LANG_ATT);
243 return errorResponse("processImportCollection", NO_PERMISSIONS_ERROR, lang);
244 }
245
246 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
247 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
248
249 if (params == null)
250 {
251 return null;
252 }
253
254 //If we have been requested to only build certain documents then we need to create a manifest file
255 String documentsParam = (String) params.get("documents");
256 if (documentsParam != null && !documentsParam.equals(""))
257 {
258 String s = File.separator;
259 String manifestFolderPath = this.site_home + s + "collect" + s + params.get(COL_PARAM) + s + "manifests";
260 String manifestFilePath = manifestFolderPath + File.separator + "tempManifest.xml";
261
262 File manifestFolderFile = new File(manifestFolderPath);
263 if (!manifestFolderFile.exists())
264 {
265 manifestFolderFile.mkdirs();
266 }
267
268 File manifestFile = new File(manifestFilePath);
269 if (!manifestFile.exists())
270 {
271 try
272 {
273 manifestFile.createNewFile();
274 }
275 catch (Exception ex)
276 {
277 ex.printStackTrace();
278 return null; //Probably should return an actual error
279 }
280 }
281 String[] docList = documentsParam.split(",");
282
283 try
284 {
285 BufferedWriter bw = new BufferedWriter(new FileWriter(manifestFile));
286 bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
287 bw.write("<Manifest>\n");
288 bw.write(" <Index>\n");
289 for (int j = 0; j < docList.length; j++)
290 {
291 bw.write(" <Filename>" + docList[j] + "</Filename>\n");
292 }
293 bw.write(" </Index>\n");
294 bw.write("</Manifest>\n");
295 bw.close();
296 }
297 catch (Exception ex)
298 {
299 ex.printStackTrace();
300 return null; //Probably should return an actual error
301 }
302 }
303
304 return runCommand(request, GS2PerlConstructor.IMPORT);
305 }
306
307 protected Element processBuildCollection(Element request)
308 {
309 if (!userHasCollectionEditPermissions(request)) {
310 String lang = request.getAttribute(GSXML.LANG_ATT);
311 return errorResponse("processBuildCollection", NO_PERMISSIONS_ERROR, lang);
312 }
313
314 return runCommand(request, GS2PerlConstructor.BUILD);
315 }
316
317 protected Element processModifyMetadata(Element request)
318 {
319
320 // There are now 3 types of operations whereby metadata gets modified:
321 // - document including document-meta editing: user needs document editing powers
322 // - adding user comments: user just needs an account and needs to be logged in
323 // - removing user comments: user needs to be in administrator group
324 // We handle all 3 cases in this service.
325
326 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
327 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
328
329 String metaserver_command = (String) params.get("a"); // e.g. set-archives-metadata or set-metadata-array
330 boolean supportsSettingMultipleMeta = metaserver_command.equals("set-metadata-array") ? true : false;
331 String json_str = (String) params.get("json"); // String or null if no param named "json"
332
333 String[] docids = null;
334
335 // For user comments (setting or removing), these are the allowed metadata fields
336 Pattern allowedMetaFieldsPattern = Pattern.compile("^(username|usertimestamp|usercomment)$");
337 String lang = request.getAttribute(GSXML.LANG_ATT);
338
339 if (userHasCollectionEditPermissions(request, params)) { // means user can modify ANY metadata
340 if (metaserver_command.equals("remove-metadata-array")) {
341 // check if only removing user comments metadata fields
342 docids = getDocIdsWithOptFilter(json_str, allowedMetaFieldsPattern);
343 if(docids == null) {
344 // removing multiple metadata that are Not user comments
345 // can be done by any user with collection edit permissions
346 docids = getDocIdsWithOptFilter(json_str, null);
347 }
348 }
349 // if dealing with an array of meta, then parse out the docids from the json
350 else if(supportsSettingMultipleMeta) {
351 docids = getDocIdsWithOptFilter(json_str, null);
352 } // else set-meta operation on single metadata field of single doc,
353 // and docid will be obtained in runCommand() where it's needed
354
355 } else {
356 // check if user logged in
357 // shouldn't be able to do any meta modification if not logged in
358
359 UserContext context = new UserContext(request);
360 if (context.getUsername().equals("")) {
361
362 return errorResponse("processModifyMetadata", NO_METADATA_PERMISSIONS_ERROR, lang);
363 } else { // User is logged in at least, see whether they can do any restricted set-meta ops
364 // that are open to regular users (those without permissions to edit this collection).
365 // For now, there's only one restricted set-meta operation open to any logged in users
366 // who don't otherwise have editing permissions for the collection: adding user comments.
367
368 boolean isAddingUserComments = false;
369
370 if(supportsSettingMultipleMeta) {
371
372 docids = getDocIdsWithOptFilter(json_str, allowedMetaFieldsPattern);
373 if(docids != null) {
374 isAddingUserComments = true;
375 }
376 } else {
377 String metaname = (String) params.get("metaname");
378 if(isAllowedToModifyMeta(metaname, allowedMetaFieldsPattern)) {
379 isAddingUserComments = true;
380 }
381 }
382
383 if(!isAddingUserComments) { // logged in user is attempting to set meta outside restricted set,
384 // In this case, they're attempting to set meta not related to user comments
385 return errorResponse("processModifyMetadata", NO_PERMISSIONS_ERROR, lang);
386 }
387 }
388 }
389
390 // wait until we can reserve the collection for processing
391 waitUntilReady(request);
392
393
394 // process
395 Element response = runCommand(request, GS2PerlConstructor.MODIFY_METADATA_SERVER, docids);
396
397 if (response.getElementsByTagName(GSXML.ERROR_ELEM).getLength() <= 0) // if no errors, wait for process to finish
398 {
399 Element statusElem = (Element) response.getElementsByTagName(GSXML.STATUS_ELEM).item(0);
400 String id = statusElem.getAttribute("pid");
401
402 GS2PerlListener currentListener = this.listeners.get(id);
403 int statusCode = currentListener.getStatus();
404 while (!GSStatus.isCompleted(statusCode))
405 {
406 // wait for the process, and keep checking the status code
407 // there is probably a better way to do this.
408 try
409 {
410 Thread.currentThread().sleep(100);
411 }
412 catch (Exception e)
413 { // ignore
414 }
415 statusCode = currentListener.getStatus();
416 }
417 }
418
419 Element statusElem = (Element) response.getElementsByTagName(GSXML.STATUS_ELEM).item(0);
420 String statusString = GSXML.getNodeText(statusElem);
421 statusString += " and monitored until done.";
422 // check for errors
423 int initial_response_status_code = Integer.parseInt(statusElem.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
424 if (GSStatus.isError(initial_response_status_code)) {
425 logger.info("Got error status code: " + initial_response_status_code);
426 statusString += "But got error status code: " + initial_response_status_code;
427 } else { // check for Construction event errors
428 String id = statusElem.getAttribute("pid");
429 GS2PerlListener currentListener = this.listeners.get(id);
430 int completed_status_code = currentListener.getStatus();
431 // update statusElem so we return the status code on completion
432 statusElem.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(completed_status_code));
433 if (GSStatus.isError(completed_status_code))
434 {
435 logger.info("Got construction event error. Error status code: " + completed_status_code);
436 statusString += "But got construction event error, status code: " + completed_status_code;
437 // add the rest of the messages to the statusElem node
438 statusString += "\n" + currentListener.getUpdate();
439 }
440 }
441 // can finally set statusString
442 GSXML.setNodeText(statusElem, statusString);
443
444 // release hold on collection
445 signalReady(request);
446 return response;
447
448 }
449
450 protected Element processActivateCollection(Element request)
451 {
452
453 String lang = request.getAttribute(GSXML.LANG_ATT);
454 if (!userHasCollectionEditPermissions(request)) {
455 return errorResponse("processActivateCollection", NO_PERMISSIONS_ERROR, lang);
456 }
457
458 // this activates the collection on disk. but now we need to tell
459 // the MR about it. but we have to wait until the process is finished.
460 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
461 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
462 String coll_name = (String) params.get(COL_PARAM);
463
464
465 UserContext userContext = new UserContext(request);
466 String request_type = request.getAttribute(GSXML.TYPE_ATT);
467
468 // now we de-activate the collection before running activate.pl, and then re-activate at end
469 // So activate.pl only does the moving, no activation. This way will prevent java from launching
470 // perl, exiting and then leaving dangling file handles (on index/text/col.gdb) in perl.
471 if (!request_type.equals(GSXML.REQUEST_TYPE_STATUS)) {
472 systemRequest("delete", coll_name, null, userContext); // deactivate collection
473 }
474
475 Element response = runCommand(request, GS2PerlConstructor.ACTIVATE); // if request is for STATUS, then this won't run activate.pl
476
477 Element status = (Element) GSXML.getChildByTagName(response, GSXML.STATUS_ELEM);
478
479 // check for finished
480 int status_code = Integer.parseInt(status.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
481 if (GSStatus.isCompleted(status_code) && GSStatus.isError(status_code))
482 {
483 // we shouldn't carry out the next bit, just return the response
484 return response;
485 }
486 String id = status.getAttribute(GSXML.STATUS_PROCESS_ID_ATT);
487 GS2PerlListener listener = this.listeners.get(id);
488 if (listener == null)
489 {
490 logger.error("somethings gone wrong, couldn't find the listener");
491 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
492 return response;
493 }
494
495 while (!GSStatus.isCompleted(status_code))
496 {
497 // wait for the process, and keep checking the status code
498 // there is probably a better way to do this.
499 try
500 {
501 Thread.currentThread().sleep(100);
502 }
503 catch (Exception e)
504 { // ignore
505 }
506 status_code = listener.getStatus();
507 }
508
509 // add the rest of the messages to the status node
510 Text t = status.getOwnerDocument().createTextNode("\n" + listener.getUpdate());
511 status.appendChild(t);
512 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
513 if (GSStatus.isError(status_code))
514 {
515 return response; // without doing the next bit
516 }
517
518 t = status.getOwnerDocument().createTextNode("\n");
519 status.appendChild(t);
520 // once have got here, we assume
521 // the first bit proceeded successfully, now reload the collection (sends a collection reactivation request)
522 systemRequest("reload", coll_name, status, userContext); // this will append more messages to the status, and overwrite the error code att
523 return response;
524
525 }
526
527 protected Element processDeleteCollection(Element request)
528 {
529
530 if (!userHasCollectionEditPermissions(request)) {
531 String lang = request.getAttribute(GSXML.LANG_ATT);
532 return errorResponse("processDeleteCollection", NO_PERMISSIONS_ERROR, lang);
533 }
534
535 Document result_doc = XMLConverter.newDOM();
536 // the response to send back
537 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
538 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
539 response.setAttribute(GSXML.FROM_ATT, name);
540 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
541 response.appendChild(status);
542 Text t = null; // the text node for the error/success message
543 String lang = request.getAttribute(GSXML.LANG_ATT);
544 String request_type = request.getAttribute(GSXML.TYPE_ATT);
545
546 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
547 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
548
549 boolean get_status_only = false;
550 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
551 {
552 get_status_only = true;
553 }
554 if (get_status_only)
555 {
556 // at the moment, delete is synchronous. but it may take ages so should do the command in another thread maybe? in which case we will want to ask for status
557 logger.error("had a status request for delete - this shouldn't happen!!");
558 //t = result_doc.createTextNode("");
559 //status.appendChild(t);
560 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
561 return response;
562 }
563 String coll_name = (String) params.get(COL_PARAM);
564 String[] args = { coll_name };
565 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, coll_name));
566 // check that the coll is there in the first place
567 if (!coll_dir.exists())
568 {
569 t = result_doc.createTextNode(getTextString("delete.exists_error", lang, args));
570 status.appendChild(t);
571 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
572 return response;
573 }
574
575 // try to delete the directory
576 if (!GSFile.deleteFile(coll_dir))
577 {
578 t = result_doc.createTextNode(getTextString("delete.delete_error", lang, args));
579 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
580 status.appendChild(t);
581 return response;
582 }
583
584 UserContext userContext = new UserContext(request);
585
586 systemRequest("delete", coll_name, status, userContext);
587 return response;
588 }
589
590 protected Element processReloadCollection(Element request)
591 {
592 if (!userHasCollectionEditPermissions(request)) {
593 String lang = request.getAttribute(GSXML.LANG_ATT);
594 return errorResponse("processReloadCollection", NO_PERMISSIONS_ERROR, lang);
595 }
596
597 Document result_doc = XMLConverter.newDOM();
598 // the response to send back
599 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
600 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
601 response.setAttribute(GSXML.FROM_ATT, name);
602 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
603 response.appendChild(status);
604 Text t = null; // the text node for the error/success message
605
606 String lang = request.getAttribute(GSXML.LANG_ATT);
607 String request_type = request.getAttribute(GSXML.TYPE_ATT);
608
609 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
610 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
611
612 boolean get_status_only = false;
613 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
614 {
615 get_status_only = true;
616 }
617 if (get_status_only)
618 {
619 // reload is synchronous - this makes no sense
620 logger.error("had a status request for reload - this shouldn't happen!!");
621 //t = result_doc.createTextNode("");
622 //status.appendChild(t);
623 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
624 return response;
625 }
626
627 String coll_name = (String) params.get(COL_PARAM);
628
629 UserContext userContext = new UserContext(request);
630
631 systemRequest("reload", coll_name, status, userContext);
632 return response;
633
634 }
635
636 /**
637 * send a configure request to the message router action name should be
638 * "delete" or "reload" response will be put into the status element
639 */
640 protected void systemRequest(String operation, String coll_name, Element status, UserContext userContext)
641 {
642 // send the request to the MR
643 Document doc = XMLConverter.newDOM();
644 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
645 Element request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", userContext);
646 message.appendChild(request);
647 Element command = doc.createElement(GSXML.SYSTEM_ELEM);
648 request.appendChild(command);
649 command.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, GSXML.COLLECTION_ELEM);
650 command.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, coll_name);
651
652 if (operation.equals("delete"))
653 {
654 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_DEACTIVATE);
655 }
656 else if (operation.equals("reload"))
657 {
658 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_ACTIVATE);
659 }
660 else
661 {
662 logger.error("invalid action name passed to systemRequest:" + operation);
663 return;
664 }
665 request.appendChild(command);
666 Node response = this.router.process(message); // at the moment, get no info in response so ignore it
667 Text t;
668 String[] args = { coll_name };
669
670 if (status != null)
671 {
672 if (response == null)
673 {
674 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".configure_error", userContext.getLanguage(), args));
675 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
676 status.appendChild(t);
677 return;
678 }
679
680 // if we got here, we have succeeded!
681 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".success", userContext.getLanguage(), args));
682 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.SUCCESS));
683 status.appendChild(t);
684 }
685 }
686
687
688
689 /**
690 * configure the service module for now, all services have type=build - need
691 * to think about this
692 */
693 // in configure we set up any params that should be saved to the session. At this stage, assuming none should be saved.
694 public boolean configure(Element info, Element extra_info)
695 {
696 if (!super.configure(info, extra_info))
697 {
698 return false;
699 }
700
701 logger.info("configuring GS2Construct");
702
703 // read thru collect folder and set up collectionList
704 this.collection_list = getCollectionList();
705
706 Element e = null;
707 // hard code in the services for now
708
709 // set up short_service_info_ - for now just has name and type
710
711 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
712 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
713 e.setAttribute(GSXML.NAME_ATT, NEW_SERVICE);
714 this.short_service_info.appendChild(e);
715
716 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
717 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
718 e.setAttribute(GSXML.NAME_ATT, IMPORT_SERVICE);
719 this.short_service_info.appendChild(e);
720
721 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
722 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
723 e.setAttribute(GSXML.NAME_ATT, BUILD_SERVICE);
724 this.short_service_info.appendChild(e);
725
726 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
727 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
728 e.setAttribute(GSXML.NAME_ATT, ACTIVATE_SERVICE);
729 this.short_service_info.appendChild(e);
730
731 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
732 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
733 e.setAttribute(GSXML.NAME_ATT, BUILD_AND_ACTIVATE_SERVICE);
734 this.short_service_info.appendChild(e);
735
736 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
737 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
738 e.setAttribute(GSXML.NAME_ATT, DELETE_SERVICE);
739 this.short_service_info.appendChild(e);
740
741 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
742 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
743 e.setAttribute(GSXML.NAME_ATT, RELOAD_SERVICE);
744 this.short_service_info.appendChild(e);
745
746 //e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
747 //e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
748 //e.setAttribute(GSXML.NAME_ATT, ADD_DOC_SERVICE);
749 //this.short_service_info.appendChild(e);
750
751 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
752 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
753 e.setAttribute(GSXML.NAME_ATT, MODIFY_METADATA_SERVICE);
754 this.short_service_info.appendChild(e);
755
756 return true;
757 }
758
759 protected Element runCommand(Element request, int type) {
760 return runCommand(request, type, null);
761 }
762
763 /** returns a response element */
764 protected Element runCommand(Element request, int type, String[] docids)
765 {
766 Document result_doc = XMLConverter.newDOM();
767 // the response to send back
768 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
769 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
770 response.setAttribute(GSXML.FROM_ATT, name);
771 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
772 response.appendChild(status);
773
774 String lang = request.getAttribute(GSXML.LANG_ATT);
775 String request_type = request.getAttribute(GSXML.TYPE_ATT);
776
777 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
778
779 //GSXML.elementToLogAsString("### Extracted param_list: ", param_list, true);
780 //GSXML.elementToLogAsUnicodeDebugString("### DEBUG Extracted param_list: ", param_list, true);
781
782 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
783
784 boolean get_status_only = false;
785 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
786 {
787 get_status_only = true;
788 }
789
790 // just check for status messages if that's all that's required
791 if (get_status_only)
792 {
793 String id = (String) params.get(PROCESS_ID_PARAM);
794 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
795 GS2PerlListener listener = this.listeners.get(id);
796 if (listener == null)
797 {
798 Text t = result_doc.createTextNode(getTextString("general.process_id_error", lang));
799 status.appendChild(t);
800 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
801 }
802 else
803 {
804 Text t = result_doc.createTextNode(listener.getUpdate());
805 status.appendChild(t);
806 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
807 // check that we actually should be removing the listener here
808 if (listener.isFinished())
809 { // remove this listener - its job is done
810 this.listeners.remove(id); // not working
811 }
812 }
813 return response;
814
815 }
816
817 // do the actual command
818 String coll_name = null;
819 if (type == GS2PerlConstructor.NEW)
820 {
821 String coll_title = (String) params.get(NEW_COL_TITLE_PARAM);
822 coll_name = createNewCollName(coll_title);
823 }
824 else
825 {
826 coll_name = (String) params.get(COL_PARAM);
827 }
828
829 // makes a paramList of the relevant params
830 Element other_params = extractOtherParams(params, type);
831
832 //create the constructor to do the work
833 GS2PerlConstructor constructor = new GS2PerlConstructor("perl_build");
834 if (!constructor.configure())
835 {
836 Text t = result_doc.createTextNode(getTextString("general.configure_constructor_error", lang));
837 status.appendChild(t);
838 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
839 return response;
840 }
841
842 constructor.setSiteHome(this.site_home);
843 constructor.setLibraryName(this.library_name);
844 constructor.setCollectionName(coll_name);
845 constructor.setActionType(type);
846 constructor.setProcessParams(other_params);
847 if (type == GS2PerlConstructor.IMPORT)
848 {
849 constructor.setManifestFile(this.site_home + File.separator + "collect" + File.separator + params.get(COL_PARAM) + File.separator + "manifests" + File.separator + "tempManifest.xml");
850 }
851 else if (type == GS2PerlConstructor.MODIFY_METADATA_SERVER) {
852 StringBuffer querystring = new StringBuffer();
853
854 // convert params into a single string again?
855 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
856 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
857
858 String oid = null;
859
860 while (i.hasNext()) {
861
862 Map.Entry<String, Serializable> entry = i.next();
863 String paramname = entry.getKey();
864 paramname = paramname.replace("s1.", ""); // replaces all occurrences
865 if (paramname.equals("collection")) {
866 paramname = "c";
867 }
868 if (paramname.equals("d")){
869 oid = (String) entry.getValue();
870 }
871
872 String paramvalue = (String) entry.getValue();
873
874 // This will be used to set QUERY_STRING in the environment, see build/GS2PerlConstructor.java
875 // Need to also ensure that special characters in param values won't get clobbered on Windows by perl/CGI.pm,
876 // see also https://www.nntp.perl.org/group/perl.perl5.porters/2016/10/msg240120.html
877 // Therefore URL encode CGI param values in the query_string, as at https://stackoverflow.com/questions/10786042/java-url-encoding-of-query-string-parameters
878
879 // perl/CGI.pm doesn't like us URL encoding the entire QUERY_STRING such as the equal sign between each paramName and paramValue.
880 // So we URL encode each paramValue separately, which is done in GS2Construct.java::runCommand()
881 querystring.append(paramname + "=" + urlEncodeValue(paramname, paramvalue));
882 if (i.hasNext()) {
883 querystring.append("&");
884 }
885 }
886
887 // Mark files for reindexing (e.g. if set-meta or remove-meta was called)
888 // Note that remove-meta doesn't mean the document should be marked for
889 // Deletion: only meta was removed.
890
891 if (oid != null) { // if we have only one oid
892 markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid));
893 } else if (docids != null) { // check if we are dealing with many doc ids, as cold in theory happen when set-metadata-array is called
894
895 for(int index = 0; index < docids.length; index++) {
896 String docid = docids[index];
897 markDocumentInFlatDatabase("R", coll_name, OID.getTop(docid));
898 }
899 } else {
900 String msg = getTextString("general.no_valid_docid_error", lang);
901 logger.error("*** " + msg);
902 Text t = result_doc.createTextNode(msg);
903 status.appendChild(t);
904 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
905 return response;
906 }
907
908 constructor.setQueryString(querystring.toString());
909 //logger.info("@@@@ perl querystring: " + querystring.toString());
910 }
911
912 GS2PerlListener listener = new GS2PerlListener();
913 constructor.addListener(listener);
914 constructor.start();
915
916 String id = newID();
917 this.listeners.put(id, listener);
918
919 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
920 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ACCEPTED));
921 Text t = result_doc.createTextNode(getTextString("general.process_start", lang));
922 status.appendChild(t);
923 return response;
924 }
925
926 //************************
927 // some helper functions
928 //************************
929
930 private String urlEncodeValue(String paramName, String paramVal) {
931 StringBuffer modParamVal = new StringBuffer();
932 try{
933 // don't do this, as it will double encode semicolons (and other such regular chars that will get URL encoded).
934 // But then perl won't double-decode to get original semicolon back.
935 // paramVal += URLEncoder.encode(oldParamVal, "UTF-8");
936
937 // Instead, just encode chars whose int value is >= 128
938 for(int i = 0; i < paramVal.length(); i++) {
939 char c = paramVal.charAt(i);
940 if(c >= 128) {
941 modParamVal.append(URLEncoder.encode(""+c, "UTF-8"));
942 // Better way as per https://stackoverflow.com/questions/10786042/java-url-encoding-of-query-string-parameters
943 // but our release kits' Java is too old for importing java.nio.charset.StandardCharsets
944 //paramVal = URLEncoder.encode(""+c, StandardCharsets.UTF_8.name());
945 } else {
946 modParamVal.append(c);
947 }
948 }
949
950 //logger.error("@@@@@@@@ old paramVal for paramName " + paramName + "is: |" + paramVal + "|");
951 paramVal = modParamVal.toString();
952 //logger.error("@@@@@@@@ new param val for paramName " + paramName + " is: |" + paramVal + "|");
953
954 } catch(UnsupportedEncodingException uee) {
955 logger.warn("**** Unable to encode query_string param " + paramName + " in UTF-8, so attempting to continue with its unencoded value."); // don't output param value to log, in case of sensitive data?
956 }
957
958 return paramVal;
959 }
960
961 /** parse the collect directory and return a list of collection names */
962 protected String[] getCollectionList()
963 {
964
965 File collectDir = new File(GSFile.collectDir(this.site_home));
966 if (!collectDir.exists())
967 {
968 logger.error("couldn't find collect dir: " + collectDir.toString());
969 return null;
970 }
971 logger.info("GS2Construct: reading thru directory " + collectDir.getPath() + " to find collections.");
972 File[] contents = collectDir.listFiles();
973 int num_colls = 0;
974 for (int i = 0; i < contents.length; i++)
975 {
976 if (contents[i].isDirectory() && !contents[i].getName().startsWith("CVS"))
977 {
978 num_colls++;
979 }
980 }
981
982 String[] names = new String[num_colls];
983
984 for (int i = 0, j = 0; i < contents.length; i++)
985 {
986 if (contents[i].isDirectory())
987 {
988 String colName = contents[i].getName();
989 if (!colName.startsWith("CVS"))
990 {
991 names[j] = colName;
992 j++;
993 }
994
995 }
996 }
997
998 return names;
999
1000 }
1001
1002 /** ids used for process id */
1003 private int current_id = 0;
1004
1005 private String newID()
1006 {
1007 current_id++;
1008 return Integer.toString(current_id);
1009 }
1010
1011 /** creates a new short name from the collection title */
1012 protected String createNewCollName(String coll_title)
1013 {
1014
1015 String base_name = null;
1016 // take the first 6 letters
1017 if (coll_title.length() < 6)
1018 {
1019 base_name = coll_title;
1020 }
1021 else
1022 {
1023 base_name = coll_title.substring(0, 6);
1024 }
1025 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name));
1026 if (!coll_dir.exists())
1027 { // this name is ok - not used yet
1028 return base_name;
1029 }
1030
1031 // now we have to make a new name until we get a good one
1032 // try name1, name2 name3 etc
1033 int i = 0;
1034 while (coll_dir.exists())
1035 {
1036 i++;
1037 coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name + Integer.toString(i)));
1038 }
1039 return base_name + Integer.toString(i);
1040
1041 }
1042
1043 /**
1044 * takes the params from the request (in the HashMap) and extracts any that
1045 * need to be passed to the constructor and puts them into a paramList
1046 * element
1047 */
1048 protected Element extractOtherParams(HashMap<String, Serializable> params, int type)
1049 {
1050 Document doc = XMLConverter.newDOM();
1051 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1052 if (type == GS2PerlConstructor.NEW)
1053 {
1054 Element param = doc.createElement(GSXML.PARAM_ELEM);
1055 param.setAttribute(GSXML.NAME_ATT, "creator");
1056 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(CREATOR_PARAM));
1057
1058 param_list.appendChild(param);
1059 param = doc.createElement(GSXML.PARAM_ELEM);
1060 param.setAttribute(GSXML.NAME_ATT, "about");
1061 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_ABOUT_PARAM));
1062 param_list.appendChild(param);
1063 param = doc.createElement(GSXML.PARAM_ELEM);
1064 param.setAttribute(GSXML.NAME_ATT, "title");
1065 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_TITLE_PARAM));
1066 param_list.appendChild(param);
1067 param = doc.createElement(GSXML.PARAM_ELEM);
1068 param.setAttribute(GSXML.NAME_ATT, "buildtype");
1069 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(BUILDTYPE_PARAM));
1070 param_list.appendChild(param);
1071 return param_list;
1072 }
1073
1074 // other ones dont have params yet
1075 return null;
1076 }
1077
1078 protected void waitUntilReady(Element request)
1079 {
1080 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1081 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1082
1083 String collection = (String) params.get(COL_PARAM);
1084
1085 if (checkCollectionIsNotBusy(collection))
1086 {
1087 return;
1088 }
1089
1090 while (!checkCollectionIsNotBusy(collection)) // When the collection ceases to be busy, we place a hold on it
1091 {
1092 try
1093 {
1094 Thread.currentThread().sleep(1000);
1095 }
1096 catch (Exception ex)
1097 {
1098 ex.printStackTrace();
1099 }
1100 }
1101 }
1102
1103 protected void signalReady(Element request)
1104 {
1105 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1106 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1107
1108 String collection = (String) params.get(COL_PARAM);
1109
1110 collectionOperationMap.remove(collection);
1111 }
1112
1113 // If collection is NOT busy, then reserve it
1114 protected synchronized boolean checkCollectionIsNotBusy(String collection)
1115 {
1116 if (collectionOperationMap.get(collection) == null)
1117 {
1118 collectionOperationMap.put(collection, true);
1119 return true;
1120 }
1121 return false;
1122 }
1123
1124 protected boolean isAllowedToModifyMeta(String metaname, Pattern allowedMetaFieldsPattern)
1125 {
1126 if(allowedMetaFieldsPattern == null) { // null when user has edit permissions, so they can set any meta
1127 ///logger.info("### User has permissions to set any meta.");
1128 return true;
1129 }
1130 if(metaname == null) {
1131 ///logger.info("### Can't check null metaname against pattern");
1132 return false;
1133 }
1134
1135 Matcher m = allowedMetaFieldsPattern.matcher(metaname);
1136 if(!m.matches()) {
1137 ///logger.info("### metaname: " + metaname + " doesn't match allowed allowed fields: " + allowedMetaFieldsPattern.toString());
1138 return false;
1139 } else {
1140 return true;
1141 }
1142 }
1143
1144 protected String[] getDocIdsWithOptFilter(String json_str, Pattern filterFields) // boolean strictOrPermissible
1145 {
1146 if(json_str == null) {
1147 logger.error("### Shouldn't be happening: null json string");
1148 return null;
1149 }
1150
1151 String[] docids = null;
1152
1153 // check that the name of each metadata being set matches the pattern filterFields.
1154 // The presence of any other meta means something other than adding user comments is being attempted,
1155 // which is invalid
1156 try {
1157
1158 JSONArray docInfoList = new JSONArray(json_str);
1159 docids = new String[docInfoList.length()];
1160 for(int index = 0; index < docInfoList.length(); index++) {
1161 JSONObject docInfo = docInfoList.getJSONObject(index);
1162 if(docInfo.has("metatable")) { // should exist for metadata arrays
1163
1164 docids[index] = docInfo.getString("docid"); // should exist if metatable existed
1165
1166 ///logger.info("@@@ Found docid: " + docids[index]);
1167
1168 JSONArray metatable = docInfo.getJSONArray("metatable");
1169 for(int i = 0; i < metatable.length(); i++) {
1170 JSONObject meta = metatable.getJSONObject(i);
1171
1172 String metaname = meta.getString("metaname");
1173 ///logger.info("### metaname: " + metaname);
1174
1175 if(!isAllowedToModifyMeta(metaname, filterFields)) {
1176 return null;
1177 }
1178 }
1179 } else {
1180 // can also have set-metadatat-array without metatable field, like
1181 // json=[{"docid":"HASHc5bce2d6d3e5b04e470ec9","metaname":"Title","metavalue":"Tralalala","metamode":"accumulate"},{"docid":"HASHbe483fa4df4e096335d1c8","metaname":"Title","metavalue":"Lala was here","metapos":0, "metamode":"override"}]
1182 // though we use the metatable way for setting user comments meta.
1183 // The alternative way is described as example usage in perllib/cgiactions/modmetadataaction.pm
1184 // and also the way it's already coded up there, so we need to allow it here.
1185 docids[index] = docInfo.getString("docid");
1186 String metaname = docInfo.getString("metaname");
1187 if(!isAllowedToModifyMeta(metaname, filterFields)) {
1188 return null;
1189 }
1190 }
1191 }
1192 } catch(JSONException jsonex) {
1193 logger.error("Exception when parsing json string: " + json_str);
1194 logger.error(jsonex);
1195
1196 }
1197
1198 // if we're here, then it means that the JSON only asked for username|usercomment|usertimestamp meta
1199 // meaning that the setmeta operation was a valid user comment operation.
1200 // In that case, we have a docid for which we need to add a user comment
1201 // set-metadata-array can take more docids, but that doesn't happen when creating a user
1202 // comment (though it now happens when editing existing user comments). And one comment is
1203 // added at a time, but 3 meta fields are set for each comment: username, usercomment and
1204 // timestamp, hence the use of set-meta-array.
1205 return docids;
1206
1207 }
1208
1209 protected Element errorResponse(String serviceName, String errorKey, String lang) {
1210 String error_message = getTextString(errorKey, lang, Dictionary.core_servlet_dictionary_name);
1211 Document result_doc = XMLConverter.newDOM();
1212 Element result = GSXML.createBasicResponse(result_doc, serviceName);
1213 GSXML.addError(result, error_message);
1214 return result;
1215 }
1216
1217 /** Copy from DebugService.userHasEditPermissions
1218 This function checks that the user is logged in and that the user
1219 is in the right group to edit the collection */
1220 protected boolean userHasCollectionEditPermissions(Element request) {
1221 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1222 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1223
1224 return userHasCollectionEditPermissions(request, params);
1225
1226 }
1227
1228 protected boolean userIsAdministrator(Element request, HashMap<String, Serializable> params) {
1229
1230 UserContext context = new UserContext(request);
1231
1232 for (String group : context.getGroups()) {
1233 // administrator always has permission
1234 if (group.equals("administrator")) {
1235 return true;
1236 }
1237 }
1238
1239 return false;
1240 }
1241
1242 protected boolean userHasCollectionEditPermissions(Element request, HashMap<String, Serializable> params) {
1243 String collection = (String) params.get(COL_PARAM); // could be null on newcoll operation
1244
1245
1246 UserContext context = new UserContext(request);
1247 if(collection == null) {
1248 return !context.getUsername().equals("");
1249 }
1250
1251 /*
1252 // FOR DEBUGGING
1253 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
1254 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
1255
1256 StringBuffer parametersLine = new StringBuffer();
1257 while (i.hasNext()) {
1258
1259 Map.Entry<String, Serializable> entry = i.next();
1260 String paramname = entry.getKey();
1261 String paramvalue = (String) entry.getValue();
1262
1263 parametersLine.append("\t" + paramname + ": " + paramvalue + "\n");
1264 }
1265
1266 logger.info("XXXXXXXXXXXXX PARAMETERS:\n" + parametersLine);
1267 */
1268
1269 for (String group : context.getGroups()) {
1270 // administrator always has permission
1271 if (group.equals("administrator")) {
1272 return true;
1273 }
1274 // all-collections-editor can edit any collection
1275 if (!collection.equals("")) {
1276 if (group.equals("all-collections-editor")) {
1277 return true;
1278 }
1279 if (group.equals(collection+"-collection-editor")) {
1280 return true;
1281 }
1282 }
1283 }
1284 // haven't found a group with edit permissions
1285 return false;
1286
1287 }
1288 protected void markDocumentInFlatDatabase(String mark, String collection, String oid) {
1289
1290 Document msg_doc = XMLConverter.newDOM();
1291 Element message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1292 UserContext userContext = new UserContext();
1293 Element query_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_DESCRIBE , collection, userContext);
1294 message.appendChild(query_request);
1295 Element result = (Element) this.router.process(message);
1296 Element resp_elem = (Element) GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
1297 Element coll_elem = (Element) GSXML.getChildByTagName(resp_elem, GSXML.COLLECTION_ELEM);
1298 String dbtype = coll_elem.getAttribute(GSXML.DB_TYPE_ATT);
1299
1300 SimpleCollectionDatabase coll_db = new SimpleCollectionDatabase(dbtype);
1301 if (!coll_db.databaseOK())
1302 {
1303 logger.error("Couldn't create the collection database of type " + dbtype);
1304 return;
1305 }
1306
1307 // Open database for reading. It may not exist if collection is pre-built without archives (such as demo collections)
1308 String coll_db_file = GSFile.archivesDatabaseFile(this.site_home, collection, dbtype);
1309 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ))
1310 {
1311 logger.error("Could not open collection archives database. Database doesn't exist or else somebody's already using it?");
1312 return;
1313 }
1314 // now we know we have an archives folder
1315 String old_value = coll_db.getValue(oid);
1316 String new_value = "<index-status>" + mark;
1317 if(old_value == null) {
1318 logger.error("### null old_value in flat DB for oid " + oid);
1319 } else {
1320 new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
1321 logger.info("### Replacing db entry for oid " + oid + " which has old_value " + old_value);
1322 logger.info("### with new value " + new_value);
1323
1324 }
1325 // Close database for reading
1326 coll_db.closeDatabase();
1327
1328 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
1329 {
1330 logger.error("Could not open collection archives database. Somebody already using this database!");
1331 return;
1332 }
1333
1334 coll_db.setValue(oid, new_value);
1335
1336 coll_db.closeDatabase();
1337 }
1338}
Note: See TracBrowser for help on using the repository browser.