View Javadoc

1   /**
2    * This file Copyright (c) 2003-2012 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.module.workflow.inbox;
35  
36  import info.magnolia.cms.core.Content;
37  import info.magnolia.cms.core.ItemType;
38  import info.magnolia.cms.gui.control.ContextMenu;
39  import info.magnolia.cms.gui.control.ContextMenuItem;
40  import info.magnolia.cms.gui.control.FunctionBar;
41  import info.magnolia.cms.gui.control.FunctionBarItem;
42  import info.magnolia.cms.gui.controlx.list.ListColumn;
43  import info.magnolia.cms.gui.controlx.list.ListControl;
44  import info.magnolia.cms.gui.controlx.list.ListModel;
45  import info.magnolia.cms.i18n.Messages;
46  import info.magnolia.cms.i18n.MessagesManager;
47  import info.magnolia.cms.util.AlertUtil;
48  import info.magnolia.cms.util.DateUtil;
49  import info.magnolia.context.MgnlContext;
50  import info.magnolia.freemarker.FreemarkerUtil;
51  import info.magnolia.module.ModuleRegistry;
52  import info.magnolia.module.admininterface.lists.AbstractList;
53  import info.magnolia.module.admininterface.lists.AdminListControlRenderer;
54  import info.magnolia.module.workflow.WorkflowConstants;
55  import info.magnolia.module.workflow.WorkflowModule;
56  import info.magnolia.module.workflow.WorkflowUtil;
57  import info.magnolia.objectfactory.Classes;
58  import info.magnolia.objectfactory.Components;
59  
60  import java.text.ParseException;
61  import java.text.SimpleDateFormat;
62  import java.util.Date;
63  import java.util.HashMap;
64  import java.util.Map;
65  import java.util.Map.Entry;
66  
67  import javax.jcr.ItemNotFoundException;
68  import javax.jcr.RepositoryException;
69  import javax.servlet.http.HttpServletRequest;
70  import javax.servlet.http.HttpServletResponse;
71  
72  import org.apache.commons.lang.ObjectUtils;
73  import org.apache.commons.lang.StringEscapeUtils;
74  import org.apache.commons.lang.StringUtils;
75  import org.slf4j.Logger;
76  import org.slf4j.LoggerFactory;
77  
78  /**
79   * The {@link info.magnolia.module.admininterface.lists.AbstractList} responsible for displaying a user's inbox, i.e current work items.
80   * 
81   * @author Philipp Bracher
82   * @version $Revision:3416 $ ($Author:philipp $)
83   */
84  public class Inbox extends AbstractList {
85  
86      private static final Logger log = LoggerFactory.getLogger(Inbox.class);
87  
88      /**
89       * The id of the workitem on which we called the command.
90       */
91      private String flowItemId;
92  
93      /**
94       * The comment the user entered by proceeding or rejecting.
95       */
96      private String comment;
97  
98      /**
99       * Show all the values of the workitem if true.
100      */
101     private boolean debug = false;
102 
103     protected Messages msgs = MessagesManager.getMessages("info.magnolia.module.workflow.messages");
104 
105     private Map<String, Object> showFunctions = new HashMap<String, Object>();
106 
107     public Inbox(String name, HttpServletRequest request, HttpServletResponse response) {
108         super(name, request, response);
109     }
110 
111     @Override
112     public void init() {
113         super.init();
114         final WorkflowModule module = Components.getComponent(ModuleRegistry.class).getModuleInstance(WorkflowModule.class);
115         resolveShowFunctions(module.getShowFunctions());
116     }
117 
118     private void resolveShowFunctions(Map<String, String> showFunctions) {
119         for (Entry<String, String> entry : showFunctions.entrySet()) {
120             String repository = entry.getKey();
121             String value = entry.getValue();
122             Object clazz = null;
123             try {
124                 clazz = Classes.newInstance(value, null);
125             } catch (ClassNotFoundException e) {
126                 // ignore
127             }
128             if (clazz != null && clazz instanceof InboxShowFunction) {
129                 this.showFunctions.put(repository, clazz);
130             } else {
131                 this.showFunctions.put(repository, value);
132             }
133         }
134     }
135 
136     @Override
137     public ListModel getModel() {
138         return new InboxListModel(MgnlContext.getUser().getName());
139     }
140 
141     @Override
142     public String getSortBy() {
143         if (StringUtils.isEmpty(super.getSortBy())) {
144             setSortBy("lastModified");
145         }
146         return super.getSortBy();
147     }
148 
149     /**
150      * Sets the select js code and defines the columns.
151      */
152     @Override
153     public void configureList(ListControl list) {
154 
155         // define the select action
156         list.setRenderer(new AdminListControlRenderer() {
157 
158             @Override
159             public String onSelect(ListControl list, Integer index) {
160 
161                 String customEditDialog = ObjectUtils.toString(list
162                         .getIteratorValue(WorkflowConstants.ATTRIBUTE_EDIT_DIALOG));
163 
164                 String editDialog = StringUtils.defaultIfEmpty(customEditDialog, WorkflowConstants.DEFAULT_EDIT_DIALOG);
165                 String repository = ObjectUtils.toString(list.getIteratorValue("repository"));
166                 String path = ObjectUtils.toString(list.getIteratorValue("path"));
167 
168                 StringBuffer js = new StringBuffer();
169                 js.append("mgnl.workflow.Inbox.current = ");
170                 js.append("{");
171                 js.append("id : '").append(list.getIteratorValue("id")).append("',");
172                 js.append("path : '").append(path).append("',");
173                 js.append("version : '").append(list.getIteratorValue("version")).append("',");
174                 js.append("repository : '").append(repository).append("',");
175                 js.append("workItemPath : '").append(list.getIteratorValue("workItemPath")).append("',");
176                 js.append("editDialog : '").append(editDialog).append("'");
177                 js.append("};");
178                 js.append("mgnl.workflow.Inbox.show = ").append(getShowJSFunction(repository, path)).append(";");
179                 js.append(super.onSelect(list, index));
180                 return js.toString();
181             }
182 
183             @Override
184             public String onDblClick(ListControl list, Integer index) {
185                 return "mgnl.workflow.Inbox.edit();";
186             }
187         });
188 
189         list.addSortableField("lastModified");
190         list.addGroupableField("repository");
191         list.addGroupableField("workflow");
192 
193         list.addColumn(new ListColumn() {
194 
195             {
196                 setName("icon");
197                 setLabel("");
198                 setWidth("30px");
199                 setSeparator(false);
200             }
201 
202             @Override
203             public Object getValue() {
204                 String path = "" + this.getListControl().getIteratorValue("path");
205                 String repository = "" + this.getListControl().getIteratorValue("repository");
206                 String uuid = "" + this.getListControl().getIteratorValue("uuid");
207                 String version = "" + this.getListControl().getIteratorValue("version");
208                 try {
209                     final Content content;
210                     // versioning disabled
211                     if (StringUtils.isEmpty(version)) {
212                         content = MgnlContext.getSystemContext().getHierarchyManager(repository).getContentByUUID(uuid);
213                     } else {
214                         content = MgnlContext.getSystemContext().getHierarchyManager(repository).getContentByUUID(uuid).getVersionedContent(version);
215                     }
216                     if (content.hasMixin(ItemType.DELETED_NODE_MIXIN)) {
217                         path = ItemType.DELETED_NODE_MIXIN;
218                     }
219                 } catch (ItemNotFoundException e) {
220                     log.debug("Item {}:{} does no longer exist in the workspace. Uuid: {}", new String[] { repository, path, uuid });
221 
222                 } catch (RepositoryException e) {
223                     log.error("Failed to retrieve versioned node [path=" + path + ", uuid=" + uuid + ", version=" + version, e);
224                 }
225                 return "<img src=\""
226                         + MgnlContext.getContextPath()
227                         + "/"
228                         + getIcon(path, repository)
229                         + "\" alt=\"\" border=\"0\" />";
230             }
231         });
232         list.addColumn(new ListColumn("name", msgs.get("inbox.item"), "100", true));
233         list.addColumn(new ListColumn("repository", msgs.get("inbox.repository"), "100px", true));
234         list.addColumn(new ListColumn("workflow", msgs.get("inbox.workflow"), "100px", true));
235         list.addColumn(new ListColumn() {
236             {
237                 setName("comment");
238                 setLabel(msgs.get("inbox.comment"));
239                 setWidth("200px");
240                 setSeparator(true);
241             }
242 
243             @Override
244             public Object getValue() {
245                 if (super.getValue() instanceof String) {
246                     String comment = (String) super.getValue();
247                     return StringEscapeUtils.escapeHtml(comment);
248                 } else {
249                     openwfe.org.engine.workitem.StringAttribute comment = (openwfe.org.engine.workitem.StringAttribute) super.getValue();
250                     comment.setValue(StringEscapeUtils.escapeHtml(comment.getValue().toString()));
251                     return comment;
252                 }
253             }
254         });
255         list.addColumn(new ListColumn() {
256 
257             {
258                 setName("lastModified");
259                 setLabel(msgs.get("inbox.date"));
260                 setWidth("150px");
261             }
262 
263             @Override
264             public Object getValue() {
265                 String str = (String) super.getValue();
266                 Date date = null;
267                 try {
268                     date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ").parse(str);
269                     return DateUtil.formatDateTime(date);
270 
271                 }
272                 catch (ParseException e) {
273                     return StringUtils.EMPTY;
274                 }
275             }
276         });
277 
278     }
279 
280     protected String getIcon(String path, String repository) {
281         return InboxHelper.getIcon(repository, path);
282     }
283 
284     protected String getShowJSFunction(String repository, String path) {
285         if (this.showFunctions.containsKey(repository)) {
286             Object showFunction = showFunctions.get(repository);
287             if (showFunction instanceof InboxShowFunction) {
288                 return ((InboxShowFunction) showFunctions.get(repository)).getShowJsFunction(path);
289             }
290             if (((String) showFunction).length() > 0) {
291                 return (String) showFunctions.get(repository);
292             }
293         }
294         return InboxHelper.getShowJSFunction(repository, path);
295     }
296 
297     @Override
298     public void configureContextMenu(ContextMenu menu) {
299         ContextMenuItem edit = new ContextMenuItem("edit");
300         edit.setLabel(msgs.get("inbox.edit"));
301         edit.setOnclick("mgnl.workflow.Inbox.edit();");
302         edit.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/mail_write.gif");
303         edit.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
304 
305         ContextMenuItem show = new ContextMenuItem("show");
306         show.setLabel(msgs.get("inbox.show"));
307         show.setOnclick("mgnl.workflow.Inbox.show();");
308         show.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/note_view.gif");
309         show.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
310 
311         ContextMenuItem proceed = new ContextMenuItem("proceed");
312         proceed.setLabel(msgs.get("inbox.proceed"));
313         proceed.setOnclick("mgnl.workflow.Inbox.proceed();");
314         proceed.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/navigate_right2_green.gif");
315         proceed.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
316 
317         ContextMenuItem reject = new ContextMenuItem("reject");
318         reject.setLabel(msgs.get("inbox.reject"));
319         reject.setOnclick("mgnl.workflow.Inbox.reject();");
320         reject.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/navigate_left2_red.gif");
321         reject.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
322 
323         ContextMenuItem cancel = new ContextMenuItem("cancel");
324         cancel.setLabel(msgs.get("inbox.cancel"));
325         cancel.setOnclick("mgnl.workflow.Inbox.cancel();");
326         cancel.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/delete2.gif");
327         cancel.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
328 
329         menu.addMenuItem(edit);
330         menu.addMenuItem(null);
331         menu.addMenuItem(show);
332         menu.addMenuItem(null);
333         menu.addMenuItem(proceed);
334         menu.addMenuItem(reject);
335         menu.addMenuItem(null);
336         menu.addMenuItem(cancel);
337     }
338 
339     /**
340      * Same as the context menu.
341      */
342     @Override
343     public void configureFunctionBar(FunctionBar bar) {
344         ContextMenu menu = this.getContextMenu();
345         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("edit")));
346         bar.addMenuItem(null);
347         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("show")));
348         bar.addMenuItem(null);
349         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("reject")));
350         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("proceed")));
351         bar.addMenuItem(null);
352         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("cancel")));
353     }
354 
355     /**
356      * Add some inbox specific stuff: mainly hidden fields.
357      */
358     @Override
359     public String onRender() {
360         return FreemarkerUtil.process(this);
361     }
362 
363     /**
364      * Proceed the item.
365      */
366     public String proceed() {
367         try {
368             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_PROCEED);
369         } catch (Exception e) {
370             AlertUtil.setMessage("can't proceed:", e);
371         }
372         return this.show();
373     }
374 
375     /**
376      * Reject the item (adds a comment).
377      */
378     public String reject() {
379         try {
380             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_REJECT, this.getComment());
381         } catch (Exception e) {
382             AlertUtil.setMessage("can't reject:", e);
383         }
384         return this.show();
385     }
386 
387     /**
388      * Stop the workflow.
389      */
390     public String cancel() {
391         try {
392             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_CANCEL);
393         } catch (Exception e) {
394             AlertUtil.setMessage("can't cancel:", e);
395         }
396         return this.show();
397     }
398 
399     public String getFlowItemId() {
400         return this.flowItemId;
401     }
402 
403     public void setFlowItemId(String flowItemId) {
404         this.flowItemId = flowItemId;
405     }
406 
407     public String getComment() {
408         return this.comment;
409     }
410 
411     public void setComment(String comment) {
412         this.comment = comment;
413     }
414 
415     public boolean isDebug() {
416         return this.debug;
417     }
418 
419     public void setDebug(boolean debug) {
420         this.debug = debug;
421     }
422 
423 }