1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.templating.elements;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.cms.security.User;
38 import info.magnolia.cms.security.operations.OperationPermissionDefinition;
39 import info.magnolia.context.WebContext;
40 import info.magnolia.i18nsystem.I18nizer;
41 import info.magnolia.jcr.inheritance.InheritanceNodeWrapper;
42 import info.magnolia.objectfactory.Components;
43 import info.magnolia.registry.RegistrationException;
44 import info.magnolia.rendering.context.RenderingContext;
45 import info.magnolia.rendering.engine.AppendableOnlyOutputProvider;
46 import info.magnolia.rendering.engine.RenderException;
47 import info.magnolia.rendering.engine.RenderingEngine;
48 import info.magnolia.rendering.template.AreaDefinition;
49 import info.magnolia.rendering.template.ComponentAvailability;
50 import info.magnolia.rendering.template.RenderableDefinition;
51 import info.magnolia.rendering.template.TemplateDefinition;
52 import info.magnolia.rendering.template.assignment.TemplateDefinitionAssignment;
53 import info.magnolia.templating.freemarker.AreaDirective;
54
55 import java.io.IOException;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.Map;
59
60 import javax.inject.Inject;
61 import javax.jcr.Node;
62 import javax.jcr.RepositoryException;
63 import javax.jcr.Session;
64
65 import org.apache.commons.lang3.StringUtils;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69
70
71
72 public class ComponentElement extends AbstractContentTemplatingElement {
73
74 private static final Logger log = LoggerFactory.getLogger(ComponentElement.class);
75
76 private Map<String, Object> contextAttributes = new HashMap<String, Object>();
77 private Node content;
78 private TemplateDefinition componentDefinition;
79
80 private boolean renderEditbar = true;
81 private String dialog;
82 private Boolean editable;
83 private Boolean moveable, writable, deletable;
84
85 private final RenderingEngine renderingEngine;
86 private final TemplateDefinitionAssignment templateDefinitionAssignment;
87 private final WebContext webContext;
88
89 @Inject
90 public ComponentElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, TemplateDefinitionAssignment templateDefinitionAssignment, I18nizer i18nizer, WebContext webContext) {
91 super(server, renderingContext, i18nizer);
92 this.renderingEngine = renderingEngine;
93 this.templateDefinitionAssignment = templateDefinitionAssignment;
94 this.webContext = webContext;
95 }
96
97
98
99
100 @Deprecated
101 public ComponentElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, TemplateDefinitionAssignment templateDefinitionAssignment, I18nizer i18nizer) {
102 this(server, renderingContext, renderingEngine, templateDefinitionAssignment, i18nizer, Components.getComponent(WebContext.class));
103 }
104
105
106
107
108 @Deprecated
109 public ComponentElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, TemplateDefinitionAssignment templateDefinitionAssignment) {
110 this(server, renderingContext, renderingEngine, templateDefinitionAssignment, Components.getComponent(I18nizer.class));
111 }
112
113 @Override
114 public void begin(Appendable out) throws IOException, RenderException {
115
116 content = getPassedContent();
117
118 if (content == null) {
119 throw new RenderException("The 'content' or 'workspace' and 'path' attribute have to be set to render a component.");
120 }
121
122 if (renderComments()) {
123
124 try {
125 this.componentDefinition = templateDefinitionAssignment.getAssignedTemplateDefinition(content);
126 } catch (RegistrationException e) {
127 throw new RenderException("No template definition found for the current content", e);
128 }
129
130 MarkupHelper helper = new MarkupHelper(out);
131
132 helper.openComment("cms:component");
133 setPageEditorAttributes(helper, content);
134 helper.append(" -->\n");
135 }
136
137
138
139 webContext.push(webContext.getRequest(), webContext.getResponse());
140 setAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
141
142 try {
143 if (componentDefinition != null) {
144 renderingEngine.render(content, componentDefinition, new HashMap<String, Object>(), new AppendableOnlyOutputProvider(out));
145 } else {
146 renderingEngine.render(content, new AppendableOnlyOutputProvider(out));
147 }
148 } finally {
149 webContext.pop();
150 restoreAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
151 }
152 }
153
154 protected void setPageEditorAttributes(MarkupHelper helper, Node node) throws IOException, RenderException {
155 helper.attribute(AreaDirective.CONTENT_ATTRIBUTE, getNodePath(content));
156
157 if (content instanceof InheritanceNodeWrapper) {
158 if (((InheritanceNodeWrapper) content).isInherited()) {
159 helper.attribute("inherited", "true");
160 }
161 }
162
163 this.editable = resolveEditable();
164 if (this.editable != null) {
165 helper.attribute("editable", String.valueOf(this.editable));
166 }
167
168 final AreaDefinition areaDefinition = this.getRenderingContext().getParentAreaDefinition();
169 OperationPermissionDefinition permissions = null;
170 User user = null;
171
172 if (areaDefinition != null) {
173 String componentId = componentDefinition.getId();
174 ComponentAvailability componentAvailability = null;
175 Iterator<ComponentAvailability> iterator = areaDefinition.getAvailableComponents().values().iterator();
176 while (iterator.hasNext()) {
177 ComponentAvailability availability = iterator.next();
178 if (availability.getId().equals(componentId)) {
179 componentAvailability = availability;
180 break;
181 }
182 }
183 if (componentAvailability != null && componentAvailability.getPermissions() != null) {
184 permissions = componentAvailability.getPermissions();
185 user = webContext.getUser();
186 }
187 }
188
189 this.moveable = this.resolveMoveable(permissions, user);
190 if (this.moveable != null) {
191 helper.attribute(OperationPermissionDefinition.MOVEABLE, String.valueOf(this.moveable));
192 }
193
194 this.writable = this.resolveWritable(permissions, user);
195 if (this.writable != null) {
196 helper.attribute(OperationPermissionDefinition.WRITABLE, String.valueOf(this.writable));
197 }
198
199 this.deletable = this.resolveDeletable(permissions, user);
200 if (this.deletable != null) {
201 helper.attribute(OperationPermissionDefinition.DELETABLE, String.valueOf(this.deletable));
202 }
203
204 if (StringUtils.isEmpty(dialog)) {
205 dialog = resolveDialog();
206 }
207 helper.attribute("dialog", dialog);
208
209 RenderableDefinition i18nizedDefinition = this.i18nize(componentDefinition);
210 String label = i18nizedDefinition.getTitle();
211
212
213 final String i18nBasename = StringUtils.isNotEmpty(componentDefinition.getI18nBasename()) ? componentDefinition.getI18nBasename() : (areaDefinition == null ? null : areaDefinition.getI18nBasename());
214 label = this.legacyTranslate(label, i18nBasename);
215
216 if (label == null || isMessageKey(label)) {
217 label = componentDefinition.getName();
218 }
219 helper.attribute("label", label);
220
221
222 String description = i18nizedDefinition.getDescription();
223 description = this.legacyTranslate(description, i18nBasename);
224
225 if (!isMessageKey(i18nizedDefinition.getDescription())) {
226 helper.attribute("description", description);
227 }
228
229 helper.attribute("activationStatus", getActivationStatus(content));
230 }
231
232 private boolean hasPermission(Node node) {
233 try {
234 return node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY);
235 } catch (RepositoryException e) {
236 log.error("Could not determine permission for node {}", node);
237 }
238 return false;
239 }
240
241 private Boolean resolveEditable() {
242 return editable != null ? editable : componentDefinition != null && componentDefinition.getEditable() != null ? componentDefinition.getEditable() : null;
243 }
244
245 private Boolean resolveMoveable(final OperationPermissionDefinition permissions, User user) {
246
247 Boolean moveable = this.moveable != null ? this.moveable : componentDefinition != null && componentDefinition.getMoveable() != null ? componentDefinition.getMoveable() : null;
248 if (moveable != null || permissions == null) {
249 return moveable;
250 }
251
252
253 if (!permissions.canMove(user)) {
254 return false;
255 }
256 return true;
257 }
258
259 private Boolean resolveWritable(final OperationPermissionDefinition permissions, User user) {
260
261 Boolean writable = this.writable != null ? this.writable : componentDefinition != null && componentDefinition.getWritable() != null ? componentDefinition.getWritable() : null;
262 if (writable != null || permissions == null) {
263 return writable;
264 }
265
266 if (!permissions.canWrite(user)) {
267 return false;
268 }
269 return true;
270 }
271
272 private Boolean resolveDeletable(final OperationPermissionDefinition permissions, User user) {
273
274 Boolean deletable = this.deletable != null ? this.deletable : componentDefinition != null && componentDefinition.getDeletable() != null ? componentDefinition.getDeletable() : null;
275 if (deletable != null || permissions == null) {
276 return deletable;
277 }
278
279 if (!permissions.canDelete(user)) {
280 return false;
281 }
282 return true;
283 }
284
285 @Override
286 public void end(Appendable out) throws IOException, RenderException {
287 if (renderComments()) {
288 new MarkupHelper(out).closeComment("cms:component");
289 }
290 }
291
292 public Map<String, Object> getContextAttributes() {
293 return contextAttributes;
294 }
295
296 public void setContextAttributes(Map<String, Object> contextAttributes) {
297 this.contextAttributes = contextAttributes;
298 }
299
300 private String resolveDialog() {
301 if (StringUtils.isNotEmpty(this.dialog)) {
302 return this.dialog;
303 }
304 String dialog = componentDefinition.getDialog();
305 if (StringUtils.isNotEmpty(dialog)) {
306 return dialog;
307 }
308 return null;
309 }
310
311 public void setDialog(String dialog) {
312 this.dialog = dialog;
313 }
314
315 public void setEditable(Boolean editable) {
316 this.editable = editable;
317 }
318
319 public Boolean getEditable() {
320 return editable;
321 }
322
323 public boolean isRenderEditbar() {
324 return renderEditbar;
325 }
326
327 public void setRenderEditbar(boolean renderEditbar) {
328 this.renderEditbar = renderEditbar;
329 }
330
331 @Override
332 protected boolean renderComments() {
333 return this.renderEditbar && isAdmin() && !webContext.getAggregationState().isPreviewMode() && hasPermission(this.content);
334 }
335
336 protected WebContext getWebContext() {
337 return webContext;
338 }
339 }