View Javadoc
1   /**
2    * This file Copyright (c) 2011-2018 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.templating.elements;
35  
36  import info.magnolia.cms.beans.config.ServerConfiguration;
37  import info.magnolia.context.MgnlContext;
38  import info.magnolia.context.WebContext;
39  import info.magnolia.i18nsystem.I18nizer;
40  import info.magnolia.i18nsystem.LocaleProvider;
41  import info.magnolia.i18nsystem.TranslationService;
42  import info.magnolia.jcr.util.NodeTypes;
43  import info.magnolia.objectfactory.Components;
44  import info.magnolia.rendering.context.RenderingContext;
45  import info.magnolia.rendering.engine.RenderException;
46  import info.magnolia.rendering.template.TemplateDefinition;
47  import info.magnolia.templating.elements.attribute.ElementAttribute;
48  import info.magnolia.templating.module.TemplatingModule;
49  
50  import java.io.IOException;
51  import java.util.HashMap;
52  import java.util.Map;
53  import java.util.Map.Entry;
54  import java.util.Optional;
55  
56  import javax.inject.Provider;
57  import javax.jcr.Node;
58  import javax.jcr.RepositoryException;
59  
60  import org.apache.commons.lang3.StringUtils;
61  
62  /**
63   * Abstract base class for elements that operate on a specified piece of content.
64   */
65  public abstract class AbstractContentTemplatingElement extends AbstractTemplatingElement {
66  
67      // TODO should also support a JSP ContentMap
68      private Node content;
69      private String workspace;
70      private String nodeIdentifier;
71      private String path;
72      private String dialog;
73      private TemplateDefinition templateDefinition;
74      private final Map<String, Object> savedCtxAttributes = new HashMap<>();
75      private Boolean editable;
76  
77      private final Provider<TemplatingModule> templatingModule;
78      private final WebContext webContext;
79  
80      public AbstractContentTemplatingElement(ServerConfiguration server, RenderingContext renderingContext, Provider<TemplatingModule> templatingModuleProvider, WebContext webContext) {
81          super(server, renderingContext);
82          this.templatingModule = templatingModuleProvider;
83          this.webContext = webContext;
84      }
85  
86      /**
87       * @deprecated since 6.1. Use {@link #AbstractContentTemplatingElement#AbstractContentTemplatingElement(info.magnolia.cms.beans.config.ServerConfiguration, info.magnolia.rendering.context.RenderingContext, javax.inject.Provider, info.magnolia.context.WebContext)} instead.
88       */
89      @Deprecated
90      public AbstractContentTemplatingElement(ServerConfiguration server, RenderingContext renderingContext, I18nizer i18nizer) {
91          this(server, renderingContext, () -> Components.getComponent(TemplatingModule.class), Components.getComponent(WebContext.class));
92      }
93  
94      /**
95       * @deprecated since 5.4.4. The support for i18nBasename will be removed in a future version.
96       */
97      @Deprecated
98      protected String legacyTranslate(String keyOrAlreadyTranslatedValue, String i18nBasename) {
99          if (isMessageKey(keyOrAlreadyTranslatedValue)) {
100             return Components.getComponent(TranslationService.class).translate(Components.getComponent(LocaleProvider.class), i18nBasename, new String[]{keyOrAlreadyTranslatedValue});
101         } else {
102             return keyOrAlreadyTranslatedValue;
103         }
104     }
105 
106     protected boolean isMessageKey(String key) {
107         return !StringUtils.endsWith(key, ".") && StringUtils.contains(key, ".") && !StringUtils.contains(key, " ");
108     }
109 
110     protected void setPageEditorAttributes(MarkupHelper helper, String elementType) throws IOException {
111         for (Map.Entry<String, ElementAttribute> stringElementAttributeProducerEntry : templatingModule.get().getPageEditorAttributes().getOrDefault(elementType, new HashMap<>()).entrySet()) {
112             Optional<String> value = stringElementAttributeProducerEntry.getValue().getValue(this);
113             helper.attribute(stringElementAttributeProducerEntry.getKey(), value.orElse(null));
114         }
115     }
116 
117     /**
118      * @deprecated since 6.1., Use {@link info.magnolia.templating.elements.attribute.Content} instead.
119      */
120     @Deprecated
121     protected String getNodePath(Node node) throws RenderException {
122         try {
123             return node.getSession().getWorkspace().getName() + ":" + node.getPath();
124         } catch (RepositoryException e) {
125             throw new RenderException("Can't construct node path for node " + node);
126         }
127     }
128 
129     /**
130      * Returns the content passed to the element (content or workspace/path attribute) or null if nothing was passed.
131      */
132     protected Node getPassedContent() throws RenderException {
133 
134         // TODO should we default workspace to 'website' ?
135         // TODO should we be strict and fail on invalid combinations ?
136 
137         // TODO we can safely keep the node around after we've resolved it
138 
139         if (content != null) {
140             return content;
141         }
142         if (StringUtils.isNotEmpty(workspace)) {
143             if (StringUtils.isNotEmpty(nodeIdentifier)) {
144                 try {
145                     return MgnlContext.getJCRSession(workspace).getNodeByIdentifier(nodeIdentifier);
146                 } catch (RepositoryException e) {
147                     throw new RenderException("Can't read content from workspace [" + workspace + "] with identifier [" + nodeIdentifier + "].");
148                 }
149             }
150             if (StringUtils.isNotEmpty(path)) {
151                 try {
152                     return MgnlContext.getJCRSession(workspace).getNode(path);
153                 } catch (RepositoryException e) {
154                     throw new RenderException("Can't read content from workspace [" + workspace + "] with path [" + path + "].");
155                 }
156             }
157             throw new IllegalArgumentException("Need to specify either uuid or path in combination with workspace");
158         }
159 
160         return null;
161     }
162 
163     public Node getContent() {
164         return content;
165     }
166 
167     public void setContent(Node node) {
168         this.content = node;
169     }
170 
171     public String getWorkspace() {
172         return workspace;
173     }
174 
175     public void setWorkspace(String workspace) {
176         this.workspace = workspace;
177     }
178 
179     public String getNodeIdentifier() {
180         return nodeIdentifier;
181     }
182 
183     public void setNodeIdentifier(String nodeIdentifier) {
184         this.nodeIdentifier = nodeIdentifier;
185     }
186 
187     public String getPath() {
188         return path;
189     }
190 
191     public void setPath(String path) {
192         this.path = path;
193     }
194 
195     /**
196      * Sets attributes in web context under the specified scope. If an attribute already exists its value will be overwritten
197      * with the new one and the old value saved for subsequent restore.
198      *
199      * @param scope one of {@link info.magnolia.context.Context#APPLICATION_SCOPE} {@link info.magnolia.context.Context#SESSION_SCOPE} {@link info.magnolia.context.Context#LOCAL_SCOPE}.
200      */
201     protected void setAttributesInWebContext(final Map<String, Object> attributes, int scope) {
202         if (attributes == null) {
203             return;
204         }
205         switch (scope) {
206         case WebContext.APPLICATION_SCOPE:
207         case WebContext.SESSION_SCOPE:
208         case WebContext.LOCAL_SCOPE:
209             break;
210         default:
211             throw new IllegalArgumentException("Scope is not valid. Use one of the scopes defined in info.magnolia.context.WebContext");
212         }
213         for (Entry<String, Object> entry : attributes.entrySet()) {
214             final String key = entry.getKey();
215             if (webContext.containsKey(key)) {
216                 savedCtxAttributes.put(key, webContext.get(key));
217             }
218             webContext.setAttribute(key, entry.getValue(), scope);
219         }
220     }
221 
222     /**
223      * Restores the original values of attributes in web context under the specified scope.
224      *
225      * @param scope one of {@link info.magnolia.context.Context#APPLICATION_SCOPE} {@link info.magnolia.context.Context#SESSION_SCOPE} {@link info.magnolia.context.Context#LOCAL_SCOPE}.
226      */
227     protected void restoreAttributesInWebContext(final Map<String, Object> attributes, int scope) {
228         if (attributes == null) {
229             return;
230         }
231         switch (scope) {
232         case WebContext.APPLICATION_SCOPE:
233         case WebContext.SESSION_SCOPE:
234         case WebContext.LOCAL_SCOPE:
235             break;
236         default:
237             throw new IllegalArgumentException("Scope is not valid. Use one of the scopes defined in info.magnolia.context.WebContext");
238         }
239         for (Entry<String, Object> entry : attributes.entrySet()) {
240             final String key = entry.getKey();
241             if (webContext.containsKey(key) && savedCtxAttributes.get(key) != null) {
242                 webContext.setAttribute(key, savedCtxAttributes.get(key), scope);
243                 continue;
244             }
245             webContext.removeAttribute(key, scope);
246         }
247     }
248 
249     /**
250      * Override to set conditions for rendering of cms:comments.
251      */
252     protected boolean renderComments() {
253         return isAdmin();
254     }
255 
256     /**
257      * @deprecated since 6.1. Use {@link info.magnolia.templating.elements.attribute.ActivationStatus} instead.
258      */
259     @Deprecated
260     protected String getActivationStatus(Node node) {
261         int status = NodeTypes.Activatable.ACTIVATION_STATUS_NOT_ACTIVATED;
262 
263         if (node != null) {
264             try {
265                 status = NodeTypes.Activatable.getActivationStatus(node);
266             } catch (RepositoryException e) {
267                 // page has no activation status
268             }
269         }
270         return String.valueOf(status);
271     }
272 
273     protected WebContext getWebContext() {
274         return webContext;
275     }
276 
277     public void setDialog(String dialog) {
278         this.dialog = dialog;
279     }
280 
281     public String getDialog() {
282         return dialog;
283     }
284 
285     public void setTemplateDefinition(TemplateDefinition templateDefinition) {
286         this.templateDefinition = templateDefinition;
287     }
288 
289     public TemplateDefinition getTemplateDefinition() {
290         return templateDefinition;
291     }
292 
293     public Boolean getEditable() {
294         return editable;
295     }
296 
297     public void setEditable(Boolean editable) {
298         this.editable = editable;
299     }
300 
301 }