1
2
3
4
5
6
7
8
9
10 package com.sri.emo.controller;
11
12 import java.io.StringReader;
13 import java.sql.SQLException;
14 import java.text.DateFormat;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collections;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.TreeSet;
22
23 import org.apache.log4j.Logger;
24 import org.dom4j.Document;
25 import org.dom4j.Element;
26
27 import com.jcorporate.expresso.core.controller.Block;
28 import com.jcorporate.expresso.core.controller.ControllerException;
29 import com.jcorporate.expresso.core.controller.ExpressoRequest;
30 import com.jcorporate.expresso.core.controller.ExpressoResponse;
31 import com.jcorporate.expresso.core.controller.Input;
32 import com.jcorporate.expresso.core.controller.NonHandleableException;
33 import com.jcorporate.expresso.core.controller.Output;
34 import com.jcorporate.expresso.core.controller.ServletControllerRequest;
35 import com.jcorporate.expresso.core.controller.State;
36 import com.jcorporate.expresso.core.controller.Transition;
37 import com.jcorporate.expresso.core.dataobjects.jdbc.JoinedDataObject;
38 import com.jcorporate.expresso.core.db.DBException;
39 import com.jcorporate.expresso.core.dbobj.MultiDBObject;
40 import com.jcorporate.expresso.core.misc.StringUtil;
41 import com.jcorporate.expresso.core.registry.RequestRegistry;
42 import com.jcorporate.expresso.core.security.ReadOnlyUser;
43 import com.jcorporate.expresso.core.security.SuperUser;
44 import com.jcorporate.expresso.core.security.filters.Filter;
45 import com.jcorporate.expresso.core.security.filters.RawFilter;
46 import com.jcorporate.expresso.services.dbobj.Setup;
47 import com.sri.common.taglib.InputTag;
48 import com.sri.emo.annotations.NodeTag;
49 import com.sri.emo.annotations.NodeTagController;
50 import com.sri.emo.annotations.PromptAddTag;
51 import com.sri.emo.annotations.PromptDeleteTag;
52 import com.sri.emo.dbobj.Attribute;
53 import com.sri.emo.dbobj.IndentComparator;
54 import com.sri.emo.dbobj.Node;
55 import com.sri.emo.dbobj.NodeType;
56 import com.sri.emo.dbobj.Part;
57 import com.sri.emo.dbobj.PartsFactory;
58 import com.sri.emo.dbobj.PickList;
59 import com.sri.emo.dbobj.Relation;
60 import com.sri.emo.dbobj.RelationType;
61
62 /***
63 * Handle Node manipulation.
64 *
65 * @author larry hamel
66 */
67 public class AddNodeAction extends NodeController {
68 /***
69 *
70 */
71 private static final long serialVersionUID = 1L;
72
73 public static final String VIEW_NODE = "viewNode";
74 public static final String PROMPT_EXPORT_NODE = "promptExportNode";
75 public static final String DO_EXPORT_NODE = "doExportNode";
76 public static final String PROMPT_EDIT_NODE = "promptEditNode";
77 public static final String DO_EDIT_NODE = "doEditNode";
78 public static final String PROMPT_IMPORT_NODE = "promptImportNode";
79 public static final String DO_IMPORT_NODE = "doImportNode";
80 public static final String FINISH_IMPORT = "finishImportNode";
81 public static final String INC_ATTRIB_ORDER = "incAttribOrder";
82 public static final String DEC_ATTRIB_ORDER = "decAttribOrder";
83 public static final String INC_REL_ORDER = "incRelOrder";
84 public static final String DEC_REL_ORDER = "decRelOrder";
85 public static final String EDIT_GROUP_DISPLAY = "EDIT_GROUP_DISPLAY";
86 public static final String VIEW_PART = "viewPart";
87
88 /***
89 * limit number of chars that appear in display for a given field
90 */
91 public static final int MAX_CHARS_OUTPUT = 100;
92
93 /***
94 * checkbox name
95 */
96 public static final String PICK_LIST_ITEM = "PICK_LIST_ITEM";
97 public static final String RETURN_TO = "RETURN_TO";
98 public static final String PICKLISTS_TAGNAME = "PICKLISTS";
99 public static final String IS_CHANGE_TITLE = "IS_CHANGE_TITLE";
100 public static final String APPEND_TO_TITLE = "APPEND_TO_TITLE";
101
102 private static Logger sLog = Logger.getLogger(AddNodeAction.class);
103 public static final String DELETE_STRAY_ATTRIBUTES = "DEL_STRAY_ATTRIBS";
104
105 public AddNodeAction() {
106 addState(new State(VIEW_NODE, "View a node"));
107
108 State promptState = new State(PROMPT_EDIT_NODE, "Edit Node");
109 addState(promptState);
110
111 State handleState = new State(DO_EDIT_NODE, "Submit Node");
112 addState(handleState);
113
114 addState(new State(PROMPT_IMPORT_NODE, "Prompt import node"));
115 addState(new State(DO_IMPORT_NODE, "Do import node"));
116 addState(new State(FINISH_IMPORT, "Finish import node"));
117 addState(new State(PROMPT_EXPORT_NODE, "promptExportNode"));
118 addState(new State(DO_EXPORT_NODE, "doExportNode"));
119
120 addState(new State(INC_ATTRIB_ORDER, "incAttribOrder"));
121 addState(new State(DEC_ATTRIB_ORDER, "decAttribOrder"));
122
123 addState(new State(INC_REL_ORDER, "incRelOrder"));
124 addState(new State(DEC_REL_ORDER, "decRelOrder"));
125
126 State s = new State(VIEW_PART, "View Part");
127 s.addRequiredParameter(Node.NODE_ID);
128 s.addRequiredParameter(Part.PART_ID);
129 addState(s);
130
131 setInitialState(VIEW_NODE);
132 }
133
134 /***
135 * Returns the title of this controller.
136 *
137 * @return String.
138 */
139 public String getTitle() {
140 return "AddNodeAction Controller";
141 }
142
143 /***
144 * Show view of node. if user has rights, include edit buttons
145 *
146 * @param request the <code>ExpressoRequest</code> object.
147 * @param response the <code>ExpressoResponse</code> object.
148 * @throws ControllerException upon error.
149 */
150 protected void runViewNodeState(final ExpressoRequest request,
151 final ExpressoResponse response) throws ControllerException {
152 String nodeId = request.getParameter(Node.NODE_ID);
153
154 if (nodeId == null) {
155 throw new ControllerException("nodeId is a required parameter");
156 }
157
158 try {
159 Node querynode = new Node(request, nodeId);
160
161 if (!querynode.find()) {
162 response.addError("Cannot find an item with ID=" + nodeId +
163 ". You may have used an obsolete link, where the item"
164 + " has been deleted. Please navigate to and use a "
165 + "fresh list of items.");
166
167 Transition trans = new Transition("", NodeAction.class,
168 NodeAction.INDEX);
169 if (isEmbeddedMode(request)) {
170 addEmbeddedParameter(trans);
171 }
172 trans.executeTransition(request, response);
173
174 return;
175 }
176
177
178 if (request.getParameter("isXML") != null) {
179 viewXML(querynode, request, response);
180
181 return;
182 }
183
184 querynode.view(this, request, response);
185
186 return;
187 } catch (Exception dbe) {
188 throw new ControllerException(dbe);
189 }
190 }
191
192 private void viewXML(final Node querynode, final ExpressoRequest request,
193 ExpressoResponse response) throws ControllerException {
194 response.setCustomResponse(true);
195
196 try {
197 ModelXMLWriter writer = new ModelXMLWriter();
198 Document document = writer.renderNodeAsXml(querynode, request);
199 outputXML(document, request);
200 } catch (Exception e) {
201 throw new ControllerException(e);
202 }
203 }
204
205 /***
206 * Prompt for preferred xml format.
207 *
208 * @param request the <code>ExpressoRequest</code> object.
209 * @param response the <code>ExpressoResponse</code> object.
210 * @throws ControllerException upon error.
211 */
212 protected void runPromptExportNodeState(final ExpressoRequest request,
213 final ExpressoResponse response) throws ControllerException {
214 String nodeId = request.getParameter(Node.NODE_ID);
215
216 if (nodeId == null) {
217 throw new ControllerException("nodeId is a required parameter");
218 }
219
220 try {
221 Node querynode = new Node(request, nodeId);
222
223 if (!querynode.find()) {
224 response.addError("Cannot find an item with ID=" + nodeId +
225 ". You may have used an obsolete link, where the item "
226 + "has been deleted. Please navigate to and use a " +
227 "fresh list of items.");
228
229 Transition trans = new Transition("", NodeAction.class, NodeAction.INDEX);
230 if (isEmbeddedMode(request)) {
231 addEmbeddedParameter(trans);
232 }
233 trans.executeTransition(request, response);
234
235 return;
236 }
237
238 Input in = new Input(IS_CHANGE_TITLE);
239 in.setType(Input.ATTRIBUTE_CHECKBOX);
240 in.setDefaultValue(IS_CHANGE_TITLE);
241 response.add(in);
242
243 in = new Input(APPEND_TO_TITLE);
244 in.setType(Input.ATTRIBUTE_TEXTLINE);
245 in.setDefaultValue(" copy");
246 response.add(in);
247
248 Block refsBlock = new Block(Node.REFS_ONLY);
249 response.add(refsBlock);
250
251 TreeSet set = PartsFactory.getEntities();
252
253 for (Iterator iterator = set.iterator(); iterator.hasNext();) {
254 NodeType type = (NodeType) iterator.next();
255 in = new Input(type.getEntityName());
256 in.setType(Input.ATTRIBUTE_CHECKBOX);
257 in.setDefaultValue(type.getDisplayName());
258
259 if (type.isReferenceIdPreferred()) {
260 in.setAttribute(Input.SELECTED, "true");
261 }
262
263 refsBlock.add(in);
264 }
265
266 Transition trans = new Transition(DO_EXPORT_NODE, this);
267 trans.setLabel("Export");
268 trans.addParam(Node.NODE_ID, nodeId);
269 if (isEmbeddedMode(request)) {
270 addEmbeddedParameter(trans);
271 }
272 response.add(trans);
273 } catch (Exception dbe) {
274 throw new ControllerException(dbe);
275 }
276 }
277
278 /***
279 * Prompt for preferred xml format.
280 *
281 * @param request the <code>ExpressoRequest</code> object.
282 * @param response the <code>ExpressoResponse</code> object.
283 * @throws ControllerException upon error.
284 */
285 protected void runDoExportNodeState(ExpressoRequest request,
286 ExpressoResponse response) throws ControllerException {
287 String nodeId = request.getParameter(Node.NODE_ID);
288
289 if (nodeId == null) {
290 throw new ControllerException("nodeId is a required parameter");
291 }
292
293 try {
294 Node querynode = new Node(request, nodeId);
295
296 if (!querynode.find()) {
297 response.addError("Cannot find an item with ID=" + nodeId
298 + ". You may have used an obsolete link, where the "
299 + "item has been deleted. Please navigate to and use "
300 + "a fresh list of items.");
301
302 Transition trans = new Transition("", NodeAction.class, NodeAction.INDEX);
303 if (isEmbeddedMode(request)) {
304 addEmbeddedParameter(trans);
305 }
306 trans.executeTransition(request, response);
307
308 return;
309 }
310
311 viewXML(querynode, request, response);
312
313 return;
314 } catch (Exception dbe) {
315 throw new ControllerException(dbe);
316 }
317 }
318
319
320 /***
321 * Views a given node. In otherwords, creates the data and edit
322 * links in a table format.
323 *
324 * @param querynode Node the node we're querying to form the data.
325 * @param request ExpressoRequest the ExpressoRequest object.
326 * @param response ExpressoResponse The ExpressoResponse object.
327 * @throws DBException upon error querying/operating on the node.
328 * @throws ControllerException upon error populating the ExpressoResponse
329 * object.
330 */
331 public void view(final Node querynode, final ExpressoRequest request,
332 final ExpressoResponse response) throws DBException, ControllerException {
333 boolean canEdit = querynode.canRequesterWrite();
334
335 String type = querynode.getNodeType();
336 String titleStr = querynode.getNodeTitle();
337
338 Output title = new Output(Node.NODE_TITLE, StringUtil.truncate(titleStr, MAX_CHARS_OUTPUT));
339 response.add(title);
340
341 response.add(new Output(Node.NODE_ID, querynode.getNodeId()));
342
343 Output message = new Output(Node.NODE_ANNOTATION,
344 str(querynode.getNodeAnnotation()));
345 response.add(message);
346
347 Output comment = new Output(Node.NODE_COMMENT,
348 str(querynode.getNodeComment()));
349 response.add(comment);
350
351
352 if (canEdit) {
353 addEditLink(querynode.getNodeId(), response);
354 }
355
356 String nodeTypeTitle = request.getParameter(NodeType.DISPLAY_TITLE);
357
358 if (nodeTypeTitle == null) {
359 nodeTypeTitle = NodeType.getDisplayName(type);
360 }
361
362 response.add(new Output(NodeType.DISPLAY_TITLE, nodeTypeTitle));
363
364
365
366 addParts(querynode, request, canEdit, response);
367
368 if (querynode.canRequesterAdd()) {
369
370 addCloneLink(querynode.getNodeId(), response);
371 }
372
373 if (querynode.canRequesterWrite()) {
374 addDeleteLink(querynode.getNodeId(), response);
375 }
376
377
378 addContainedByLinks(querynode.getNodeId(), response);
379 addViewXMLLink(querynode.getNodeId(), response);
380 addTreeViewLink(querynode.getNodeId(), response);
381
382 addTags(querynode, request, response);
383 }
384
385 /***
386 * Grabs all node tags and drops them into a request attribute called 'NodeTags'
387 * that the JSP can modify.
388 *
389 * @param queryNode Node the current node we're viewing.
390 * @param request ExpressoRequest the request
391 * @param response ExpressoResposne the response
392 * @throws DBException upon database error.
393 * @throws ControllerException upon adding controller element error.
394 */
395 public static void addTags(final Node queryNode, final ExpressoRequest request, final ExpressoResponse response) throws DBException, ControllerException {
396
397 Transition addTag = new Transition("addTag", "add tag", NodeTagController.class, PromptAddTag.NAME);
398 addTag.addParam("nodeId", queryNode.getNodeId());
399 response.add(addTag);
400
401
402 Block tags = new Block("NodeTags");
403
404
405 List allTags = queryNode.getTags();
406
407
408 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, request.getLocale());
409
410 ReadOnlyUser requestingUser = request.getRequestingUser();
411
412 for (Iterator i = allTags.iterator(); i.hasNext();) {
413 NodeTag eachTag = (NodeTag) i.next();
414 Block eachRow = new Block("Tag_" + eachTag.getTagId().toString());
415 eachRow.add(new Output("dateAdded", df.format(eachTag.getAddedOn())));
416 eachRow.add(new Output("tagValue", eachTag.getTagValue()));
417 eachRow.add(new Output("addedBy", eachTag.getAddedBy()));
418
419 boolean canDelete = RequestRegistry.getUser().isAdmin() ||
420 eachTag.getAddedBy().equals(requestingUser.getLoginName());
421 if (canDelete) {
422 Transition edit = new Transition("[delete]",
423 NodeTagController.class, PromptDeleteTag.NAME);
424 edit.addParam(PromptDeleteTag.TAG_ID, eachTag.getTagId().toString());
425 eachRow.add(edit);
426 }
427 tags.add(eachRow);
428 }
429
430 response.add(tags);
431
432 }
433
434
435 private void addTreeViewLink(final String nodeId, final ExpressoResponse response) throws ControllerException {
436 if (!isEmbeddedMode(response.getExpressoRequest())) {
437 Transition trans = new Transition("", NodeAction.class, NodeAction.VIEW_TREE);
438 trans.addParam(Node.NODE_ID, nodeId);
439 response.add(trans);
440 }
441 }
442
443 private void addParts(final Node node,
444 final ExpressoRequest request,
445 final boolean canEdit,
446 ExpressoResponse response) throws DBException, ControllerException {
447
448 Part[] parts = PartsFactory.getParts(node.getNodeType());
449 List allAttribs = node.getAttributes();
450 List allRelations = node.getRawRelated();
451 Block partList = new Block("allparts");
452
453
454
455
456
457
458 for (int i = 0; (parts != null) && (i < parts.length); i++) {
459 Part part = parts[i];
460 ArrayList attribsForThisPart = new ArrayList();
461 ArrayList relatedMultiesForThisPart = new ArrayList();
462
463 if (part.isOwnedAttribute()) {
464 filterAttribs(allAttribs, part, attribsForThisPart);
465 } else {
466 filterMultiRelations(allRelations, part, relatedMultiesForThisPart);
467 }
468
469 partList.add(getBlock(node,
470 request,
471 part,
472 canEdit,
473 attribsForThisPart,
474 relatedMultiesForThisPart));
475
476
477 allAttribs.removeAll(attribsForThisPart);
478 allRelations.removeAll(relatedMultiesForThisPart);
479 }
480
481
482 deleteMultiRelations(allRelations, node);
483 deleteAttributes(allAttribs, node);
484
485 response.add(partList);
486 }
487
488 /***
489 * remove relations of type "I am a part of" because these
490 * are reflexive relations, not intended as true parts
491 */
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507 /***
508 * remove relations of type "I am a part of" because these
509 * are reflexive relations, not intended as true parts
510 */
511 public static List filterIamPartOfJoinedRelations(List allRelations) throws DBException {
512 ArrayList iAmPartOf = new ArrayList();
513 for (Iterator iterator = allRelations.iterator(); iterator.hasNext();) {
514 JoinedDataObject oneJoinedRecord = (JoinedDataObject) iterator.next();
515 String relation = oneJoinedRecord.getDataField("Relation." + Relation.RELATION_TYPE).asString();
516 if (RelationType.DEST_CONTAINS_SRC.equals(relation)) {
517 iAmPartOf.add(oneJoinedRecord);
518 }
519 }
520
521 allRelations.removeAll(iAmPartOf);
522
523 return iAmPartOf;
524 }
525
526 private void filterMultiRelations(List allRelations, Part part, List relatedMultiesForThisPart) throws DBException {
527 for (Iterator iterator = allRelations.iterator(); iterator.hasNext();) {
528 MultiDBObject multiDBObject = (MultiDBObject) iterator.next();
529 String nodetype = multiDBObject.getField(Node.NODE_JOIN, Node.NODE_TYPE);
530 String relation = multiDBObject.getField(Node.RELATION_JOIN, Relation.RELATION_TYPE);
531 if (part.getNodeRelation().equals(relation)
532 && part.getPartType().equals(nodetype)
533 ) {
534 relatedMultiesForThisPart.add(multiDBObject);
535 }
536 }
537 }
538
539 public static void filterJoinedDataObjectRelations(List allRelations,
540 Part part,
541 List relatedDataObjectsForThisPart) throws DBException {
542 int relationsSize = allRelations.size();
543 for (int i = 0; i < relationsSize; i++) {
544 JoinedDataObject oneJoinedRecord = (JoinedDataObject) allRelations.get(i);
545 String nodetype = oneJoinedRecord.getDataField("Node." + Node.NODE_TYPE).asString();
546 String relation = oneJoinedRecord.getDataField("Relation." + Relation.RELATION_TYPE).asString();
547 if (part.getNodeRelation().equals(relation)
548 && part.getPartType().equals(nodetype)
549 ) {
550 relatedDataObjectsForThisPart.add(oneJoinedRecord);
551 }
552 }
553
554 }
555
556
557 /***
558 * iterate through list of all attributes and filter those that belong to the specified Part
559 * into the output list
560 *
561 * @param allAttribs input list of Attribute
562 * @param part membership criteria (the part type)
563 * @param attribsForThisPart output list
564 */
565 public static void filterAttribs(List allAttribs, Part part, ArrayList attribsForThisPart) throws DBException {
566 for (Iterator iterator = allAttribs.iterator(); iterator.hasNext();) {
567 Attribute anAttrib = (Attribute) iterator.next();
568 if (part.getPartType().equals(anAttrib.getAttribType())) {
569 attribsForThisPart.add(anAttrib);
570 }
571 }
572 }
573
574 /***
575 * @param attribsLeftOver list of Attribute which should be deleted
576 */
577 public static void deleteAttributes(List attribsLeftOver, Node srcNode) throws DBException {
578 for (Iterator iterator = attribsLeftOver.iterator(); iterator.hasNext();) {
579 Attribute attrib = (Attribute) iterator.next();
580 String msg = "Attribute type is not found in any part of parent node for attribute: "
581 + attrib.forKey() + " which has parentId/parent-title/type: "
582 + attrib.getParentNodeId() + "/" + srcNode.getNodeTitle() + "/" + attrib.getAttribType();
583 if (StringUtil.toBoolean(Setup.getValueUnrequired(DELETE_STRAY_ATTRIBUTES))) {
584 sLog.warn(msg + " so it is being deleted.");
585 attrib.setRequestingUser(SuperUser.INSTANCE);
586 try {
587 attrib.delete();
588 } catch (DBException e) {
589 sLog.error("Cannot delete: ", e);
590 }
591 } else {
592 sLog.warn(msg);
593 }
594 }
595
596 }
597
598 /***
599 * @param relLeftOver list of MultiDBObjects which should have relation elements deleted
600 */
601 private void deleteMultiRelations(List relLeftOver, Node srcNode) throws DBException {
602
603 for (Iterator iterator = relLeftOver.iterator(); iterator.hasNext();) {
604 MultiDBObject aMulti = (MultiDBObject) iterator.next();
605 Relation rel = (Relation) aMulti.getDBObject(Node.RELATION_JOIN);
606
607
608
609 if (rel.isReflexive()) {
610 continue;
611 }
612
613 String msg = "Relation type is not found in any part of src node for relation: "
614 + rel.forKey()
615 + " for src node: " + srcNode.getNodeTitle() + "|" + srcNode.getNodeId();
616 if (StringUtil.toBoolean(Setup.getValueUnrequired(DELETE_STRAY_ATTRIBUTES))) {
617 sLog.warn(msg + " so it is being deleted.");
618 rel.setRequestingUser(SuperUser.INSTANCE);
619 try {
620 rel.delete();
621 } catch (DBException e) {
622 sLog.error("Cannot delete: ", e);
623 }
624 } else {
625 sLog.warn(msg);
626 }
627
628 }
629 }
630
631 /***
632 * @param relLeftOver list of JoinedDataObject which should have relation elements deleted
633 */
634 public static void deleteJoinedRelations(List relLeftOver, Node srcNode) throws DBException {
635 int relationsSize = relLeftOver.size();
636 for (int i = 0; i < relationsSize; i++) {
637 JoinedDataObject aJoin = (JoinedDataObject) relLeftOver.get(i);
638 Relation rel = (Relation) aJoin.getNestedObject("Relation");
639
640 String msg = "Relation type is not found in any part of src node for relation: "
641 + rel.forKey()
642 + " for src node: " + srcNode.getNodeTitle() + "|" + srcNode.getNodeId();
643 if (StringUtil.toBoolean(Setup.getValueUnrequired(DELETE_STRAY_ATTRIBUTES))) {
644 sLog.warn(msg + " so it is being deleted.");
645 rel.setRequestingUser(SuperUser.INSTANCE);
646 try {
647 rel.delete();
648 } catch (DBException e) {
649 sLog.error("Cannot delete: ", e);
650 }
651 } else {
652 sLog.warn(msg);
653 }
654 }
655 }
656
657 private void addViewXMLLink(final String nodeId, final ExpressoResponse response) throws ControllerException {
658 Transition trans = new Transition(PROMPT_EXPORT_NODE, this);
659 trans.addParam(Node.NODE_ID, nodeId);
660 response.add(trans);
661 }
662
663 private void addContainedByLinks(final String nodeId,
664 final ExpressoResponse response) throws DBException, ControllerException {
665 Relation relation = new Relation(RequestRegistry.getUser());
666 relation.setSrcId(nodeId);
667 relation.setRelationTypeName(RelationType.DEST_CONTAINS_SRC);
668
669 List containerRelations = relation.searchAndRetrieveList();
670
671 if (containerRelations.size() > 0) {
672 Block block = new Block(ROW_BLOCK);
673 response.add(block);
674
675
676 ArrayList containers = new ArrayList(containerRelations.size());
677
678 for (Iterator iterator = containerRelations.iterator();
679 iterator.hasNext();) {
680 Relation rel = (Relation) iterator.next();
681 containers.add(rel.getDestNode());
682 }
683
684
685 Collections.sort(containers);
686
687 for (Iterator iter = containers.iterator(); iter.hasNext();) {
688 Node anode = (Node) iter.next();
689 Block row = new Block(ROW);
690 block.add(row);
691
692 Transition trans = new Transition(anode.getNodeTitle(),
693 getClass(), VIEW_NODE);
694 trans.addParam(Node.NODE_ID, anode.getNodeId());
695 row.add(trans);
696 row.setName(NodeType.getDisplayName(anode.getNodeType()));
697 row.add(new Output(Node.NODE_ID, anode.getNodeId()));
698 }
699 }
700 }
701
702 private void addDeleteLink(final String nodeId, final ExpressoResponse response) throws ControllerException {
703 Transition deleteTransition = new Transition("Delete",
704 NodeAction.class, NodeAction.PROMPT_DELETE_NODE);
705 deleteTransition.addParam(Node.NODE_ID, nodeId);
706 response.add(deleteTransition);
707 }
708
709 /***
710 * Adds the clone link. Makes no sense in tree-view of a node. Not
711 * available in embedded mode.
712 *
713 * @param nodeId String
714 * @param response ExpressoResponse
715 * @throws ControllerException
716 */
717 private void addCloneLink(final String nodeId, final ExpressoResponse response) throws ControllerException {
718 if (!isEmbeddedMode(response.getExpressoRequest())) {
719 Transition trans = new Transition("Duplicate", NodeAction.class,
720 NodeAction.PROMPT_CLONE_NODE);
721 trans.addParam(Node.NODE_ID, nodeId);
722 response.add(trans);
723 }
724 }
725
726 /***
727 * Adds the edit link for a paritcular node.
728 *
729 * @param nodeId String
730 * @param response ExpressoResponse
731 * @throws ControllerException
732 */
733 private void addEditLink(final String nodeId, final ExpressoResponse response) throws ControllerException {
734 Transition editTrans = new Transition("", getClass(),
735 PROMPT_EDIT_NODE);
736 editTrans.addParam(Node.NODE_ID, nodeId);
737 if (isEmbeddedMode(response.getExpressoRequest())) {
738 addEmbeddedParameter(editTrans);
739 }
740 response.add(editTrans);
741 }
742
743 /***
744 * prompt for NEW node or updates to node info--not attributes or shared relations,
745 * but the few fields that the node owns: title, annotation, comment.
746 * <p/>
747 * can be for a new node, or existing
748 *
749 * @param request the <code>ExpressoRequest</code> object.
750 * @param response the <code>ExpressoResponse</code> object.
751 * @throws ControllerException upon error.
752 */
753 protected void runPromptEditNodeState(final ExpressoRequest request,
754 final ExpressoResponse response) throws ControllerException {
755
756
757
758 String type = request.getParameter(Node.NODE_TYPE);
759 String nodeId = request.getParameter(Node.NODE_ID);
760
761 if ((type == null) && (nodeId == null)) {
762 throw new ControllerException("either node ID or node type is requried.");
763 }
764
765
766 String targetRelation = request.getParameter(NodeAction.RELATION_TARGET);
767 String relation = request.getParameter(Relation.RELATION_TYPE);
768
769 if (targetRelation != null) {
770 Input target = new Input(NodeAction.RELATION_TARGET);
771 target.setType(InputTag.TYPE_HIDDEN);
772 target.setDefaultValue(targetRelation);
773 response.add(target);
774
775 Input rel = new Input(Relation.RELATION_TYPE);
776 rel.setType(InputTag.TYPE_HIDDEN);
777 rel.setDefaultValue(relation);
778 response.add(rel);
779 }
780
781 Transition errorTrans = NodeAction.getListTransition(null);
782 if (isEmbeddedMode(request)) {
783 addEmbeddedParameter(errorTrans);
784 }
785
786
787 String titleStr = "";
788 String summaryStr = "";
789 String commentStr = "";
790 Node existing = null;
791
792 boolean isExisting = nodeId != null;
793
794 try {
795 existing = new Node(request);
796
797 if (isExisting) {
798 /***
799 * will throw if not found;
800 * programming error if provided nodeId is not found...
801 * hmm, what if someone deleted node in the meanwhile?
802 * still an edge case--we don't want to create a new one if user
803 * thinks that they are editing old one.
804 */
805 existing.setField(Node.NODE_ID, nodeId);
806 existing.retrieve();
807 type = existing.getNodeType();
808
809 Input hiddenId = new Input(Node.NODE_ID);
810 hiddenId.setType(InputTag.TYPE_HIDDEN);
811 hiddenId.setDefaultValue(nodeId);
812 response.add(hiddenId);
813 } else {
814
815 if (!NodeType.isValidType(type)) {
816 throw new ControllerException("invalid node type: " + type);
817 }
818
819
820 if (!isValidAndPopulated(existing, new String[0], null,
821 request, response)) {
822 try {
823 errorTrans.addParam(Node.NODE_TYPE, type);
824 errorTrans.executeTransition(request, response);
825 } catch (Exception e) {
826
827 throw new ControllerException(e);
828 }
829 }
830 }
831
832 titleStr = existing.getNodeTitle();
833
834
835
836 Filter old = existing.setFilterClass(new RawFilter());
837 summaryStr = existing.getNodeAnnotation();
838 commentStr = existing.getNodeComment();
839
840
841 existing.setFilterClass(old);
842
843 Input title = new Input(Node.NODE_TITLE);
844 response.add(title);
845 title.setMaxLength(255);
846 title.setType(Input.ATTRIBUTE_TEXTLINE);
847 title.setDefaultValue(titleStr);
848 title.setDisplayLength(40);
849
850 Input hiddenType = new Input(Node.NODE_TYPE);
851 hiddenType.setType(InputTag.TYPE_HIDDEN);
852 hiddenType.setDefaultValue(type);
853 response.add(hiddenType);
854
855 response.add(new Output(NodeType.DISPLAY_TITLE,
856 NodeType.getDisplayName(type)));
857
858 Input message = getTextArea(Node.NODE_ANNOTATION, summaryStr);
859 response.add(message);
860
861 Input comment = getTextArea(Node.NODE_COMMENT, commentStr);
862 response.add(comment);
863
864
865 Transition submitTransition = new Transition(DO_EDIT_NODE, this);
866 if (isEmbeddedMode(request)) {
867 addEmbeddedParameter(submitTransition);
868 addReturnToSenderParameter(request, response, submitTransition);
869 }
870 submitTransition.setLabel("Save");
871 response.add(submitTransition);
872 } catch (DBException dbe) {
873 response.addError("error: " + dbe.getMessage());
874 getLogger().error("error: ", dbe);
875 response.setFormCache();
876
877 try {
878 errorTrans.executeTransition(request, response);
879
880 return;
881 } catch (Exception e) {
882 throw new ControllerException(e);
883 }
884 }
885 }
886
887 /***
888 * Write changes in attribute to DB.
889 * note that an erased attribute means that we should *delete* the item from the DB
890 *
891 * @param request the <code>ExpressoRequest</code> object.
892 * @param setNum the set number.
893 * @throws ControllerException upon error.
894 */
895 protected void saveAttribute(final ExpressoRequest request, final int setNum)
896 throws ControllerException {
897 try {
898 String id = request.getParameter(getIdParamName(setNum));
899 String value = request.getParameter(getValueParamName(setNum));
900 String comment = request.getParameter(getCommentParamName(setNum));
901
902
903
904 String nodeId = request.getParameter(Node.NODE_ID);
905 String attribType = request.getParameter(Attribute.ATTRIBUTE_TYPE);
906
907 boolean isNew = (id == null) || (id.length() == 0) ||
908 id.equals(Attribute.ATTRIBUTE_ID_UNKNOWN);
909 Attribute attribute = new Attribute(RequestRegistry.getUser());
910
911 if (!isNew) {
912 attribute.setDBName(request.getDataContext());
913 attribute.setField(Attribute.ATTRIBUTE_ID, id);
914
915 boolean found = attribute.find();
916 boolean isSame = found &&
917 value.equals(attribute.getAttribValue()) &&
918 comment.equals(attribute.getAttribComment());
919 boolean shouldDelete = found && !isSame &&
920 (value.length() == 0) && (comment.length() == 0);
921
922 if (isSame) {
923
924 return;
925 }
926
927
928 if (shouldDelete) {
929 attribute.delete();
930
931 return;
932 }
933
934 if (found) {
935 if (value != null) {
936 value = value.trim();
937 }
938 if (comment != null) {
939 comment = comment.trim();
940 }
941 attribute.setField(Attribute.ATTRIBUTE_VALUE, value);
942 attribute.setField(Attribute.ATTRIBUTE_COMMENT, comment);
943 attribute.update();
944
945 return;
946 }
947 }
948
949
950
951 if ((value.length() == 0) && (comment.length() == 0)) {
952 return;
953 }
954
955
956 if ((nodeId == null) || (nodeId.length() == 0) ||
957 (attribType == null) || (attribType.length() == 0)) {
958 throw new ControllerException("nodeId and attrib_type are required parameters");
959 }
960
961
962 Node node = new Node(request, nodeId);
963
964 if (!node.find()) {
965 throw new ControllerException("cannot find node with id: " +
966 nodeId);
967 }
968
969 attribute.setAttributeType(attribType);
970 attribute.setParentNodeId(nodeId);
971 attribute.setParentNodeType(node.getNodeType());
972
973 if (value != null) {
974 value = value.trim();
975 }
976 if (comment != null) {
977 comment = comment.trim();
978 }
979 attribute.setAttributeValue(value);
980 attribute.setAttributeComment(comment);
981 attribute.add();
982 } catch (DBException dbe) {
983 throw new ControllerException(dbe);
984 }
985 }
986
987 protected Block getBlock(final Node node,
988 final ExpressoRequest request, final Part part,
989 boolean canEdit,
990 final List attribs, final List relatedNodes) throws DBException, ControllerException {
991 Block block = null;
992
993 if (part.isOwnedAttribute()) {
994 block = getAttributeOutputBlock(node, request, part,
995 canEdit, attribs);
996 } else {
997 block = getSharedNodeBlock(node, request, part,
998 canEdit, relatedNodes);
999 }
1000
1001
1002 block.add(getHelpUrl(part.getPartType()));
1003
1004 return block;
1005 }
1006
1007 ;
1008
1009 /***
1010 * Return an attributes of this node, of this type.
1011 *
1012 * @param parentNodeId the id of the parent in the tree.
1013 * @param request the <code>ExpressoRequest</code> object.
1014 * @param attribType the attribute type.
1015 * @return ArrayList of attributes
1016 * @throws ControllerException upon error.
1017 */
1018 protected ArrayList getAttributes(final String parentNodeId,
1019 final ExpressoRequest request,
1020 final String attribType)
1021 throws ControllerException {
1022 try {
1023 Attribute attribute = new Attribute(RequestRegistry.getUser());
1024 attribute.setDBName(request.getDataContext());
1025 attribute.setField(Attribute.NODE_ID, parentNodeId);
1026 attribute.setField(Attribute.ATTRIBUTE_TYPE, attribType);
1027
1028 return attribute.searchAndRetrieveList(Attribute.ATTRIBUTE_ORDER);
1029 } catch (DBException e) {
1030 throw new ControllerException(e);
1031 }
1032 }
1033
1034 /***
1035 * Utility to create an output block for a given attribute part.
1036 *
1037 * @param node the parent node
1038 * @param request the <code>ExpressoRequest</code> object.
1039 * @param part the <code>Part</code> to get the attributes from.
1040 * @param canEdit true if you want a transition link
1041 * to the edit controller
1042 * @param attribs A list of attributes
1043 * @return Populated Block.
1044 * @throws DBException upon database related error.
1045 * @throws ControllerException upon Expresso controller related error.
1046 */
1047 protected Block getAttributeOutputBlock(final Node node,
1048 final ExpressoRequest request, final Part part,
1049 boolean canEdit,
1050 final List attribs) throws DBException,
1051 ControllerException {
1052 if (!part.isOwnedAttribute()) {
1053 throw new DBException("cannot handle shared node here");
1054 }
1055
1056 Block typeBlock = new Block("part" + part.getPartNum());
1057 typeBlock.add(new Output(Part.PART_DISPLAY_NAME, part.getPartLabel()));
1058
1059 /***
1060 * add ATTRIBUTE_TYPE item to mark this block as an attribute
1061 */
1062 typeBlock.add(new Output(Attribute.ATTRIBUTE_TYPE, part.getPartType()));
1063 typeBlock.add(new Output(Part.PART_HELP_STRING, part.getPartType()));
1064
1065
1066 Attribute repAttrib = NodeAction.getRepAttribute(attribs, node, part.getPartType());
1067
1068 if (repAttrib.hasCustomHandler()) {
1069 Transition trans = repAttrib.getViewTrans(request.getAllParameters());
1070 if (trans != null) {
1071 Block rowBlock = new Block(ROW_BLOCK);
1072 typeBlock.add(rowBlock);
1073 rowBlock.add(trans);
1074 rowBlock.add(repAttrib.getViewComment(request.getAllParameters()));
1075 if (!repAttrib.getAttribCreated().equals(repAttrib.getAttribModified())) {
1076 rowBlock.add(new Output(Attribute.ATTRIBUTE_MODIFIED, repAttrib.getAttribModified().substring(0, 10)));
1077 }
1078 }
1079 } else {
1080 int numItems = 0;
1081 numItems = attribs.size();
1082
1083
1084
1085
1086 if (numItems > 0) {
1087 int i = 0;
1088 for (Iterator iterator = attribs.iterator(); iterator.hasNext();) {
1089 Attribute attribute = (Attribute) iterator.next();
1090
1091
1092 if (part.isSingleValued() && i >= 1) {
1093 break;
1094 }
1095
1096
1097 Block rowBlock = new Block(ROW_BLOCK);
1098 typeBlock.add(rowBlock);
1099
1100 try {
1101 String value = attribute.getAttribValue();
1102
1103 if (part.hasPicklist()) {
1104
1105
1106 value = attribute.getField(Attribute.ATTRIBUTE_PICKLIST_DISPLAY);
1107 }
1108
1109 rowBlock.add(new Output(Attribute.ATTRIBUTE_VALUE,
1110 str(value)));
1111 rowBlock.add(new Output(Attribute.ATTRIBUTE_COMMENT,
1112 str(attribute.getAttribComment())));
1113
1114 Transition trans = new Transition(NodeAction.
1115 PROMPT_EDIT_ATTRIB +
1116 attribute.getAttribId(), "", NodeAction.class,
1117 NodeAction.PROMPT_EDIT_ATTRIB);
1118 trans.addParam(Attribute.ATTRIBUTE_ID,
1119 attribute.getAttribId());
1120 rowBlock.add(trans);
1121 } catch (DBException dbe) {
1122 throw new ControllerException(dbe);
1123 }
1124
1125 i++;
1126 }
1127 }
1128 }
1129
1130 if (canEdit) {
1131 Transition trans = part.getEditTrans(node.getNodeId(), request.getAllParameters());
1132 if (isEmbeddedMode(request)) {
1133 addEmbeddedParameter(trans);
1134 }
1135
1136 typeBlock.add(trans);
1137 }
1138
1139 return typeBlock;
1140 }
1141
1142 private Transition getHelpUrl(final String partType) {
1143 Transition trans = new Transition("", PartAction.class, PartAction.GENERATE_DOCS);
1144 trans.setAttribute("PART_HELP_STRING", partType);
1145 return trans;
1146 }
1147
1148 /***
1149 * utility to create an input block for a given attribute part.
1150 * <p/>
1151 * this method is complicated by fact that we must associate sets of items
1152 * together as inputs;
1153 * we'll get back just a pile of parameters from the HTML page POST,
1154 * so we need a way to prefix param names such that
1155 * they can be grouped together later.
1156 *
1157 * @param nodeId the id of the node.
1158 * @param srcNodeType source the node type.
1159 * @param request The ExpressoRequest object
1160 * @param attribName the name of the attribute to populate
1161 * @return a Block of Inputs for the attributes
1162 * @throws ControllerException upon any sort of error.
1163 */
1164 protected Block getAttributeInputBlock(final String nodeId,
1165 final String srcNodeType,
1166 final ExpressoRequest request,
1167 final String attribName) throws ControllerException {
1168 Block typeBlock = new Block("attributes");
1169 int itemNum = 0;
1170 ArrayList list = getAttributes(nodeId, request, attribName);
1171 Part part = null;
1172 boolean isMultiple = false;
1173
1174 try {
1175 part = PartsFactory.getAttribPart(srcNodeType, attribName);
1176 isMultiple = part.areMultipleAttributesAllowed();
1177
1178 if (part.hasPicklist()) {
1179 Block rowBlock = new Block(ROW_BLOCK);
1180 typeBlock.add(rowBlock);
1181
1182 String selected = "";
1183 String comment = "";
1184
1185 if (list.size() > 0) {
1186
1187 Attribute attribute = (Attribute) list.get(0);
1188 selected = attribute.getAttribValue();
1189
1190 if (selected == null) {
1191 selected = "";
1192 }
1193
1194 comment = attribute.getAttribComment();
1195
1196 if (comment == null) {
1197 comment = "";
1198 }
1199
1200
1201 Input hiddenId = new Input(getIdParamName(itemNum));
1202 rowBlock.add(hiddenId);
1203 hiddenId.setType(InputTag.TYPE_HIDDEN);
1204 hiddenId.setDefaultValue(attribute.getAttribId());
1205 }
1206
1207
1208 addPicklistDropDown(nodeId, part, request, rowBlock, selected);
1209
1210 Input commentInput = getTextArea(getCommentParamName(itemNum),
1211 comment,
1212 part.numDisplayLines(),
1213 TEXTAREA_NUM_COLS);
1214 rowBlock.add(commentInput);
1215 } else {
1216
1217
1218
1219
1220
1221
1222 for (Iterator iter = list.iterator(); iter.hasNext();) {
1223 Attribute attribute = (Attribute) iter.next();
1224 String attribId = attribute.getAttribId();
1225
1226
1227 Block rowBlock = new Block(ROW_BLOCK);
1228 typeBlock.add(rowBlock);
1229
1230 Input valueInput = new Input(this
1231 .getValueParamName(itemNum));
1232 rowBlock.add(valueInput);
1233
1234 if (!part.hasPicklist()) {
1235 valueInput.setMaxLength(4000);
1236 valueInput.setType(InputTag.TYPE_TEXTAREA);
1237 valueInput.setLines(part.numDisplayLines());
1238 valueInput.setDisplayLength(TEXTAREA_NUM_COLS);
1239 valueInput.setDefaultValue(attribute.getAttribValue());
1240 }
1241
1242 Input commentInput = getTextArea(getCommentParamName(itemNum),
1243 attribute.getAttribComment(),
1244 part.numDisplayLines(),
1245 TEXTAREA_NUM_COLS);
1246 rowBlock.add(commentInput);
1247
1248
1249 Input hiddenId = new Input(getIdParamName(itemNum));
1250 rowBlock.add(hiddenId);
1251 hiddenId.setType(InputTag.TYPE_HIDDEN);
1252 hiddenId.setDefaultValue(attribId);
1253
1254 itemNum++;
1255 }
1256
1257
1258
1259
1260
1261 int num_empty_attribs = 5;
1262
1263 if (!isMultiple) {
1264 if (itemNum == 0) {
1265 num_empty_attribs = 1;
1266 } else {
1267 num_empty_attribs = 0;
1268 }
1269 }
1270
1271 for (int i = itemNum; i < (itemNum + num_empty_attribs); i++) {
1272
1273 Block rowBlock = new Block(ROW_BLOCK);
1274 typeBlock.add(rowBlock);
1275
1276 Input valueInput = getTextArea(getValueParamName(i),
1277 null, part.numDisplayLines(),
1278 TEXTAREA_NUM_COLS);
1279 rowBlock.add(valueInput);
1280
1281 Input commentInput = getTextArea(getCommentParamName(i),
1282 null, part.numDisplayLines(),
1283 TEXTAREA_NUM_COLS);
1284 rowBlock.add(commentInput);
1285 }
1286 }
1287 } catch (Exception dbe) {
1288 throw new ControllerException(dbe);
1289 }
1290
1291 return typeBlock;
1292 }
1293
1294 /***
1295 * Utility to create an output block for a given shared node part.
1296 *
1297 * @param node the source node
1298 * @param request ExpressoRequest The <code>ExpressoRequest</code>
1299 * object
1300 * @param part Part the part we're querying.
1301 * @param canEdit boolean true if the user can edit the node
1302 * @param relatedNodes list of multidbobjects from node.getRawRelated() of related node info.
1303 * @return Block Bloc populated with the appropriate inputs/outputs, etc
1304 * @throws DBException upon database access error
1305 * @throws ControllerException upon Controller related error.
1306 */
1307 protected Block getSharedNodeBlock(final Node node,
1308 final ExpressoRequest request, final Part part,
1309 final boolean canEdit,
1310 final List relatedNodes) throws DBException, ControllerException {
1311 if (part.isOwnedAttribute()) {
1312 throw new DBException("cannot handle attribute node here");
1313 }
1314 String relTarget = request.getParameter(NodeAction.RELATION_TARGET);
1315
1316 Block typeBlock = new Block("part" + part.getPartNum());
1317 typeBlock.add(new Output(Part.PART_DISPLAY_NAME, part.getPartLabel()));
1318
1319
1320
1321 typeBlock.add(new Output(Relation.RELATION_TYPE, part.getNodeRelation()));
1322
1323
1324
1325 if (node.getNodeType().equals(part.getPartType())) {
1326 typeBlock.add(new Output(Part.PART_HELP_STRING, part.getNodeRelation()));
1327 } else {
1328 typeBlock.add(new Output(Part.PART_HELP_STRING, part.getPartType()));
1329 }
1330
1331 try {
1332
1333
1334
1335
1336 for (Iterator iterator = relatedNodes.iterator(); iterator.hasNext();) {
1337 MultiDBObject multiDBObject = (MultiDBObject) iterator.next();
1338 Node relatedNode = (Node) multiDBObject.getDBObject(Node.NODE_JOIN);
1339
1340
1341 Block rowBlock = new Block(ROW_BLOCK);
1342 typeBlock.add(rowBlock);
1343
1344 try {
1345
1346 rowBlock.add(new Output(Node.NODE_ANNOTATION,
1347 str(StringUtil.truncate(relatedNode.getNodeAnnotation(),
1348 MAX_CHARS_OUTPUT))));
1349 rowBlock.add(new Output(Relation.RELATION_ANNOTATION,
1350 str(StringUtil.truncate("", MAX_CHARS_OUTPUT))));
1351 rowBlock.add(new Output(Node.NODE_COMMENT,
1352 str(StringUtil.truncate(relatedNode.getNodeComment(),
1353 MAX_CHARS_OUTPUT))));
1354
1355 if (relatedNode.getNodeId().equals(relTarget)) {
1356 rowBlock.add(new Output(NodeAction.RELATION_TARGET, "1"));
1357 }
1358 Transition trans = AddNodeAction.getViewTrans(relatedNode.
1359 getNodeId());
1360 trans.setLabel(relatedNode.getNodeTitle());
1361 rowBlock.add(trans);
1362 } catch (DBException dbe) {
1363 throw new ControllerException(dbe);
1364 }
1365 }
1366 } catch (DBException e) {
1367 throw new ControllerException(e);
1368 }
1369
1370 if (canEdit) {
1371 Transition associateTrans = part.getEditTrans(node.getNodeId(), request.getAllParameters());
1372 associateTrans.setLabel("Edit");
1373 if (isEmbeddedMode(request)) {
1374 addEmbeddedParameter(associateTrans);
1375 }
1376 typeBlock.add(associateTrans);
1377 }
1378
1379 return typeBlock;
1380 }
1381
1382 /***
1383 * Add number suffix from to Attribute.ATTRIBUTE_COMMENT_PREFIX.
1384 *
1385 * @param setNum The number suffix.
1386 * @return java.lang.String
1387 */
1388 private String getCommentParamName(final int setNum) {
1389 return Attribute.ATTRIBUTE_COMMENT_PREFIX + setNum;
1390 }
1391
1392 /***
1393 * Add number suffix from to Attribute.ATTRIBUTE_ID_PREFIX.
1394 *
1395 * @param setNum the number suffix.
1396 * @return java.lang.String
1397 */
1398 private String getIdParamName(final int setNum) {
1399 return Attribute.ATTRIBUTE_ID_PREFIX + setNum;
1400 }
1401
1402 /***
1403 * Add number suffix from to Attribute.ATTRIBUTE_VALUE_PREFIX.
1404 *
1405 * @param setNum the number suffix.
1406 * @return java.lang.String
1407 */
1408 private String getValueParamName(final int setNum) {
1409 return Attribute.ATTRIBUTE_VALUE_PREFIX + setNum;
1410 }
1411
1412 private String getValueParamName(final String setNum) {
1413
1414 return Attribute.ATTRIBUTE_VALUE_PREFIX + setNum;
1415 }
1416
1417 /***
1418 * handle updates to node info--not attributes or shared relations,
1419 * but the few fields that the node owns: title, annotation, comment
1420 *
1421 * @param request The ServletExpressoRequest object.
1422 * @param response The ExpressoResponse object.
1423 * @throws ControllerException upon error.
1424 */
1425 protected void runDoEditNodeState(final ExpressoRequest request,
1426 final ExpressoResponse response) throws ControllerException {
1427 try {
1428 /***
1429 * @todo auto-inc fields are not handled by isValidAndPopulated :-(
1430 */
1431 String nodeId = request.getParameter(Node.NODE_ID);
1432 boolean isExisting = nodeId != null;
1433 Node existing = null;
1434 String oldtitle = "";
1435
1436 if (isExisting) {
1437 existing = new Node(request, nodeId);
1438
1439 /***
1440 * will throw if not found;
1441 * programming error if provided nodeId is not found...
1442 * hmm, what if someone deleted node in the meanwhile?
1443 * still an edge case--we don't want to create a new one if user
1444 * thinks that they are editing old one.
1445 */
1446 try {
1447 existing.retrieve();
1448 oldtitle = existing.getNodeTitleRaw();
1449 } catch (DBException dbe) {
1450 response.addError("error: " + dbe.getMessage());
1451 getLogger().error("error: ", dbe);
1452 response.setFormCache();
1453 transition(PROMPT_EDIT_NODE, request, response);
1454
1455 return;
1456 }
1457 }
1458
1459 Node uploaded = new Node(RequestRegistry.getUser());
1460 String[] required = {Node.NODE_TYPE, Node.NODE_TITLE};
1461
1462 if (!isValidAndPopulated(uploaded, required, PROMPT_EDIT_NODE,
1463 request, response)) {
1464 return;
1465
1466 }
1467
1468 String title = uploaded.getNodeTitle();
1469
1470
1471 if (!(isExisting && title.equals(oldtitle))) {
1472
1473 Node checkName = new Node(RequestRegistry.getUser());
1474 checkName.setNodeTitle(title);
1475
1476 if (checkName.find()) {
1477 response.addError("Title is already in use; please choose another.");
1478 response.setFormCache();
1479 transition(PROMPT_EDIT_NODE, request, response);
1480
1481 return;
1482 }
1483 }
1484
1485 String targetId = request.getParameter(NodeAction.RELATION_TARGET);
1486 if (!isExisting) {
1487
1488 uploaded.setDBName(RequestRegistry.getDataContext());
1489 uploaded.setNodeOwner(RequestRegistry.getUser().getLoginName());
1490
1491 try {
1492 uploaded.add();
1493 nodeId = uploaded.getNodeId();
1494
1495
1496
1497 if (targetId != null) {
1498 Node target = new Node(targetId);
1499 target.retrieve();
1500 String relationType = request.getParameter(Relation.RELATION_TYPE);
1501
1502 Relation relation = new Relation(RequestRegistry.getUser());
1503 relation.setField(Relation.RELATION_SRC, targetId);
1504 relation.setField(Relation.RELATION_DEST, nodeId);
1505 relation.setField(Relation.RELATION_TYPE, relationType);
1506 relation.add();
1507 }
1508 } catch (DBException dbe) {
1509 response.addError("error: " + dbe.getMessage());
1510 getLogger().error("error: ", dbe);
1511 response.setFormCache();
1512 transition(PROMPT_EDIT_NODE, request, response);
1513
1514 return;
1515 }
1516 } else {
1517
1518 boolean isChanged = false;
1519
1520 String annotation = uploaded.getNodeAnnotation();
1521 String comment = uploaded.getNodeComment();
1522
1523
1524 if (oldtitle.equals(title) &&
1525 existing.getNodeAnnotation().equals(annotation) &&
1526 existing.getNodeComment().equals(comment)) {
1527 isChanged = false;
1528 } else {
1529 isChanged = true;
1530 existing.setNodeTitle(title);
1531 existing.setNodeAnnotation(annotation);
1532 existing.setNodeComment(comment);
1533 existing.setNodeOwner(RequestRegistry.getUser().getLoginName());
1534 }
1535
1536 if (isChanged) {
1537 existing.update();
1538 }
1539 }
1540
1541
1542 if (targetId != null) {
1543
1544
1545 Transition trans = getViewTrans(targetId);
1546 trans.addParam(NodeAction.RELATION_TARGET, nodeId);
1547 if (this.redirectToSender((ServletControllerRequest) request, "Changes Saved")) {
1548 return;
1549 }
1550 trans.redirectTransition(request, response);
1551 } else {
1552
1553
1554 Transition trans = new Transition(VIEW_NODE, this);
1555 trans.addParam(Node.NODE_ID, nodeId);
1556
1557 if (this.redirectToSender((ServletControllerRequest) request, "Changes Saved")) {
1558 return;
1559 }
1560
1561 trans.redirectTransition(request, response);
1562 }
1563 } catch (Exception dbe) {
1564 throw new ControllerException(dbe);
1565 }
1566 }
1567
1568 private void addPicklistDropDown(final String parent_id, final Part part,
1569 final ExpressoRequest request, Block row, String selected) throws DBException {
1570
1571 Input valueInput = new Input(getValueParamName(parent_id));
1572 valueInput.setType(InputTag.TYPE_DROPDOWN);
1573 row.add(valueInput);
1574
1575 String[][] picklistArray = null;
1576
1577 try {
1578 picklistArray = part.getPicklistArray(request);
1579 } catch (Exception e) {
1580 throw new DBException(e);
1581 }
1582
1583 boolean foundSelected = false;
1584
1585
1586
1587 String defaultSelected = picklistArray[0][0];
1588
1589 for (int i = 0; i < picklistArray.length; i++) {
1590 valueInput.addValidValue(picklistArray[i][0], picklistArray[i][1]);
1591
1592 if (selected.equals(picklistArray[i][0])) {
1593 foundSelected = true;
1594 valueInput.setDefaultValue(selected);
1595 }
1596
1597
1598
1599 if (PickList.NOT_SPECIFIED_DISPLAY.equals(picklistArray[i][0])) {
1600 defaultSelected = PickList.NOT_SPECIFIED_DISPLAY;
1601 }
1602 }
1603
1604
1605 if (!foundSelected) {
1606 valueInput.setDefaultValue(defaultSelected);
1607 }
1608
1609
1610 List picklist = null;
1611
1612 try {
1613 picklist = part.getPicklist(request);
1614 } catch (Exception e) {
1615 throw new DBException(e);
1616 }
1617
1618
1619
1620 if ((picklist.size() > 0) &&
1621 ((PickList) picklist.get(0)).canRequesterWrite()) {
1622 Transition promptEditPicklist = new Transition(PicklistAction.PROMPT_LIST,
1623 "", PicklistAction.class, PicklistAction.PROMPT_LIST);
1624 promptEditPicklist.addParam(PickList.NODE_TYPE, part.getParentType());
1625 promptEditPicklist.addParam(PickList.ATTRIBUTE_TYPE, part.getPartType());
1626 promptEditPicklist.addParam(Node.NODE_ID, parent_id);
1627 if (isEmbeddedMode(request)) {
1628 addEmbeddedParameter(promptEditPicklist);
1629 }
1630 row.add(promptEditPicklist);
1631 }
1632 }
1633
1634 /***
1635 * Prompt import of node from xml.
1636 *
1637 * @param request The ExpressoRequest object.
1638 * @param response The ExpressoResponse object.
1639 * @throws ControllerException upon error.
1640 */
1641 protected void runPromptImportNodeState(final ExpressoRequest request,
1642 final ExpressoResponse response) throws ControllerException {
1643 try {
1644 Input in = getTextArea("xml", response.getFormCache("xml"), 20, 80);
1645 response.add(in);
1646
1647 in = new Input(Node.RELATION_JOIN);
1648 in.setType(Input.ATTRIBUTE_CHECKBOX);
1649 in.addValidValue(Node.RELATION_JOIN,
1650 "Ignore external relation IDs (relations not satisfied"
1651 + " within XML itself are ignored)");
1652
1653 String def = response.getFormCache(Node.RELATION_JOIN);
1654
1655 if (def.length() == 0) {
1656 def = Node.RELATION_JOIN;
1657 }
1658
1659 in.setDefaultValue(def);
1660 in.setAttribute(Input.SELECTED, "true");
1661 response.add(in);
1662
1663 Transition doImport = new Transition("Import", getClass(),
1664 DO_IMPORT_NODE);
1665 response.add(doImport);
1666 } catch (Exception dbe) {
1667 throw new ControllerException(dbe);
1668 }
1669 }
1670
1671 /***
1672 * Prompt import of node from xml.
1673 * assumes that all reference IDs ('ident') are for Nodes internal
1674 * to the given tree
1675 *
1676 * @param request The ExpressoRequest object.
1677 * @param response The ExpressoResponse object.
1678 * @throws ControllerException upon error.
1679 */
1680 protected void runDoImportNodeState(final ExpressoRequest request, final ExpressoResponse response) throws
1681 ControllerException {
1682 try {
1683 String xml = request.getParameter("xml");
1684 Element root = PartAction.parseXML(new StringReader(xml));
1685
1686
1687 boolean isFindAllIds = StringUtil.toBoolean(request.getParameter(Node.RELATION_JOIN));
1688 boolean isExternalRefRequired =
1689 request.getParameter(Node.RELATION_JOIN) != null;
1690
1691
1692 ModelXMLReader reader = new ModelXMLReader();
1693
1694 Map allNodesByXML_ID = reader.parseImportXmlAndCreateNodes(isFindAllIds, isExternalRefRequired, root,
1695 request, "//");
1696
1697
1698 Block imports = new Block(ROW_BLOCK);
1699 Block delblock = new Block(ROW);
1700 response.add(imports);
1701 response.add(delblock);
1702
1703 List allnodes = new ArrayList(allNodesByXML_ID.values());
1704 Collections.sort(allnodes, new IndentComparator());
1705
1706 for (Iterator iterator = allnodes.iterator();
1707 iterator.hasNext();) {
1708 Node anode = (Node) iterator.next();
1709
1710
1711 Block row = new Block(anode.getAttribute(Node.INDENT)
1712 .toString());
1713 imports.add(row);
1714
1715 Transition trans = getViewTrans(anode.getNodeId());
1716 trans.setLabel(anode.getNodeTitle());
1717 row.add(trans);
1718
1719
1720 row.add(new Output(Node.NODE_TYPE,
1721 anode.getEntity().getDisplayName()));
1722
1723
1724 delblock.add(new Output(NodeAction.DO_DELETE_NODE,
1725 anode.getNodeId()));
1726 }
1727
1728
1729
1730
1731 } catch (SQLException e) {
1732 String msg = StringUtil.notNull(e.getMessage());
1733
1734 if ((msg.indexOf("Violation of unique index") != -1) ||
1735 (msg.indexOf("Duplicate entry") != -1)) {
1736 msg = "Duplicate titles are not allowed: " + msg;
1737 }
1738
1739 response.addError("error: " + msg);
1740 getLogger().error("error: ", e);
1741 response.setFormCache();
1742
1743 Transition prompt = new Transition(PROMPT_IMPORT_NODE, this);
1744 if (isEmbeddedMode(request)) {
1745 addEmbeddedParameter(prompt);
1746 }
1747
1748 prompt.executeTransition(request, response);
1749 } catch (Exception e) {
1750 response.addError("error: " + e.getMessage());
1751 getLogger().error("error: ", e);
1752 response.setFormCache();
1753
1754 Transition prompt = new Transition(PROMPT_IMPORT_NODE, this);
1755 if (isEmbeddedMode(request)) {
1756 addEmbeddedParameter(prompt);
1757 }
1758
1759 try {
1760 prompt.executeTransition(request, response);
1761 } catch (NonHandleableException e1) {
1762 throw new ControllerException(e1);
1763 }
1764 }
1765 }
1766
1767
1768 public static Transition getViewTrans(final String nodeId) {
1769 Transition trans = new Transition("", AddNodeAction.class,
1770 AddNodeAction.VIEW_NODE);
1771 trans.addParam(Node.NODE_ID, nodeId);
1772
1773 return trans;
1774 }
1775
1776 /***
1777 * Increment order of this attribute
1778 *
1779 * @param request The ExpressoRequest object.
1780 * @param response The ExpressoResponse object.
1781 * @throws ControllerException upon error.
1782 * @throws DBException upon database related error.
1783 */
1784 protected void runIncAttribOrderState(final ExpressoRequest request,
1785 final ExpressoResponse response) throws DBException, ControllerException {
1786 Attribute attrib = getAttrib(request);
1787 renumAttribs(attrib, true);
1788 redirectToPromptEditAttrib(attrib, request, response);
1789 }
1790
1791 private Attribute getAttrib(final ExpressoRequest request) throws DBException {
1792 Attribute attrib = new Attribute();
1793 attrib.setAttributeId(request.getParameter(Attribute.ATTRIBUTE_ID));
1794 attrib.retrieve();
1795
1796 return attrib;
1797 }
1798
1799 /***
1800 * increment order of this attribute
1801 *
1802 * @param request The ExpressoRequest object.
1803 * @param response The ExpressoResponse object.
1804 * @throws ControllerException upon error.
1805 * @throws DBException upon database related error.
1806 */
1807 protected void runDecAttribOrderState(final ExpressoRequest request, final ExpressoResponse response) throws
1808 DBException,
1809 ControllerException {
1810 Attribute attrib = getAttrib(request);
1811 renumAttribs(attrib, false);
1812 redirectToPromptEditAttrib(attrib, request, response);
1813 }
1814
1815 private void redirectToPromptEditAttrib(final Attribute attrib,
1816 final ExpressoRequest request,
1817 final ExpressoResponse response) throws DBException,
1818 ControllerException {
1819 Node parent = attrib.getParentNode();
1820 Transition trans = new Transition("", NodeAction.class, NodeAction.PROMPT_EDIT_ATTRIB);
1821 if (isEmbeddedMode(request)) {
1822 addEmbeddedParameter(trans);
1823 }
1824 trans.addParam(Node.NODE_ID, parent.getNodeId());
1825 trans.addParam(Attribute.ATTRIBUTE_TYPE, attrib.getAttribType());
1826 trans.redirectTransition(request, response);
1827 }
1828
1829 /***
1830 * The meat of the renumbering.
1831 *
1832 * @param attrib the attribute to reorder.
1833 * @param isInc true if we are incrementing the attrib order.
1834 * @throws DBException upon database related error.
1835 */
1836 private void renumAttribs(final Attribute attrib, final boolean isInc) throws DBException {
1837 /***
1838 * @todo add test for going below 0 or above N, but for now,
1839 * depend on UI to not provide for those mistakes
1840 */
1841 Attribute[] attribs = attrib.getParentNode().getAttributes(attrib.
1842 getAttribType());
1843 int oldnum = attrib.getOrderInt();
1844
1845 if (isInc) {
1846 attrib.setOrder(oldnum + 1);
1847 attrib.updateOrder();
1848
1849
1850
1851 int index = (oldnum + 1) - 1;
1852
1853 if (attribs[index].getOrderInt() == (oldnum + 1)) {
1854 attribs[index].setOrder(oldnum);
1855 attribs[index].updateOrder();
1856 }
1857 } else {
1858 attrib.setOrder(oldnum - 1);
1859 attrib.updateOrder();
1860
1861
1862 int index = oldnum - 1 - 1;
1863
1864 if (attribs[index].getOrderInt() == (oldnum - 1)) {
1865 attribs[index].setOrder(oldnum);
1866 attribs[index].updateOrder();
1867 }
1868 }
1869 }
1870
1871 /***
1872 * Increment order of this relation.
1873 *
1874 * @param request The ExpressoRequest object.
1875 * @param response The ExpressoResponse object.
1876 * @throws ControllerException upon error.
1877 * @throws DBException upon database related error.
1878 */
1879 protected void runIncRelOrderState(final ExpressoRequest request, final ExpressoResponse response) throws
1880 DBException,
1881 ControllerException {
1882 Relation rel = getRelation(request);
1883 renumRelations(rel, true);
1884 redirectToPicklist(rel, request, response);
1885 }
1886
1887 private void redirectToPicklist(final Relation rel, final ExpressoRequest request,
1888 ExpressoResponse response) throws DBException, ControllerException {
1889 Node srcNode = rel.getSrcNode();
1890 Node destNode = rel.getDestNode();
1891 Transition trans = new Transition("", NodeAction.class, NodeAction.PROMPT_PICKLIST_NODE);
1892 if (isEmbeddedMode(request)) {
1893 addEmbeddedParameter(trans);
1894 }
1895 trans.addParam(Node.NODE_TYPE, destNode.getNodeType());
1896 trans.addParam(Node.NODE_ID, srcNode.getNodeId());
1897 trans.addParam(Relation.RELATION_TYPE, rel.getRelationTypeName());
1898 trans.redirectTransition(request, response);
1899 }
1900
1901 private Relation getRelation(final ExpressoRequest request) throws DBException {
1902 Relation rel = new Relation(RequestRegistry.getUser());
1903 rel.setSrcId(request.getParameter(Relation.RELATION_SRC));
1904 rel.setDestId(request.getParameter(Relation.RELATION_DEST));
1905 rel.setRelationTypeName(request.getParameter(Relation.RELATION_TYPE));
1906 rel.retrieve();
1907
1908 return rel;
1909 }
1910
1911 /***
1912 * Dec order of this relation.
1913 *
1914 * @param request The ExpressoRequest object.
1915 * @param response The ExpressoResponse object.
1916 * @throws ControllerException upon error.
1917 * @throws DBException upon database related error.
1918 */
1919 protected void runDecRelOrderState(final ExpressoRequest request, final ExpressoResponse response) throws
1920 DBException,
1921 ControllerException {
1922 Relation rel = getRelation(request);
1923 renumRelations(rel, false);
1924 redirectToPicklist(rel, request, response);
1925 }
1926
1927 /***
1928 * The mean of the renumbering.
1929 *
1930 * @param rel The relation we're reordering
1931 * @param isInc true if we're incrementing the order.
1932 * @throws DBException upon database related error.
1933 */
1934 private void renumRelations(final Relation rel, final boolean isInc) throws DBException {
1935
1936
1937 Node srcNode = rel.getSrcNode();
1938 Node destNode = rel.getDestNode();
1939 List multiList = srcNode.getRawRelated(rel.getRelationTypeName(),
1940 destNode.getNodeType());
1941 ArrayList rellist = new ArrayList();
1942
1943 for (Iterator iterator = multiList.iterator(); iterator.hasNext();) {
1944 MultiDBObject multi = (MultiDBObject) iterator.next();
1945 rellist.add(multi.getDBObject(Node.RELATION_JOIN));
1946 }
1947
1948 Relation[] rels = (Relation[]) rellist.toArray(new Relation[rellist.size()]);
1949
1950 int oldnum = rel.getOrderInt();
1951
1952 if (isInc) {
1953 rel.setOrder(oldnum + 1);
1954 rel.updateOrder();
1955
1956
1957
1958 int index = (oldnum + 1) - 1;
1959
1960 if (rels[index].getOrderInt() == (oldnum + 1)) {
1961 rels[index].setOrder(oldnum);
1962 rels[index].updateOrder();
1963 }
1964 } else {
1965 rel.setOrder(oldnum - 1);
1966 rel.updateOrder();
1967
1968
1969 int index = oldnum - 1 - 1;
1970
1971 if (rels[index].getOrderInt() == (oldnum - 1)) {
1972 rels[index].setOrder(oldnum);
1973 rels[index].updateOrder();
1974 }
1975 }
1976 }
1977
1978
1979 /***
1980 * Views all attributes or relations of a given part. Useful for when somebody
1981 * does not have permission to update an attribute, but the attribute
1982 * label is truncates (such as in treeview)
1983 *
1984 * @param request ExpressoRequest the ExpressoRequest object.
1985 * @param response ExpressoResponse the ExpressoResponse object.
1986 * @throws ControllerException upon controller framework related error.
1987 * @throws DBException upon database access related error.
1988 */
1989 protected void runViewPartState(final ExpressoRequest request,
1990 final ExpressoResponse response)
1991 throws ControllerException, DBException {
1992 Node node = NodeAction.getNode(request);
1993 Part part = new Part();
1994 part.setPartId(request.getParameter(Part.PART_ID));
1995 part.retrieve();
1996
1997 request.getSession().setAttribute("ReadOnlyRequest", Boolean.TRUE);
1998
1999 try {
2000 response.add(new Output(Attribute.ATTRIBUTE_DISPLAY_NAME, part.getPartLabel()));
2001
2002 Transition viewTrans = AddNodeAction.getViewTrans(node.getNodeId());
2003 viewTrans.setLabel(node.getNodeTitle());
2004 response.add(viewTrans);
2005
2006
2007
2008 Block allparts = new Block("allparts");
2009 response.add(allparts);
2010 if (part.isOwnedAttribute()) {
2011 Attribute[] attribsArray = node.getAttributes(part.getPartType());
2012 allparts.add(getAttributeOutputBlock(node, request, part, false, Arrays.asList(attribsArray)));
2013 } else {
2014 List related = node.getRawRelated(part.getNodeRelation(), part.getPartType());
2015 allparts.add(getSharedNodeBlock(node, request, part, false, related));
2016 }
2017
2018 } catch (Exception e) {
2019 throw new ControllerException(e);
2020 }
2021
2022 }
2023
2024 }