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

Last change on this file since 33111 was 33111, checked in by ak19, 5 years ago

GS309RC1 had a bug when doc editing a meta like ex.Title that contained a semi-colon. The final commits for rc1 that would URLEncode CGI param values in Java passed into perl, encoded the entirety of each paramvalue. This solved the initial problem of taking care of special chars like the degree symbol. But the extra URL encoding introduced a new problem: characters like the semi-colon, meaningful in URLs, were already encoded and would end up with another layer of encoding which perl wouldn't know to double-decode, so that the requested prevMetaValue would not match and new meta set to replace old would always accumulate instead of replace again. I think Dr Bainbridge had already mentioned the solution initially, when he discussed how to take care of special chars: URL encode just char values over 128. Except I didn't understand the relevance then, so I didn't think of it in the initial solution. When I reinvented the solution to resolve the 2nd problem, what he had earlier said was the full solution to the first problem finally made sense.

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