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.rendering.listeners;
35
36 import info.magnolia.context.MgnlContext;
37 import info.magnolia.jcr.util.NodeUtil;
38 import info.magnolia.registry.RegistrationException;
39 import info.magnolia.rendering.engine.FilteringResponseOutputProvider;
40 import info.magnolia.rendering.engine.OutputProvider;
41 import info.magnolia.rendering.template.RenderableDefinition;
42 import info.magnolia.rendering.template.TemplateDefinition;
43 import info.magnolia.rendering.template.assignment.TemplateDefinitionAssignment;
44
45 import java.util.Map;
46 import java.util.Objects;
47
48 import javax.inject.Inject;
49 import javax.jcr.Node;
50 import javax.jcr.RepositoryException;
51 import javax.servlet.http.HttpServletResponse;
52
53 import org.apache.commons.lang3.StringUtils;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57
58
59
60 public class AreaFilteringListener extends AbstractRenderingListener {
61
62 private static final Logger log = LoggerFactory.getLogger(AreaFilteringListener.class);
63
64 public static final String MGNL_AREA_PARAMETER = "mgnlArea";
65 public static final String MGNL_AREA_PATH_SEPARATOR = ",";
66
67 private boolean checkAreaExistence = true;
68 private boolean finished = false;
69 private boolean insideOfTargetArea = false;
70 private String targetArea = null;
71 private String[] targetAreaPath = null;
72 private int depth = 0;
73 private FilteringResponseOutputProvider output = null;
74
75 private TemplateDefinitionAssignment templateDefinitionAssignment;
76
77 @Inject
78 public AreaFilteringListener(TemplateDefinitionAssignment templateDefinitionAssignment) {
79 this.templateDefinitionAssignment = templateDefinitionAssignment;
80 }
81
82 @Override
83 public RenderingListenerReturnCode init(OutputProvider out, HttpServletResponse response) throws RepositoryException {
84 if (super.init(out, response) == RenderingListenerReturnCode.SKIP) {
85 return RenderingListenerReturnCode.SKIP;
86 }
87
88 if (!(out instanceof FilteringResponseOutputProvider)) {
89 throw new IllegalStateException(String.format("'%s' can be used only with '%s' but '%s' is in use. "
90 + "Use the correct provider or disable this listener (/server/renderingEngine/listeners) if you aren't not using direct area rendering",
91 this.getClass(), FilteringResponseOutputProvider.class, out.getClass()));
92 }
93 output = (FilteringResponseOutputProvider) out;
94
95 targetAreaPath = StringUtils.split((String) MgnlContext.getAttribute(MGNL_AREA_PARAMETER), MGNL_AREA_PATH_SEPARATOR);
96 if (targetAreaPath == null) {
97 return RenderingListenerReturnCode.SKIP;
98 }
99
100 targetArea = targetAreaPath[targetAreaPath.length - 1];
101 if (isCheckAreaExistence()) {
102 Node mainContentNode = MgnlContext.getAggregationState().getMainContentNode();
103 if (!areaNodeExists(mainContentNode) && !areaDefinitionExists(mainContentNode)) {
104 return RenderingListenerReturnCode.NOT_FOUND;
105 }
106 }
107 return null;
108 }
109
110 @Override
111 public RenderingListenerReturnCode before(Node content, RenderableDefinition definition, Map<String, Object> contextObjects, OutputProvider out) throws RepositoryException {
112 if (finished) {
113 return RenderingListenerReturnCode.STOP;
114 }
115
116 if (insideOfTargetArea) {
117 depth++;
118 } else if (isTargetArea(content, definition)) {
119 output.setWriteEnabled(true);
120 insideOfTargetArea = true;
121 } else {
122 output.setWriteEnabled(false);
123 if (!NodeUtil.isSame(content, MgnlContext.getAggregationState().getCurrentContentNode())) {
124 return RenderingListenerReturnCode.SKIP;
125 }
126 }
127 return null;
128 }
129
130 @Override
131 public RenderingListenerReturnCode after(Node content, RenderableDefinition definition, Map<String, Object> contextObjects, OutputProvider out) throws RepositoryException {
132 if (!insideOfTargetArea) {
133 return null;
134 }
135 if (depth == 0) {
136 output.setWriteEnabled(false);
137 insideOfTargetArea = false;
138 finished = true;
139 } else {
140 depth--;
141 }
142 return null;
143 }
144
145 @Override
146 public AreaFilteringListener copy() {
147 AreaFilteringListener copy = (AreaFilteringListener) super.copy();
148 copy.setCheckAreaExistence(isCheckAreaExistence());
149 return copy;
150 }
151
152 public boolean isCheckAreaExistence() {
153 return checkAreaExistence;
154 }
155
156 public void setCheckAreaExistence(boolean checkAreaExistence) {
157 this.checkAreaExistence = checkAreaExistence;
158 }
159
160 private boolean isTargetArea(Node content, RenderableDefinition definition) throws RepositoryException {
161 return areaAndContentHaveEqualNames(targetArea, content) || areaAndDefinitionHaveEqualNames(targetArea, definition);
162 }
163
164 private boolean areaAndDefinitionHaveEqualNames(String targetArea, RenderableDefinition definition) {
165 if (targetArea.equals(definition.getName())) {
166
167 TemplateDefinition pageTemplateDefinition = getTemplateDefinition(MgnlContext.getAggregationState().getMainContentNode());
168 String pageTemplateId = pageTemplateDefinition != null ? pageTemplateDefinition.getId() : null;
169 String currentTemplateId = definition != null ? definition.getId() : null;
170 return !Objects.equals(currentTemplateId, pageTemplateId);
171 }
172 return false;
173 }
174
175 private boolean areaAndContentHaveEqualNames(String areaName, Node content) throws RepositoryException {
176 return content != null && areaName.equals(content.getName());
177 }
178
179 private boolean areaNodeExists(Node node) throws RepositoryException {
180 if (node.hasNode(targetArea) || node.hasNode(StringUtils.join(targetAreaPath, "/"))) {
181 return true;
182 }
183 return false;
184 }
185
186 private boolean areaDefinitionExists(Node node) {
187 TemplateDefinition templateDefinition = getTemplateDefinition(node);
188 if (templateDefinition != null) {
189 for (String area : targetAreaPath) {
190 if (!templateDefinition.getAreas().containsKey(area)) {
191 return false;
192 }
193 templateDefinition = templateDefinition.getAreas().get(area);
194 }
195 return true;
196 } else {
197 return false;
198 }
199 }
200
201 private TemplateDefinition getTemplateDefinition(Node node) {
202 try {
203 return templateDefinitionAssignment.getAssignedTemplateDefinition(node);
204 } catch (RegistrationException e) {
205 log.error("Cannot get template definition for node '{}'", node, e);
206 }
207 return null;
208 }
209 }