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

Last change on this file since 37585 was 37585, checked in by davidb, 13 months ago

moved setting the status error code to be earlier so it is guaranteed to be set where as before it was only set if there was an error; refactored some variable names

  • Property svn:keywords set to Author Date Id Revision
File size: 47.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.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 initial_response_status_code = Integer.parseInt(statusElem.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
415 if (GSStatus.isError(initial_response_status_code)) {
416 logger.info("Got error status code: " + initial_response_status_code);
417 statusString += "But got error status code: " + initial_response_status_code;
418 } else { // check for Construction event errors
419 String id = statusElem.getAttribute("pid");
420 GS2PerlListener currentListener = this.listeners.get(id);
421 int completed_status_code = currentListener.getStatus();
422 // update statusElem so we return the status code on completion
423 statusElem.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(completed_status_code));
424 if (GSStatus.isError(completed_status_code))
425 {
426 logger.info("Got construction event error. Error status code: " + completed_status_code);
427 statusString += "But got construction event error, status code: " + completed_status_code;
428 // add the rest of the messages to the statusElem node
429 statusString += "\n" + currentListener.getUpdate();
430 }
431 }
432 // can finally set statusString
433 GSXML.setNodeText(statusElem, statusString);
434
435 // release hold on collection
436 signalReady(request);
437 return response;
438
439 }
440
441 protected Element processActivateCollection(Element request)
442 {
443
444 String lang = request.getAttribute(GSXML.LANG_ATT);
445 if (!userHasCollectionEditPermissions(request)) {
446 return errorResponse("processActivateCollection", NO_PERMISSIONS_ERROR, lang);
447 }
448
449 // this activates the collection on disk. but now we need to tell
450 // the MR about it. but we have to wait until the process is finished.
451 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
452 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
453 String coll_name = (String) params.get(COL_PARAM);
454
455
456 UserContext userContext = new UserContext(request);
457 String request_type = request.getAttribute(GSXML.TYPE_ATT);
458
459 // now we de-activate the collection before running activate.pl, and then re-activate at end
460 // So activate.pl only does the moving, no activation. This way will prevent java from launching
461 // perl, exiting and then leaving dangling file handles (on index/text/col.gdb) in perl.
462 if (!request_type.equals(GSXML.REQUEST_TYPE_STATUS)) {
463 systemRequest("delete", coll_name, null, userContext); // deactivate collection
464 }
465
466 Element response = runCommand(request, GS2PerlConstructor.ACTIVATE); // if request is for STATUS, then this won't run activate.pl
467
468 Element status = (Element) GSXML.getChildByTagName(response, GSXML.STATUS_ELEM);
469
470 // check for finished
471 int status_code = Integer.parseInt(status.getAttribute(GSXML.STATUS_ERROR_CODE_ATT));
472 if (GSStatus.isCompleted(status_code) && GSStatus.isError(status_code))
473 {
474 // we shouldn't carry out the next bit, just return the response
475 return response;
476 }
477 String id = status.getAttribute(GSXML.STATUS_PROCESS_ID_ATT);
478 GS2PerlListener listener = this.listeners.get(id);
479 if (listener == null)
480 {
481 logger.error("somethings gone wrong, couldn't find the listener");
482 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
483 return response;
484 }
485
486 while (!GSStatus.isCompleted(status_code))
487 {
488 // wait for the process, and keep checking the status code
489 // there is probably a better way to do this.
490 try
491 {
492 Thread.currentThread().sleep(100);
493 }
494 catch (Exception e)
495 { // ignore
496 }
497 status_code = listener.getStatus();
498 }
499
500 // add the rest of the messages to the status node
501 Text t = status.getOwnerDocument().createTextNode("\n" + listener.getUpdate());
502 status.appendChild(t);
503 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
504 if (GSStatus.isError(status_code))
505 {
506 return response; // without doing the next bit
507 }
508
509 t = status.getOwnerDocument().createTextNode("\n");
510 status.appendChild(t);
511 // once have got here, we assume
512 // the first bit proceeded successfully, now reload the collection (sends a collection reactivation request)
513 systemRequest("reload", coll_name, status, userContext); // this will append more messages to the status, and overwrite the error code att
514 return response;
515
516 }
517
518 protected Element processDeleteCollection(Element request)
519 {
520
521 if (!userHasCollectionEditPermissions(request)) {
522 String lang = request.getAttribute(GSXML.LANG_ATT);
523 return errorResponse("processDeleteCollection", NO_PERMISSIONS_ERROR, lang);
524 }
525
526 Document result_doc = XMLConverter.newDOM();
527 // the response to send back
528 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
529 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
530 response.setAttribute(GSXML.FROM_ATT, name);
531 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
532 response.appendChild(status);
533 Text t = null; // the text node for the error/success message
534 String lang = request.getAttribute(GSXML.LANG_ATT);
535 String request_type = request.getAttribute(GSXML.TYPE_ATT);
536
537 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
538 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
539
540 boolean get_status_only = false;
541 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
542 {
543 get_status_only = true;
544 }
545 if (get_status_only)
546 {
547 // 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
548 logger.error("had a status request for delete - this shouldn't happen!!");
549 //t = result_doc.createTextNode("");
550 //status.appendChild(t);
551 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
552 return response;
553 }
554 String coll_name = (String) params.get(COL_PARAM);
555 String[] args = { coll_name };
556 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, coll_name));
557 // check that the coll is there in the first place
558 if (!coll_dir.exists())
559 {
560 t = result_doc.createTextNode(getTextString("delete.exists_error", lang, args));
561 status.appendChild(t);
562 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
563 return response;
564 }
565
566 // try to delete the directory
567 if (!GSFile.deleteFile(coll_dir))
568 {
569 t = result_doc.createTextNode(getTextString("delete.delete_error", lang, args));
570 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
571 status.appendChild(t);
572 return response;
573 }
574
575 UserContext userContext = new UserContext(request);
576
577 systemRequest("delete", coll_name, status, userContext);
578 return response;
579 }
580
581 protected Element processReloadCollection(Element request)
582 {
583 if (!userHasCollectionEditPermissions(request)) {
584 String lang = request.getAttribute(GSXML.LANG_ATT);
585 return errorResponse("processReloadCollection", NO_PERMISSIONS_ERROR, lang);
586 }
587
588 Document result_doc = XMLConverter.newDOM();
589 // the response to send back
590 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
591 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
592 response.setAttribute(GSXML.FROM_ATT, name);
593 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
594 response.appendChild(status);
595 Text t = null; // the text node for the error/success message
596
597 String lang = request.getAttribute(GSXML.LANG_ATT);
598 String request_type = request.getAttribute(GSXML.TYPE_ATT);
599
600 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
601 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
602
603 boolean get_status_only = false;
604 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
605 {
606 get_status_only = true;
607 }
608 if (get_status_only)
609 {
610 // reload is synchronous - this makes no sense
611 logger.error("had a status request for reload - this shouldn't happen!!");
612 //t = result_doc.createTextNode("");
613 //status.appendChild(t);
614 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
615 return response;
616 }
617
618 String coll_name = (String) params.get(COL_PARAM);
619
620 UserContext userContext = new UserContext(request);
621
622 systemRequest("reload", coll_name, status, userContext);
623 return response;
624
625 }
626
627 /**
628 * send a configure request to the message router action name should be
629 * "delete" or "reload" response will be put into the status element
630 */
631 protected void systemRequest(String operation, String coll_name, Element status, UserContext userContext)
632 {
633 // send the request to the MR
634 Document doc = XMLConverter.newDOM();
635 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
636 Element request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", userContext);
637 message.appendChild(request);
638 Element command = doc.createElement(GSXML.SYSTEM_ELEM);
639 request.appendChild(command);
640 command.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, GSXML.COLLECTION_ELEM);
641 command.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, coll_name);
642
643 if (operation.equals("delete"))
644 {
645 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_DEACTIVATE);
646 }
647 else if (operation.equals("reload"))
648 {
649 command.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_ACTIVATE);
650 }
651 else
652 {
653 logger.error("invalid action name passed to systemRequest:" + operation);
654 return;
655 }
656 request.appendChild(command);
657 Node response = this.router.process(message); // at the moment, get no info in response so ignore it
658 Text t;
659 String[] args = { coll_name };
660
661 if (status != null)
662 {
663 if (response == null)
664 {
665 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".configure_error", userContext.getLanguage(), args));
666 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
667 status.appendChild(t);
668 return;
669 }
670
671 // if we got here, we have succeeded!
672 t = status.getOwnerDocument().createTextNode(getTextString(operation + ".success", userContext.getLanguage(), args));
673 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.SUCCESS));
674 status.appendChild(t);
675 }
676 }
677
678 /**
679 * configure the service module for now, all services have type=build - need
680 * to think about this
681 */
682 public boolean configure(Element info, Element extra_info)
683 {
684 if (!super.configure(info, extra_info))
685 {
686 return false;
687 }
688
689 logger.info("configuring GS2Construct");
690
691 Element e = null;
692 // hard code in the services for now
693
694 // set up short_service_info_ - for now just has name and type
695
696 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
697 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
698 e.setAttribute(GSXML.NAME_ATT, NEW_SERVICE);
699 this.short_service_info.appendChild(e);
700
701 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
702 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
703 e.setAttribute(GSXML.NAME_ATT, IMPORT_SERVICE);
704 this.short_service_info.appendChild(e);
705
706 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
707 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
708 e.setAttribute(GSXML.NAME_ATT, BUILD_SERVICE);
709 this.short_service_info.appendChild(e);
710
711 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
712 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
713 e.setAttribute(GSXML.NAME_ATT, ACTIVATE_SERVICE);
714 this.short_service_info.appendChild(e);
715
716 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
717 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
718 e.setAttribute(GSXML.NAME_ATT, BUILD_AND_ACTIVATE_SERVICE);
719 this.short_service_info.appendChild(e);
720
721 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
722 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
723 e.setAttribute(GSXML.NAME_ATT, DELETE_SERVICE);
724 this.short_service_info.appendChild(e);
725
726 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
727 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
728 e.setAttribute(GSXML.NAME_ATT, RELOAD_SERVICE);
729 this.short_service_info.appendChild(e);
730
731 //e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
732 //e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
733 //e.setAttribute(GSXML.NAME_ATT, ADD_DOC_SERVICE);
734 //this.short_service_info.appendChild(e);
735
736 e = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
737 e.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_PROCESS);
738 e.setAttribute(GSXML.NAME_ATT, MODIFY_METADATA_SERVICE);
739 this.short_service_info.appendChild(e);
740
741 return true;
742 }
743
744 protected Element runCommand(Element request, int type) {
745 return runCommand(request, type, null);
746 }
747
748 /** returns a response element */
749 protected Element runCommand(Element request, int type, String[] docids)
750 {
751 Document result_doc = XMLConverter.newDOM();
752 // the response to send back
753 String name = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
754 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
755 response.setAttribute(GSXML.FROM_ATT, name);
756 Element status = result_doc.createElement(GSXML.STATUS_ELEM);
757 response.appendChild(status);
758
759 String lang = request.getAttribute(GSXML.LANG_ATT);
760 String request_type = request.getAttribute(GSXML.TYPE_ATT);
761
762 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
763
764 //GSXML.elementToLogAsString("### Extracted param_list: ", param_list, true);
765 //GSXML.elementToLogAsUnicodeDebugString("### DEBUG Extracted param_list: ", param_list, true);
766
767 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
768
769 boolean get_status_only = false;
770 if (request_type.equals(GSXML.REQUEST_TYPE_STATUS))
771 {
772 get_status_only = true;
773 }
774
775 // just check for status messages if that's all that's required
776 if (get_status_only)
777 {
778 String id = (String) params.get(PROCESS_ID_PARAM);
779 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
780 GS2PerlListener listener = this.listeners.get(id);
781 if (listener == null)
782 {
783 Text t = result_doc.createTextNode(getTextString("general.process_id_error", lang));
784 status.appendChild(t);
785 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
786 }
787 else
788 {
789 Text t = result_doc.createTextNode(listener.getUpdate());
790 status.appendChild(t);
791 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(listener.getStatus()));
792 // check that we actually should be removing the listener here
793 if (listener.isFinished())
794 { // remove this listener - its job is done
795 this.listeners.remove(id); // not working
796 }
797 }
798 return response;
799
800 }
801
802 // do the actual command
803 String coll_name = null;
804 if (type == GS2PerlConstructor.NEW)
805 {
806 String coll_title = (String) params.get(NEW_COL_TITLE_PARAM);
807 coll_name = createNewCollName(coll_title);
808 }
809 else
810 {
811 coll_name = (String) params.get(COL_PARAM);
812 }
813
814 // makes a paramList of the relevant params
815 Element other_params = extractOtherParams(params, type);
816
817 //create the constructor to do the work
818 GS2PerlConstructor constructor = new GS2PerlConstructor("perl_build");
819 if (!constructor.configure())
820 {
821 Text t = result_doc.createTextNode(getTextString("general.configure_constructor_error", lang));
822 status.appendChild(t);
823 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
824 return response;
825 }
826
827 constructor.setSiteHome(this.site_home);
828 constructor.setLibraryName(this.library_name);
829 constructor.setCollectionName(coll_name);
830 constructor.setActionType(type);
831 constructor.setProcessParams(other_params);
832 if (type == GS2PerlConstructor.IMPORT)
833 {
834 constructor.setManifestFile(this.site_home + File.separator + "collect" + File.separator + params.get(COL_PARAM) + File.separator + "manifests" + File.separator + "tempManifest.xml");
835 }
836 else if (type == GS2PerlConstructor.MODIFY_METADATA_SERVER) {
837 StringBuffer querystring = new StringBuffer();
838
839 // convert params into a single string again?
840 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
841 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
842
843 String oid = null;
844
845 while (i.hasNext()) {
846
847 Map.Entry<String, Serializable> entry = i.next();
848 String paramname = entry.getKey();
849 paramname = paramname.replace("s1.", ""); // replaces all occurrences
850 if (paramname.equals("collection")) {
851 paramname = "c";
852 }
853 if (paramname.equals("d")){
854 oid = (String) entry.getValue();
855 }
856
857 String paramvalue = (String) entry.getValue();
858
859 // This will be used to set QUERY_STRING in the environment, see build/GS2PerlConstructor.java
860 // Need to also ensure that special characters in param values won't get clobbered on Windows by perl/CGI.pm,
861 // see also https://www.nntp.perl.org/group/perl.perl5.porters/2016/10/msg240120.html
862 // Therefore URL encode CGI param values in the query_string, as at https://stackoverflow.com/questions/10786042/java-url-encoding-of-query-string-parameters
863
864 // perl/CGI.pm doesn't like us URL encoding the entire QUERY_STRING such as the equal sign between each paramName and paramValue.
865 // So we URL encode each paramValue separately, which is done in GS2Construct.java::runCommand()
866 querystring.append(paramname + "=" + urlEncodeValue(paramname, paramvalue));
867 if (i.hasNext()) {
868 querystring.append("&");
869 }
870 }
871
872 if (oid != null) { // if we have only one oid
873 markDocumentInFlatDatabase("R", coll_name, OID.getTop(oid));
874 } else if (docids != null) { // check if we are dealing with many doc ids, as cold in theory happen when set-metadata-array is called
875
876 for(int index = 0; index < docids.length; index++) {
877 String docid = docids[index];
878 markDocumentInFlatDatabase("R", coll_name, OID.getTop(docid));
879 }
880 } else {
881 String msg = getTextString("general.no_valid_docid_error", lang);
882 logger.error("*** " + msg);
883 Text t = result_doc.createTextNode(msg);
884 status.appendChild(t);
885 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ERROR));
886 return response;
887 }
888
889 constructor.setQueryString(querystring.toString());
890 }
891
892 GS2PerlListener listener = new GS2PerlListener();
893 constructor.addListener(listener);
894 constructor.start();
895
896 String id = newID();
897 this.listeners.put(id, listener);
898
899 status.setAttribute(GSXML.STATUS_PROCESS_ID_ATT, id);
900 status.setAttribute(GSXML.STATUS_ERROR_CODE_ATT, Integer.toString(GSStatus.ACCEPTED));
901 Text t = result_doc.createTextNode(getTextString("general.process_start", lang));
902 status.appendChild(t);
903 return response;
904 }
905
906 //************************
907 // some helper functions
908 //************************
909
910 private String urlEncodeValue(String paramName, String paramVal) {
911 StringBuffer modParamVal = new StringBuffer();
912 try{
913 // don't do this, as it will double encode semicolons (and other such regular chars that will get URL encoded).
914 // But then perl won't double-decode to get original semicolon back.
915 // paramVal += URLEncoder.encode(oldParamVal, "UTF-8");
916
917 // Instead, just encode chars whose int value is >= 128
918 for(int i = 0; i < paramVal.length(); i++) {
919 char c = paramVal.charAt(i);
920 if(c >= 128) {
921 modParamVal.append(URLEncoder.encode(""+c, "UTF-8"));
922 // Better way as per https://stackoverflow.com/questions/10786042/java-url-encoding-of-query-string-parameters
923 // but our release kits' Java is too old for importing java.nio.charset.StandardCharsets
924 //paramVal = URLEncoder.encode(""+c, StandardCharsets.UTF_8.name());
925 } else {
926 modParamVal.append(c);
927 }
928 }
929
930 //logger.error("@@@@@@@@ old paramVal for paramName " + paramName + "is: |" + paramVal + "|");
931 paramVal = modParamVal.toString();
932 //logger.error("@@@@@@@@ new param val for paramName " + paramName + " is: |" + paramVal + "|");
933
934 } catch(UnsupportedEncodingException uee) {
935 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?
936 }
937
938 return paramVal;
939 }
940
941 /** parse the collect directory and return a list of collection names */
942 protected String[] getCollectionList()
943 {
944
945 File collectDir = new File(GSFile.collectDir(this.site_home));
946 if (!collectDir.exists())
947 {
948 logger.error("couldn't find collect dir: " + collectDir.toString());
949 return null;
950 }
951 logger.info("GS2Construct: reading thru directory " + collectDir.getPath() + " to find collections.");
952 File[] contents = collectDir.listFiles();
953 int num_colls = 0;
954 for (int i = 0; i < contents.length; i++)
955 {
956 if (contents[i].isDirectory() && !contents[i].getName().startsWith("CVS"))
957 {
958 num_colls++;
959 }
960 }
961
962 String[] names = new String[num_colls];
963
964 for (int i = 0, j = 0; i < contents.length; i++)
965 {
966 if (contents[i].isDirectory())
967 {
968 String colName = contents[i].getName();
969 if (!colName.startsWith("CVS"))
970 {
971 names[j] = colName;
972 j++;
973 }
974
975 }
976 }
977
978 return names;
979
980 }
981
982 /** ids used for process id */
983 private int current_id = 0;
984
985 private String newID()
986 {
987 current_id++;
988 return Integer.toString(current_id);
989 }
990
991 /** creates a new short name from the collection title */
992 protected String createNewCollName(String coll_title)
993 {
994
995 String base_name = null;
996 // take the first 6 letters
997 if (coll_title.length() < 6)
998 {
999 base_name = coll_title;
1000 }
1001 else
1002 {
1003 base_name = coll_title.substring(0, 6);
1004 }
1005 File coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name));
1006 if (!coll_dir.exists())
1007 { // this name is ok - not used yet
1008 return base_name;
1009 }
1010
1011 // now we have to make a new name until we get a good one
1012 // try name1, name2 name3 etc
1013 int i = 0;
1014 while (coll_dir.exists())
1015 {
1016 i++;
1017 coll_dir = new File(GSFile.collectionBaseDir(this.site_home, base_name + Integer.toString(i)));
1018 }
1019 return base_name + Integer.toString(i);
1020
1021 }
1022
1023 /**
1024 * takes the params from the request (in the HashMap) and extracts any that
1025 * need to be passed to the constructor and puts them into a paramList
1026 * element
1027 */
1028 protected Element extractOtherParams(HashMap<String, Serializable> params, int type)
1029 {
1030 Document doc = XMLConverter.newDOM();
1031 Element param_list = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1032 if (type == GS2PerlConstructor.NEW)
1033 {
1034 Element param = doc.createElement(GSXML.PARAM_ELEM);
1035 param.setAttribute(GSXML.NAME_ATT, "creator");
1036 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(CREATOR_PARAM));
1037
1038 param_list.appendChild(param);
1039 param = doc.createElement(GSXML.PARAM_ELEM);
1040 param.setAttribute(GSXML.NAME_ATT, "about");
1041 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_ABOUT_PARAM));
1042 param_list.appendChild(param);
1043 param = doc.createElement(GSXML.PARAM_ELEM);
1044 param.setAttribute(GSXML.NAME_ATT, "title");
1045 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(NEW_COL_TITLE_PARAM));
1046 param_list.appendChild(param);
1047 param = doc.createElement(GSXML.PARAM_ELEM);
1048 param.setAttribute(GSXML.NAME_ATT, "buildtype");
1049 param.setAttribute(GSXML.VALUE_ATT, (String) params.get(BUILDTYPE_PARAM));
1050 param_list.appendChild(param);
1051 return param_list;
1052 }
1053
1054 // other ones dont have params yet
1055 return null;
1056 }
1057
1058 protected void waitUntilReady(Element request)
1059 {
1060 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1061 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1062
1063 String collection = (String) params.get(COL_PARAM);
1064
1065 if (checkCollectionIsNotBusy(collection))
1066 {
1067 return;
1068 }
1069
1070 while (!checkCollectionIsNotBusy(collection)) // When the collection ceases to be busy, we place a hold on it
1071 {
1072 try
1073 {
1074 Thread.currentThread().sleep(1000);
1075 }
1076 catch (Exception ex)
1077 {
1078 ex.printStackTrace();
1079 }
1080 }
1081 }
1082
1083 protected void signalReady(Element request)
1084 {
1085 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1086 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1087
1088 String collection = (String) params.get(COL_PARAM);
1089
1090 collectionOperationMap.remove(collection);
1091 }
1092
1093 // If collection is NOT busy, then reserve it
1094 protected synchronized boolean checkCollectionIsNotBusy(String collection)
1095 {
1096 if (collectionOperationMap.get(collection) == null)
1097 {
1098 collectionOperationMap.put(collection, true);
1099 return true;
1100 }
1101 return false;
1102 }
1103
1104 protected boolean isAllowedToSetMeta(String metaname, Pattern allowedMetaFieldsPattern)
1105 {
1106 if(allowedMetaFieldsPattern == null) { // null when user has edit permissions, so they can set any meta
1107 ///logger.info("### User has permissions to set any meta.");
1108 return true;
1109 }
1110 if(metaname == null) {
1111 ///logger.info("### Can't check null metaname against pattern");
1112 return false;
1113 }
1114
1115 Matcher m = allowedMetaFieldsPattern.matcher(metaname);
1116 if(!m.matches()) {
1117 ///logger.info("### metaname: " + metaname + " doesn't match allowed allowed fields: " + allowedMetaFieldsPattern.toString());
1118 return false;
1119 } else {
1120 return true;
1121 }
1122 }
1123
1124 protected String[] getDocIdsWithOptFilter(String json_str, Pattern filterFields) // boolean strictOrPermissible
1125 {
1126 if(json_str == null) {
1127 logger.error("### Shouldn't be happening: null json string");
1128 return null;
1129 }
1130
1131 String[] docids = null;
1132
1133 // check that the name of each metadata being set matches the pattern filterFields.
1134 // The presence of any other meta means something other than adding user comments is being attempted,
1135 // which is invalid
1136 try {
1137
1138 JSONArray docInfoList = new JSONArray(json_str);
1139 docids = new String[docInfoList.length()];
1140 for(int index = 0; index < docInfoList.length(); index++) {
1141 JSONObject docInfo = docInfoList.getJSONObject(index);
1142 if(docInfo.has("metatable")) { // should exist for metadata arrays
1143
1144 docids[index] = docInfo.getString("docid"); // should exist if metatable existed
1145
1146 ///logger.info("@@@ Found docid: " + docids[index]);
1147
1148 JSONArray metatable = docInfo.getJSONArray("metatable");
1149 for(int i = 0; i < metatable.length(); i++) {
1150 JSONObject meta = metatable.getJSONObject(i);
1151
1152 String metaname = meta.getString("metaname");
1153 ///logger.info("### metaname: " + metaname);
1154
1155 if(!isAllowedToSetMeta(metaname, filterFields)) {
1156 return null;
1157 }
1158 }
1159 }
1160 }
1161 } catch(JSONException jsonex) {
1162 logger.error("Exception when parsing json string: " + json_str);
1163 logger.error(jsonex);
1164
1165 }
1166
1167 // if we're here, then it means that the JSON only asked for username|usercomment|usertimestamp meta
1168 // meaning that the setmeta operation was a valid user comment operation.
1169 // In that case, we have a docid for which we need to add a user comment
1170 // set-metadata-array can take more docids, but doesn't happen for a user comment. And one comment
1171 // is added at a time, but 3 meta fields are set for each comment: username, usercomment and timestamp
1172 // hence the use of set-meta-array.
1173 return docids;
1174
1175 }
1176
1177 protected Element errorResponse(String serviceName, String errorKey, String lang) {
1178 String error_message = getTextString(errorKey, lang, Dictionary.core_servlet_dictionary_name);
1179 Document result_doc = XMLConverter.newDOM();
1180 Element result = GSXML.createBasicResponse(result_doc, serviceName);
1181 GSXML.addError(result, error_message);
1182 return result;
1183 }
1184
1185 /** Copy from DebugService.userHasEditPermissions
1186 This function checks that the user is logged in and that the user
1187 is in the right group to edit the collection */
1188 protected boolean userHasCollectionEditPermissions(Element request) {
1189 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1190 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
1191
1192 return userHasCollectionEditPermissions(request, params);
1193
1194 }
1195
1196 protected boolean userHasCollectionEditPermissions(Element request, HashMap<String, Serializable> params) {
1197 String collection = (String) params.get(COL_PARAM); // could be null on newcoll operation
1198
1199
1200 UserContext context = new UserContext(request);
1201 if(collection == null) {
1202 return !context.getUsername().equals("");
1203 }
1204
1205 /*
1206 // FOR DEBUGGING
1207 Set<Map.Entry<String, Serializable>> entries = params.entrySet();
1208 Iterator<Map.Entry<String, Serializable>> i = entries.iterator();
1209
1210 StringBuffer parametersLine = new StringBuffer();
1211 while (i.hasNext()) {
1212
1213 Map.Entry<String, Serializable> entry = i.next();
1214 String paramname = entry.getKey();
1215 String paramvalue = (String) entry.getValue();
1216
1217 parametersLine.append("\t" + paramname + ": " + paramvalue + "\n");
1218 }
1219
1220 logger.info("XXXXXXXXXXXXX PARAMETERS:\n" + parametersLine);
1221 */
1222
1223 for (String group : context.getGroups()) {
1224 // administrator always has permission
1225 if (group.equals("administrator")) {
1226 return true;
1227 }
1228 // all-collections-editor can edit any collection
1229 if (!collection.equals("")) {
1230 if (group.equals("all-collections-editor")) {
1231 return true;
1232 }
1233 if (group.equals(collection+"-collection-editor")) {
1234 return true;
1235 }
1236 }
1237 }
1238 // haven't found a group with edit permissions
1239 return false;
1240
1241 }
1242 protected void markDocumentInFlatDatabase(String mark, String collection, String oid) {
1243
1244 Document msg_doc = XMLConverter.newDOM();
1245 Element message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1246 UserContext userContext = new UserContext();
1247 Element query_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_DESCRIBE , collection, userContext);
1248 message.appendChild(query_request);
1249 Element result = (Element) this.router.process(message);
1250 Element resp_elem = (Element) GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
1251 Element coll_elem = (Element) GSXML.getChildByTagName(resp_elem, GSXML.COLLECTION_ELEM);
1252 String dbtype = coll_elem.getAttribute(GSXML.DB_TYPE_ATT);
1253
1254 SimpleCollectionDatabase coll_db = new SimpleCollectionDatabase(dbtype);
1255 if (!coll_db.databaseOK())
1256 {
1257 logger.error("Couldn't create the collection database of type " + dbtype);
1258 return;
1259 }
1260
1261 // Open database for reading. It may not exist if collection is pre-built without archives (such as demo collections)
1262 String coll_db_file = GSFile.archivesDatabaseFile(this.site_home, collection, dbtype);
1263 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ))
1264 {
1265 logger.error("Could not open collection archives database. Database doesn't exist or else somebody's already using it?");
1266 return;
1267 }
1268 // now we know we have an archives folder
1269 String old_value = coll_db.getValue(oid);
1270 String new_value = "<index-status>" + mark;
1271 if(old_value == null) {
1272 logger.error("### null old_value in flat DB for oid " + oid);
1273 } else {
1274 new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
1275 logger.info("### Replacing db entry for oid " + oid + " which has old_value " + old_value);
1276 logger.info("### with new value " + new_value);
1277
1278 }
1279 // Close database for reading
1280 coll_db.closeDatabase();
1281
1282 if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
1283 {
1284 logger.error("Could not open collection archives database. Somebody already using this database!");
1285 return;
1286 }
1287
1288 coll_db.setValue(oid, new_value);
1289
1290 coll_db.closeDatabase();
1291 }
1292}
Note: See TracBrowser for help on using the repository browser.