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.cms.gui.dialog;
35  
36  import freemarker.template.TemplateException;
37  import info.magnolia.cms.core.Content;
38  import info.magnolia.cms.core.ItemType;
39  import info.magnolia.cms.core.NodeData;
40  import info.magnolia.cms.gui.control.ControlImpl;
41  import info.magnolia.cms.gui.control.FreemarkerControl;
42  import info.magnolia.cms.security.AccessDeniedException;
43  import info.magnolia.cms.util.NodeDataUtil;
44  
45  import java.io.IOException;
46  import java.io.Writer;
47  import java.util.ArrayList;
48  import java.util.Collection;
49  import java.util.HashMap;
50  import java.util.Iterator;
51  import java.util.LinkedHashMap;
52  import java.util.List;
53  import java.util.Map;
54  
55  import javax.jcr.PathNotFoundException;
56  import javax.jcr.PropertyType;
57  import javax.jcr.RepositoryException;
58  import javax.servlet.http.HttpServletRequest;
59  import javax.servlet.http.HttpServletResponse;
60  
61  import org.apache.commons.lang.StringUtils;
62  import org.apache.commons.lang.exception.NestableRuntimeException;
63  import org.slf4j.Logger;
64  import org.slf4j.LoggerFactory;
65  
66  
67  /**
68   * A Magnolia dialog that renders by a freemarker template. There are two main properties for the dialog:<br/>
69   * <table>
70   * <tr>
71   * <td>path (required)</td>
72   * <td>Path to freemarker template: will be loaded from classpath or from filesystem</td>
73   * </tr>
74   * <tr>
75   * <td>multiple</td>
76   * <td>true / false. This property gives support to multiple field values storage.</td>
77   * </tr>
78   * </table>
79   * The dialog passes some parameters to freemarker template:
80   * <table>
81   * <tr>
82   * <td>name</td>
83   * <td>Dialog / field name</td>
84   * </tr>
85   * <tr>
86   * <td>value</td>
87   * <td>Field value (multiple = false)</td>
88   * </tr>
89   * <tr>
90   * <td>values</td>
91   * <td>field values (multiple = true)</td>
92   * </tr>
93   * <tr>
94   * <td>request</td>
95   * <td>current HttpServletRequest</td>
96   * </tr>
97   * <tr>
98   * <td>configuration</td>
99   * <td>Map of dialog configuration. This allows to pass to template complex dialog configuration.<br/> Eg.
100  *
101  * <pre>
102  * -+ Dialog node
103  *  |-- property 1 = value 1
104  *  |-+ subnode1
105  *  | |-- property 11 = value 11
106  *  | |-- property 12 = value 12
107  *  | |-+ subnode 11
108  *  |   |-- property 111 = value 111
109  *  |
110  *  |-- property 2 = value 2
111  *
112  * The map will contain:
113  * configuration = Map {
114  *    "property1" = "value1",
115  *    "subnode1"  = Map {
116  *        "property11" = "value11",
117  *        "property12" = "value12",
118  *        "subnode11"  =  Map {
119  *            "property111" = "value111"
120  *        }
121  *    },
122  *    "property2" = "value2"
123  * }
124  * </pre>
125  * </td>
126  * </tr>
127  * </table>
128  * @author Manuel Molaschi
129  * @version $Id: $
130  */
131 
132 public class DialogFreemarker extends DialogBox {
133 
134     /**
135      * Logger.
136      */
137     private static Logger log = LoggerFactory.getLogger(DialogFreemarker.class);
138 
139     private Map configuration;
140 
141     private String path;
142 
143     /**
144      * Sets the path.
145      * @param path the path to set
146      */
147     public void setPath(String path) {
148         this.path = path;
149     }
150 
151     /**
152      * Returns the configuration.
153      * @return the configuration
154      */
155     public Map getConfiguration() {
156         return configuration;
157     }
158 
159     /**
160      * Returns the path.
161      * @return the path
162      */
163     public String getPath() {
164         return path;
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
170     @Override
171     protected List readValues() {
172 
173         List values = new ArrayList();
174 
175         if (this.getStorageNode() != null) {
176             try {
177                 // cycles on website content node to get multiple value
178                 int size = this.getStorageNode().getContent(this.getName()).getNodeDataCollection().size();
179                 for (int i = 0; i < size; i++) {
180                     NodeData data = this.getStorageNode().getContent(this.getName()).getNodeData("" + i);
181                     values.add(data.getString());
182                 }
183             }
184             catch (PathNotFoundException e) {
185                 // not yet existing: OK
186             }
187             catch (RepositoryException re) {
188                 log.error("can't set values", re);
189             }
190         }
191         return values;
192     }
193 
194     /**
195      * Get a recursive map view of a content node
196      * @param node content node
197      * @return recursive map view on content node properties and children
198      * @throws PathNotFoundException
199      * @throws RepositoryException
200      * @throws AccessDeniedException
201      */
202     protected Map getSubNodes(Content node) throws RepositoryException, AccessDeniedException {
203 
204         Map values = new LinkedHashMap();
205 
206         // cycles on properties and stores them in map
207         Collection properties = node.getNodeDataCollection();
208 
209         if (properties != null && properties.size() > 0) {
210             Iterator propertiesIt = properties.iterator();
211             while (propertiesIt.hasNext()) {
212                 NodeData property = (NodeData) propertiesIt.next();
213                 values.put(property.getName(), NodeDataUtil.getValueObject(property));
214             }
215         }
216 
217         // cycle on children
218         Collection children = node.getChildren(ItemType.CONTENTNODE);
219         if (children != null && children.size() > 0) {
220             Iterator childrenIt = children.iterator();
221             while (childrenIt.hasNext()) {
222                 Content child = (Content) childrenIt.next();
223 
224                 // gets sub map
225                 Map subValues = getSubNodes(child);
226                 // stores it in map
227                 values.put(child.getName(), subValues);
228             }
229         }
230 
231         return values;
232     }
233 
234     /**
235      * {@inheritDoc}
236      */
237 
238     @Override
239     public void init(HttpServletRequest request, HttpServletResponse response, Content websiteNode, Content configNode)
240         throws RepositoryException {
241         super.init(request, response, websiteNode, configNode);
242         configuration = this.getSubNodes(configNode);
243     }
244 
245     /**
246      * {@inheritDoc}
247      */
248     @Override
249     public void drawHtml(Writer out) throws IOException {
250         String ftlPath = StringUtils.defaultIfEmpty(this.getConfigValue("path"), path);
251         drawHtml(out, ftlPath);
252     }
253 
254     /**
255      * @param out Writer
256      * @param freemarkerTemplate path for the freemarker template
257      * @throws IOException
258      */
259     protected void drawHtml(Writer out, String freemarkerTemplate) throws IOException {
260         Map parameters = new HashMap();
261         parameters.put("name", this.getName());
262         parameters.put("value", this.getValue());
263         parameters.put("values", this.getValues());
264         parameters.put("request", this.getRequest());
265         parameters.put("configuration", this.configuration);
266 
267         this.drawHtmlPre(out);
268 
269         try {
270             FreemarkerControl control = new FreemarkerControl("multiple".equals(this.getConfigValue("valueType"))
271                 ? ControlImpl.VALUETYPE_MULTIPLE
272                 : ControlImpl.VALUETYPE_SINGLE);
273             control.setType(this.getConfigValue("type", PropertyType.TYPENAME_STRING));
274             control.setName(this.getName());
275             control.drawHtml(out, freemarkerTemplate, parameters);
276         }
277 
278         catch (TemplateException ex) {
279             log.error("Error processing dialog template:", ex);
280             throw new NestableRuntimeException(ex);
281         }
282 
283         this.drawHtmlPost(out);
284     }
285 
286 }