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

Last change on this file since 37204 was 37204, checked in by davidb, 15 months ago

Copy tidy-up, mostly changing whitespace to tabs to improve indentation display

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