View Javadoc

1   /**
2    * This file Copyright (c) 2003-2014 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 final 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                 Object value = super.getValue();
246                 if (value instanceof String) {
247                     String comment = (String) super.getValue();
248                     return StringEscapeUtils.escapeHtml(comment);
249                 } else if (value instanceof openwfe.org.engine.workitem.StringAttribute) {
250                     openwfe.org.engine.workitem.StringAttribute comment = (openwfe.org.engine.workitem.StringAttribute) super.getValue();
251                     comment.setValue(StringEscapeUtils.escapeHtml(comment.getValue().toString()));
252                     return comment;
253                 } else {
254                     log.warn("Inbox: cannot set value for column '{}'.", this.getName());
255                     return StringUtils.EMPTY;
256                 }
257             }
258         });
259         list.addColumn(new ListColumn() {
260 
261             {
262                 setName("lastModified");
263                 setLabel(msgs.get("inbox.date"));
264                 setWidth("150px");
265             }
266 
267             @Override
268             public Object getValue() {
269                 String str = (String) super.getValue();
270                 Date date = null;
271                 try {
272                     date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ssZ").parse(str);
273                     return DateUtil.formatDateTime(date);
274 
275                 }
276                 catch (ParseException e) {
277                     return StringUtils.EMPTY;
278                 }
279             }
280         });
281 
282     }
283 
284     protected String getIcon(String path, String repository) {
285         return InboxHelper.getIcon(repository, path);
286     }
287 
288     protected String getShowJSFunction(String repository, String path) {
289         if (this.showFunctions.containsKey(repository)) {
290             Object showFunction = showFunctions.get(repository);
291             if (showFunction instanceof InboxShowFunction) {
292                 return ((InboxShowFunction) showFunctions.get(repository)).getShowJsFunction(path);
293             }
294             if (((String) showFunction).length() > 0) {
295                 return (String) showFunctions.get(repository);
296             }
297         }
298         return InboxHelper.getShowJSFunction(repository, path);
299     }
300 
301     @Override
302     public void configureContextMenu(ContextMenu menu) {
303         ContextMenuItem edit = new ContextMenuItem("edit");
304         edit.setLabel(msgs.get("inbox.edit"));
305         edit.setOnclick("mgnl.workflow.Inbox.edit();");
306         edit.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/mail_write.gif");
307         edit.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
308 
309         ContextMenuItem show = new ContextMenuItem("show");
310         show.setLabel(msgs.get("inbox.show"));
311         show.setOnclick("mgnl.workflow.Inbox.show();");
312         show.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/note_view.gif");
313         show.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
314 
315         ContextMenuItem proceed = new ContextMenuItem("proceed");
316         proceed.setLabel(msgs.get("inbox.proceed"));
317         proceed.setOnclick("mgnl.workflow.Inbox.proceed();");
318         proceed.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/navigate_right2_green.gif");
319         proceed.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
320 
321         ContextMenuItem reject = new ContextMenuItem("reject");
322         reject.setLabel(msgs.get("inbox.reject"));
323         reject.setOnclick("mgnl.workflow.Inbox.reject();");
324         reject.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/navigate_left2_red.gif");
325         reject.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
326 
327         ContextMenuItem cancel = new ContextMenuItem("cancel");
328         cancel.setLabel(msgs.get("inbox.cancel"));
329         cancel.setOnclick("mgnl.workflow.Inbox.cancel();");
330         cancel.setIcon(MgnlContext.getContextPath() + "/.resources/icons/16/delete2.gif");
331         cancel.addJavascriptCondition("{test: function(){return mgnl.workflow.Inbox.current.id!=null}}");
332 
333         menu.addMenuItem(edit);
334         menu.addMenuItem(null);
335         menu.addMenuItem(show);
336         menu.addMenuItem(null);
337         menu.addMenuItem(proceed);
338         menu.addMenuItem(reject);
339         menu.addMenuItem(null);
340         menu.addMenuItem(cancel);
341     }
342 
343     /**
344      * Same as the context menu.
345      */
346     @Override
347     public void configureFunctionBar(FunctionBar bar) {
348         ContextMenu menu = this.getContextMenu();
349         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("edit")));
350         bar.addMenuItem(null);
351         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("show")));
352         bar.addMenuItem(null);
353         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("reject")));
354         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("proceed")));
355         bar.addMenuItem(null);
356         bar.addMenuItem(new FunctionBarItem(menu.getMenuItemByName("cancel")));
357     }
358 
359     /**
360      * Add some inbox specific stuff: mainly hidden fields.
361      */
362     @Override
363     public String onRender() {
364         return FreemarkerUtil.process(this);
365     }
366 
367     /**
368      * Proceed the item.
369      */
370     public String proceed() {
371         try {
372             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_PROCEED);
373         } catch (Exception e) {
374             AlertUtil.setMessage("can't proceed:", e);
375         }
376         return this.show();
377     }
378 
379     /**
380      * Reject the item (adds a comment).
381      */
382     public String reject() {
383         try {
384             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_REJECT, this.getComment());
385         } catch (Exception e) {
386             AlertUtil.setMessage("can't reject:", e);
387         }
388         return this.show();
389     }
390 
391     /**
392      * Stop the workflow.
393      */
394     public String cancel() {
395         try {
396             WorkflowUtil.proceed(this.getFlowItemId(), WorkflowConstants.ACTION_CANCEL);
397         } catch (Exception e) {
398             AlertUtil.setMessage("can't cancel:", e);
399         }
400         return this.show();
401     }
402 
403     public String getFlowItemId() {
404         return this.flowItemId;
405     }
406 
407     public void setFlowItemId(String flowItemId) {
408         this.flowItemId = flowItemId;
409     }
410 
411     public String getComment() {
412         return this.comment;
413     }
414 
415     public void setComment(String comment) {
416         this.comment = comment;
417     }
418 
419     public boolean isDebug() {
420         return this.debug;
421     }
422 
423     public void setDebug(boolean debug) {
424         this.debug = debug;
425     }
426 
427 }