001: package org.emforge.jbpm.web.bean;
002:
003: import java.io.IOException;
004: import java.util.Collection;
005: import java.util.Date;
006: import java.util.LinkedList;
007: import java.util.List;
008:
009: import javax.faces.model.SelectItem;
010:
011: import org.apache.commons.collections.CollectionUtils;
012: import org.apache.commons.lang.StringUtils;
013: import org.apache.commons.logging.Log;
014: import org.apache.commons.logging.LogFactory;
015: import org.emforge.BpmService;
016: import org.emforge.EmForgeException;
017: import org.emforge.projectmanager.ProjectService;
018: import org.emforge.projectmanager.base.ProjectDO;
019: import org.emforge.xfer.AttachmentTO;
020: import org.emforge.xfer.PriorityTO;
021: import org.emforge.xfer.StepTO;
022: import org.emforge.xfer.TaskTO;
023: import org.emforge.xfer.TransitionTO;
024: import org.emforge.xfer.VariableTO;
025: import org.emforge.xfer.WorkflowTO;
026:
027: import ru.emdev.EmForge.security.UserSettingsService;
028: import ru.emdev.EmForge.util.RandomGUID;
029: import ru.emdev.EmForge.web.bean.BaseControllerImpl;
030: import ru.emdev.EmForge.web.bean.EditTextController;
031: import ru.emdev.EmForge.web.bean.MainMenuController.MainMenuItem;
032: import ru.emdev.EmForge.wiki.web.bean.Crumb;
033:
034: import com.ecyrd.jspwiki.SearchResult;
035: import com.ecyrd.jspwiki.WikiContext;
036: import com.ecyrd.jspwiki.WikiException;
037: import com.ecyrd.jspwiki.WikiPage;
038: import com.ecyrd.jspwiki.providers.ProviderException;
039: import com.ecyrd.jspwiki.url.URLConstructor;
040:
041: /** this controller is used to create new task
042: *
043: * It is included several steps, so, it is why it is implemented as session-bean
044: * (to easily keep information between steps)
045: *
046: */
047: public class NewTaskController extends BaseControllerImpl {
048: private static final String NEWTASK_NAME_PREFIX = "__newtask_";
049: private final Log logger = LogFactory.getLog(getClass());
050:
051: /** This structure is used for displaying list of tasks, found by searching by title
052: *
053: */
054: public class TaskSearchResult {
055: private Long id;
056: private String title;
057: private String[] fragments;
058: private String iconLink;
059:
060: public TaskSearchResult(Long id, String title,
061: String[] fragments, String iconLink) {
062: this .id = id;
063: this .title = title;
064: this .fragments = fragments;
065: this .iconLink = iconLink;
066: }
067:
068: public Long getId() {
069: return id;
070: }
071:
072: public void setId(Long i_id) {
073: id = i_id;
074: }
075:
076: public String getTitle() {
077: return title;
078: }
079:
080: public void setTitle(String i_title) {
081: title = i_title;
082: }
083:
084: public String[] getFragments() {
085: return fragments;
086: }
087:
088: public String getIconLink() {
089: return iconLink;
090: }
091:
092: public void setIconLink(String i_iconLink) {
093: iconLink = i_iconLink;
094: }
095:
096: public void setFragments(String[] i_fragments) {
097: fragments = i_fragments;
098: }
099:
100: public String getFragment() {
101: if (fragments.length > 0) {
102: return fragments[0];
103: }
104: return null;
105: }
106:
107: public String getUrl() {
108: return urlConstructor.makeURL(WikiContext.VIEW, id
109: .toString(), false, null);
110: }
111: }
112:
113: private BpmService bpmService;
114: private ProjectService projectService;
115: private UserSettingsService userSettingsService;
116: private URLConstructor urlConstructor;
117:
118: private static final Integer STEP_PROJECT_AND_WORKFLOW = 1;
119: private static final Integer STEP_TITLE_SEARCH = 2;
120: private static final Integer STEP_SEARCH_RESULTS = 3;
121: private static final Integer STEP_DESCRIPTION = 4;
122: private static final Integer STEP_OPTIONS = 5;
123: private static final Integer STEP_TRANSITIONS = 6;
124: private static final Integer STEP_PREVIEW = 7;
125:
126: private Integer step = STEP_PROJECT_AND_WORKFLOW;
127:
128: private StepTO parentStep;
129: private WorkflowTO workflow;
130: private StepTO startStep;
131: private Long projectId;
132:
133: private String title;
134: private List<TaskSearchResult> searchResult;
135:
136: private String pageName;
137: private String description;
138:
139: PriorityTO taskPriority = PriorityTO.NORMAL;
140: Date taskDueDate;
141:
142: Collection<SelectItem> transitionSelectItems = null;
143: TransitionTO selectedTransition = null;
144:
145: public void setBpmService(BpmService i_bpmService) {
146: bpmService = i_bpmService;
147: }
148:
149: public void setProjectService(ProjectService i_projectService) {
150: projectService = i_projectService;
151: }
152:
153: public void setUserSettingsService(
154: UserSettingsService i_userSettingsService) {
155: userSettingsService = i_userSettingsService;
156: }
157:
158: public void setUrlConstructor(URLConstructor i_urlConstructor) {
159: urlConstructor = i_urlConstructor;
160: }
161:
162: @Override
163: public MainMenuItem getSelectionItemOnMainMenu() {
164: return MainMenuItem.TASKS;
165: }
166:
167: @Override
168: public String getTitleImpl() {
169: return "Create New Task";
170: }
171:
172: @Override
173: public Crumb getTrailCrumbInfo() {
174: return null;
175: }
176:
177: @Override
178: protected void init() {
179: // actually - nothing to do here - since it is session bean
180: }
181:
182: public WorkflowTO[] getWorkflowList() {
183: try {
184: return bpmService.getWorkflows();
185: } catch (EmForgeException ex) {
186: addErrorMessage("Cannot get list of workflows", ex
187: .getMessage());
188: return null;
189: }
190: }
191:
192: public String getWorkflowStyle() {
193: WorkflowTO processedWorkflow = (WorkflowTO) getValue("#{workflow}");
194:
195: if (workflow != null && workflow.equals(processedWorkflow)) {
196: return "font-weight:bold;";
197: } else {
198: return "";
199: }
200: }
201:
202: public String getFormHeader() {
203: String prefix = "Create New Task";
204:
205: if (parentStep != null) {
206: prefix = "Create Sub-Task";
207: }
208:
209: if (STEP_PROJECT_AND_WORKFLOW.equals(step)) {
210: return prefix + ": Choose Project and Task Workflow";
211: } else if (STEP_TITLE_SEARCH.equals(step)) {
212: return prefix + ": Enter Task Title";
213: } else if (STEP_SEARCH_RESULTS.equals(step)) {
214: return prefix + ": Title Search Results";
215: } else if (STEP_DESCRIPTION.equals(step)) {
216: return prefix + ": Edit Description";
217: } else if (STEP_OPTIONS.equals(step)) {
218: return prefix + ": Edit Options";
219: } else if (STEP_TRANSITIONS.equals(step)) {
220: return prefix + ": Choose the Action";
221: } else if (STEP_PREVIEW.equals(step)) {
222: return prefix + ": Preview";
223: } else {
224: return null;
225: }
226: }
227:
228: public StepTO getParentStep() {
229: return parentStep;
230: }
231:
232: public void setParentStep(StepTO i_parentStep) {
233: parentStep = i_parentStep;
234:
235: // copy task general properties into new process properties;
236: taskPriority = parentStep.getPriority();
237: taskDueDate = parentStep.getDueDate();
238: }
239:
240: public WorkflowTO getWorkflow() {
241: return workflow;
242: }
243:
244: public void setWorkflowId(Long id) throws Exception {
245: if (id == null) {
246: setWorkflow(null);
247: } else {
248: setWorkflow(bpmService.getWorkflowById(id));
249: }
250: }
251:
252: @SuppressWarnings("unchecked")
253: public void setWorkflow(WorkflowTO i_workflow) {
254: workflow = i_workflow;
255:
256: // clear start step
257: startStep = null;
258:
259: // initialize start task
260: getStartStep();
261: }
262:
263: public String getWorkflowName() {
264: if (workflow == null) {
265: return null;
266: }
267:
268: return workflow.getName();
269: }
270:
271: public ProjectDO getTaskProject() {
272: if (projectId == null) {
273: // return user default project
274: return userSettingsService.getDefaultProject();
275: } else {
276: return projectService.getProject(projectId);
277: }
278:
279: }
280:
281: public void setTaskProject(ProjectDO project) {
282: if (project == null) {
283: projectId = null;
284: } else {
285: projectId = project.getId();
286: }
287: }
288:
289: public PriorityTO getTaskPriority() {
290: return taskPriority;
291: }
292:
293: public void setTaskPriority(PriorityTO i_taskPriority) {
294: taskPriority = i_taskPriority;
295: }
296:
297: public Date getTaskDueDate() {
298: return taskDueDate;
299: }
300:
301: public void setTaskDueDate(Date i_processDueDate) {
302: taskDueDate = i_processDueDate;
303: }
304:
305: public VariableTO[] getWritableVariables() {
306: StepTO step = getStartStep();
307:
308: if (step != null) {
309: return step.getWritableVariables();
310: } else {
311: return null;
312: }
313: }
314:
315: private StepTO getStartStep() {
316: if (startStep == null) {
317: if (workflow != null && getTaskProject() != null) {
318: try {
319: startStep = bpmService.getWorkflowStartStep(
320: workflow.getName(), workflow.getVersion(),
321: getTaskProject().getName());
322:
323: // fill transitions
324: transitionSelectItems = new LinkedList<SelectItem>();
325: for (TransitionTO transition : startStep
326: .getTransitions()) {
327: transitionSelectItems.add(new SelectItem(
328: transition, transition.getLabel()));
329: }
330: } catch (EmForgeException ex) {
331: addErrorMessage(
332: "Cannot get start step for Workflow", ex
333: .getMessage());
334: }
335: }
336: }
337:
338: return startStep;
339: }
340:
341: /** Action to select some process definition to start new task
342: *
343: * Actually - nothing to do here - since we need just to set selected process def
344: */
345: public String selectWorkflow() {
346: return null;
347: }
348:
349: // steps accessors
350: public boolean isProjectAndWorkflow() {
351: return STEP_PROJECT_AND_WORKFLOW.equals(step);
352: }
353:
354: public boolean isTitleSearch() {
355: return STEP_TITLE_SEARCH.equals(step);
356: }
357:
358: public boolean isSearchResults() {
359: return STEP_SEARCH_RESULTS.equals(step);
360: }
361:
362: public boolean isShowDescription() {
363: return STEP_DESCRIPTION.equals(step);
364: }
365:
366: public boolean isShowOptions() {
367: return STEP_OPTIONS.equals(step);
368: }
369:
370: public boolean isShowTransitions() {
371: return STEP_TRANSITIONS.equals(step);
372: }
373:
374: public boolean isShowPreview() {
375: return STEP_PREVIEW.equals(step);
376: }
377:
378: public String getTitle() {
379: return title;
380: }
381:
382: public void setTitle(String i_title) {
383: title = i_title;
384: }
385:
386: public String getDescription() {
387: return description;
388: }
389:
390: public String getPageName() {
391: if (pageName == null) {
392: // generate page name
393: RandomGUID myGUID = new RandomGUID();
394:
395: pageName = NEWTASK_NAME_PREFIX + myGUID.valueAfterMD5;
396:
397: WikiPage newpage = new WikiPage(m_wikiEngine, pageName);
398: WikiContext wikiContext = new WikiContext(m_wikiEngine,
399: newpage);
400:
401: String user = getCurrentUser().getUsername();
402: wikiContext.getPage().setAuthor(user);
403: wikiContext.getPage().setLastModified(new Date());
404:
405: try {
406: m_wikiEngine.saveText(wikiContext, ""); // save empty string for now
407: } catch (WikiException ex) {
408: logger.error("Cannot create page " + pageName, ex);
409: }
410:
411: // this is strange - but we should do it - to correctly initialyze pageName wiki converter
412: // which will be used latelly in Preview
413: initEditTextController();
414: }
415:
416: return pageName;
417: }
418:
419: @SuppressWarnings("unchecked")
420: public AttachmentTO[] getAttachments() {
421: if (StringUtils.isEmpty(getPageName())) {
422: return new AttachmentTO[0];
423: }
424:
425: try {
426: return m_attachmentService.getAttachments(getPageName());
427: } catch (EmForgeException ex) {
428: logger.error("Cannot get list of attachments", ex);
429: }
430: return new AttachmentTO[0];
431: }
432:
433: public List<TaskSearchResult> getSearchResult() {
434: return searchResult;
435: }
436:
437: public void setSearchResult(List<TaskSearchResult> i_searchResult) {
438: searchResult = i_searchResult;
439: }
440:
441: /** performs seach by entered title
442: *
443: */
444: @SuppressWarnings("unchecked")
445: public String searchTitle() {
446: if (StringUtils.isNotEmpty(title)) {
447: try {
448: Collection<SearchResult> luceneResults = m_wikiEngine
449: .findPages(title);
450: searchResult = new LinkedList<TaskSearchResult>();
451:
452: for (SearchResult result : luceneResults) {
453: // is found page - task?
454: try {
455: //parse long
456: Long taskId = Long.valueOf(result.getPage()
457: .getName());
458:
459: // get task
460: TaskTO task = bpmService.getTask(taskId);
461: if (task != null
462: && task.getProjectName() != null
463: && task.getProjectName().equals(
464: getTaskProject().getName())) {
465: // add it into result
466: searchResult.add(new TaskSearchResult(task
467: .getId(), task.getTitle(), result
468: .getContexts(), task
469: .getWorkflowIconLink()));
470:
471: // show only 10 results
472: if (searchResult.size() == 10) {
473: break;
474: }
475:
476: }
477: } catch (Exception ex) {
478: // just ignore it here
479: }
480: }
481:
482: } catch (ProviderException e) {
483: logger.error(e);
484: addErrorMessage("Cannot process search", e.getMessage());
485: } catch (IOException e) {
486: logger.error(e);
487: addErrorMessage("Cannot process search", e.getMessage());
488: }
489: }
490:
491: return null;
492: }
493:
494: private void initEditTextController() {
495: // get editText controller
496: EditTextController editTextController = (EditTextController) getBean("editTextController");
497:
498: editTextController.setPageName(getPageName());
499: editTextController.setWikiText(description);
500: }
501:
502: public void storeDescription() {
503: // get editText controller
504: EditTextController editTextController = (EditTextController) getBean("editTextController");
505:
506: description = editTextController.getWikiText();
507: }
508:
509: // Navigation Actions and accessors
510: public boolean isDisplayPrev() {
511: if (STEP_PROJECT_AND_WORKFLOW.equals(step)) {
512: return false;
513: } else {
514: return true;
515: }
516: }
517:
518: public Collection<SelectItem> getTransitions() {
519: return transitionSelectItems;
520: }
521:
522: public TransitionTO getSelectedTransition() {
523: return selectedTransition;
524: }
525:
526: public void setSelectedTransition(TransitionTO i_selectedTransition) {
527: selectedTransition = i_selectedTransition;
528: }
529:
530: /** Calling getLabel from selectedTransition may produce lazy-initialization error
531: * since it is session-wide bean.
532: * So, we will get it from select items
533: * @return
534: */
535: public String getSelectedTransitionTitle() {
536: if (selectedTransition == null) {
537: return null;
538: } else {
539: for (SelectItem selectItem : transitionSelectItems) {
540: if (selectedTransition.equals(selectItem.getValue())) {
541: return selectItem.getLabel();
542: }
543: }
544: }
545:
546: return null;
547: }
548:
549: public boolean isDisplayNext() {
550: if (STEP_PROJECT_AND_WORKFLOW.equals(step)) {
551: return getTaskProject() != null && workflow != null;
552: } else if (STEP_TITLE_SEARCH.equals(step)) {
553: return true;
554: } else if (STEP_SEARCH_RESULTS.equals(step)) {
555: return true;
556: } else if (STEP_DESCRIPTION.equals(step)) {
557: return true;
558: } else if (STEP_OPTIONS.equals(step)) {
559: return true;
560: } else if (STEP_TRANSITIONS.equals(step)) {
561: return true;
562: } else {
563: return false;
564: }
565: }
566:
567: public boolean isDisplayCreateButton() {
568: if (STEP_PREVIEW.equals(step)) {
569: return true;
570: } else {
571: return false;
572: }
573: }
574:
575: public String getNextButtonStyle() {
576: if (isDisplayNext()) {
577: return "display:inline;";
578: } else {
579: return "display:none;";
580: }
581: }
582:
583: public void clean() {
584: projectId = null;
585: parentStep = null;
586: workflow = null;
587: startStep = null;
588: title = null;
589: searchResult = null;
590: description = null;
591:
592: pageName = null;
593:
594: selectedTransition = null;
595: transitionSelectItems = null;
596:
597: step = STEP_PROJECT_AND_WORKFLOW;
598: }
599:
600: /** Clear data in the form and go back to main page
601: *
602: * @return
603: */
604: public String cancel() {
605: if (pageName != null) {
606: try {
607: m_wikiEngine.deletePage(pageName);
608: } catch (ProviderException ex) {
609: logger.error(
610: "Cannot remove temporary page " + pageName, ex);
611: }
612: }
613: clean();
614:
615: step = STEP_PROJECT_AND_WORKFLOW;
616:
617: return "tasks";
618: }
619:
620: public String next() {
621: if (STEP_PROJECT_AND_WORKFLOW.equals(step)) {
622: step = STEP_TITLE_SEARCH;
623: } else if (STEP_TITLE_SEARCH.equals(step)) {
624: searchTitle();
625: if (CollectionUtils.isEmpty(searchResult)) {
626: initEditTextController();
627: step = STEP_DESCRIPTION;
628: } else {
629: step = STEP_SEARCH_RESULTS;
630: }
631: } else if (STEP_SEARCH_RESULTS.equals(step)) {
632: initEditTextController();
633: step = STEP_DESCRIPTION;
634: } else if (STEP_DESCRIPTION.equals(step)) {
635: storeDescription();
636: step = STEP_OPTIONS;
637: } else if (STEP_OPTIONS.equals(step)) {
638: StepTO stepTo = getStartStep();
639: if (stepTo != null && stepTo.getTransitions().length > 1) {
640: // show transitions only if required
641: step = STEP_TRANSITIONS;
642: } else {
643: step = STEP_PREVIEW;
644: }
645: } else if (STEP_TRANSITIONS.equals(step)) {
646: step = STEP_PREVIEW;
647: }
648:
649: return null;
650: }
651:
652: /** Is "Prev" action should be done immidiatly?
653: *
654: * For "Description" step - no - we should store edited text.
655: * @return
656: */
657: public boolean isPrevImmidiate() {
658: return !(STEP_DESCRIPTION.equals(step));
659: }
660:
661: public String prev() {
662: if (STEP_DESCRIPTION.equals(step)) {
663: storeDescription();
664: }
665: step -= 1;
666:
667: if (STEP_DESCRIPTION.equals(step)) {
668: // initialize edit text controller to put back edited description
669: initEditTextController();
670: } else if (STEP_SEARCH_RESULTS.equals(step)
671: && (searchResult == null || searchResult.size() == 0)) {
672: // skip search results step
673: step--;
674: } else if (STEP_TRANSITIONS.equals(step)) {
675: StepTO stepTo = getStartStep();
676: if (stepTo != null && stepTo.getTransitions().length <= 1) {
677: // go one more step backward
678: step -= 1;
679: }
680: }
681:
682: return null;
683: }
684:
685: public String create() {
686: StepTO stepTo = null;
687: TaskTO createdTask = null;
688:
689: // save description
690: saveDescription(description);
691:
692: stepTo = getStartStep();
693:
694: stepTo.setTitle(title);
695: stepTo.setPriority(taskPriority);
696: stepTo.setDueDate(taskDueDate);
697:
698: String transitionName = "";
699: if (selectedTransition != null) {
700: transitionName = selectedTransition.getName();
701: }
702:
703: try {
704: if (parentStep == null) {
705: createdTask = bpmService.startTask(stepTo,
706: transitionName, getPageName());
707: } else {
708: createdTask = bpmService
709: .startSubTask(parentStep.getId(), stepTo,
710: transitionName, getPageName());
711: }
712: } catch (EmForgeException ex) {
713: addErrorMessage("Cannot start Task", ex.getMessage());
714:
715: return null;
716: }
717:
718: clean();
719:
720: // redirect to the process page to see - that process is created
721: redirect(TaskController.TASK_PAGE_NAME + "/"
722: + createdTask.getId());
723:
724: return null;
725: }
726:
727: private void saveDescription(String i_content) {
728: // save content into wiki
729: try {
730: WikiContext wikiContext = new WikiContext(m_wikiEngine,
731: m_wikiEngine.getPage(getPageName()));
732: m_wikiEngine.saveText(wikiContext, i_content);
733: } catch (Exception e) {
734: logger.error("Cannot put text to page " + getPageName()
735: + ":" + e);
736: }
737: }
738: }
|