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.i18n.Messages;
38 import info.magnolia.cms.i18n.MessagesManager;
39 import info.magnolia.cms.security.User;
40 import info.magnolia.cms.security.operations.OperationPermissionDefinition;
41 import info.magnolia.context.MgnlContext;
42 import info.magnolia.context.WebContext;
43 import info.magnolia.jcr.inheritance.InheritanceNodeWrapper;
44 import info.magnolia.registry.RegistrationException;
45 import info.magnolia.rendering.context.RenderingContext;
46 import info.magnolia.rendering.engine.AppendableOnlyOutputProvider;
47 import info.magnolia.rendering.engine.RenderException;
48 import info.magnolia.rendering.engine.RenderingEngine;
49 import info.magnolia.rendering.template.AreaDefinition;
50 import info.magnolia.rendering.template.ComponentAvailability;
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 final RenderingEngine renderingEngine;
78 private Node content;
79 private final TemplateDefinitionAssignment templateDefinitionAssignment;
80 private TemplateDefinition componentDefinition;
81
82 private boolean renderEditbar = true;
83 private String dialog;
84 private Boolean editable;
85 private Boolean moveable, writable, deletable;
86
87 @Inject
88 public ComponentElement(ServerConfiguration server, RenderingContext renderingContext, RenderingEngine renderingEngine, TemplateDefinitionAssignment templateDefinitionAssignment) {
89 super(server, renderingContext);
90 this.renderingEngine = renderingEngine;
91 this.templateDefinitionAssignment = templateDefinitionAssignment;
92 }
93
94 @Override
95 public void begin(Appendable out) throws IOException, RenderException {
96
97 content = getPassedContent();
98
99 if (content == null) {
100 throw new RenderException("The 'content' or 'workspace' and 'path' attribute have to be set to render a component.");
101 }
102
103 if (renderComments()) {
104
105 try {
106 this.componentDefinition = templateDefinitionAssignment.getAssignedTemplateDefinition(content);
107 } catch (RegistrationException e) {
108 throw new RenderException("No template definition found for the current content", e);
109 }
110
111 final Messages messages = MessagesManager.getMessages(componentDefinition.getI18nBasename());
112
113 MarkupHelper helper = new MarkupHelper(out);
114
115 helper.openComment("cms:component");
116
117 helper.attribute(AreaDirective.CONTENT_ATTRIBUTE, getNodePath(content));
118
119 if (content instanceof InheritanceNodeWrapper) {
120 if (((InheritanceNodeWrapper) content).isInherited()) {
121 helper.attribute("inherited", "true");
122 }
123 }
124
125 this.editable = resolveEditable();
126 if (this.editable != null) {
127 helper.attribute("editable", String.valueOf(this.editable));
128 }
129
130 final AreaDefinition areaDefinition = this.getRenderingContext().getParentAreaDefinition();
131 OperationPermissionDefinition permissions = null;
132 User user = null;
133
134 if (areaDefinition != null) {
135 String componentId = componentDefinition.getId();
136 ComponentAvailability componentAvailability = null;
137 Iterator<ComponentAvailability> iterator = areaDefinition.getAvailableComponents().values().iterator();
138 while (iterator.hasNext()) {
139 ComponentAvailability availability = iterator.next();
140 if (availability.getId().equals(componentId)) {
141 componentAvailability = availability;
142 break;
143 }
144 }
145 if (componentAvailability != null && componentAvailability.getPermissions() != null) {
146 permissions = componentAvailability.getPermissions();
147 user = MgnlContext.getUser();
148 }
149 }
150
151 this.moveable = this.resolveMoveable(permissions, user);
152 if (this.moveable != null) {
153 helper.attribute(OperationPermissionDefinition.MOVEABLE, String.valueOf(this.moveable));
154 }
155
156 this.writable = this.resolveWritable(permissions, user);
157 if (this.writable != null) {
158 helper.attribute(OperationPermissionDefinition.WRITABLE, String.valueOf(this.writable));
159 }
160
161 this.deletable = this.resolveDeletable(permissions, user);
162 if (this.deletable != null) {
163 helper.attribute(OperationPermissionDefinition.DELETABLE, String.valueOf(this.deletable));
164 }
165
166 if (StringUtils.isEmpty(dialog)) {
167 dialog = resolveDialog();
168 }
169 helper.attribute("dialog", dialog);
170
171 String label = StringUtils.defaultIfEmpty(componentDefinition.getTitle(), componentDefinition.getName());
172 helper.attribute("label", messages.getWithDefault(label, label));
173
174 if (StringUtils.isNotEmpty(componentDefinition.getDescription())) {
175 helper.attribute("description", componentDefinition.getDescription());
176 }
177
178 helper.attribute("activationStatus", getActivationStatus(content));
179
180 helper.append(" -->\n");
181 }
182
183
184
185 WebContext webContext = MgnlContext.getWebContext();
186 webContext.push(webContext.getRequest(), webContext.getResponse());
187 setAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
188
189 try {
190 if (componentDefinition != null) {
191 renderingEngine.render(content, componentDefinition, new HashMap<String, Object>(), new AppendableOnlyOutputProvider(out));
192 } else {
193 renderingEngine.render(content, new AppendableOnlyOutputProvider(out));
194 }
195 } finally {
196 webContext.pop();
197 webContext.setPageContext(null);
198 restoreAttributesInWebContext(contextAttributes, WebContext.LOCAL_SCOPE);
199 }
200 }
201
202 private boolean hasPermission(Node node) {
203 try {
204 return node.getSession().hasPermission(node.getPath(), Session.ACTION_SET_PROPERTY);
205 } catch (RepositoryException e) {
206 log.error("Could not determine permission for node {}", node);
207 }
208 return false;
209 }
210
211 private Boolean resolveEditable() {
212 return editable != null ? editable : componentDefinition != null && componentDefinition.getEditable() != null ? componentDefinition.getEditable() : null;
213 }
214
215 private Boolean resolveMoveable(final OperationPermissionDefinition permissions, User user) {
216
217 Boolean moveable = this.moveable != null ? this.moveable : componentDefinition != null && componentDefinition.getMoveable() != null ? componentDefinition.getMoveable() : null;
218 if (moveable != null || permissions == null) {
219 return moveable;
220 }
221
222
223 if (!permissions.canMove(user)) {
224 return false;
225 }
226 return true;
227 }
228
229 private Boolean resolveWritable(final OperationPermissionDefinition permissions, User user) {
230
231 Boolean writable = this.writable != null ? this.writable : componentDefinition != null && componentDefinition.getWritable() != null ? componentDefinition.getWritable() : null;
232 if (writable != null || permissions == null) {
233 return writable;
234 }
235
236 if (!permissions.canWrite(user)) {
237 return false;
238 }
239 return true;
240 }
241
242 private Boolean resolveDeletable(final OperationPermissionDefinition permissions, User user) {
243
244 Boolean deletable = this.deletable != null ? this.deletable : componentDefinition != null && componentDefinition.getDeletable() != null ? componentDefinition.getDeletable() : null;
245 if (deletable != null || permissions == null) {
246 return deletable;
247 }
248
249 if (!permissions.canDelete(user)) {
250 return false;
251 }
252 return true;
253 }
254
255 @Override
256 public void end(Appendable out) throws IOException, RenderException {
257 if (renderComments()) {
258 new MarkupHelper(out).closeComment("cms:component");
259 }
260 }
261
262 public Map<String, Object> getContextAttributes() {
263 return contextAttributes;
264 }
265
266 public void setContextAttributes(Map<String, Object> contextAttributes) {
267 this.contextAttributes = contextAttributes;
268 }
269
270 private String resolveDialog() {
271 if (StringUtils.isNotEmpty(this.dialog)) {
272 return this.dialog;
273 }
274 String dialog = componentDefinition.getDialog();
275 if (StringUtils.isNotEmpty(dialog)) {
276 return dialog;
277 }
278 return null;
279 }
280
281 public void setDialog(String dialog) {
282 this.dialog = dialog;
283 }
284
285 public void setEditable(Boolean editable) {
286 this.editable = editable;
287 }
288
289 public Boolean getEditable() {
290 return editable;
291 }
292
293 public boolean isRenderEditbar() {
294 return renderEditbar;
295 }
296
297 public void setRenderEditbar(boolean renderEditbar) {
298 this.renderEditbar = renderEditbar;
299 }
300
301 @Override
302 protected boolean renderComments() {
303 return this.renderEditbar && isAdmin() && !MgnlContext.getAggregationState().isPreviewMode() && hasPermission(this.content);
304 }
305 }