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.spa.rest.v1;
35
36 import info.magnolia.cms.beans.config.ServerConfiguration;
37 import info.magnolia.context.WebContext;
38 import info.magnolia.jcr.predicate.AbstractPredicate;
39 import info.magnolia.jcr.util.NodeTypes;
40 import info.magnolia.jcr.util.NodeUtil;
41 import info.magnolia.registry.RegistrationException;
42 import info.magnolia.rendering.context.RenderingContext;
43 import info.magnolia.rendering.engine.OutputProvider;
44 import info.magnolia.rendering.engine.RenderException;
45 import info.magnolia.rendering.spa.renderer.AnnotationRenderingEngine;
46 import info.magnolia.rendering.template.TemplateDefinition;
47 import info.magnolia.rendering.template.assignment.TemplateDefinitionAssignment;
48 import info.magnolia.rendering.template.variation.RenderableVariationResolver;
49 import info.magnolia.repository.RepositoryConstants;
50 import info.magnolia.rest.AbstractEndpoint;
51 import info.magnolia.rest.EndpointDefinition;
52 import info.magnolia.templating.elements.AbstractContentTemplatingElement;
53 import info.magnolia.templating.elements.AreaElement;
54 import info.magnolia.templating.elements.ComponentElement;
55 import info.magnolia.templating.elements.PageElement;
56 import info.magnolia.templating.elements.TemplatingElement;
57 import info.magnolia.templating.module.TemplatingModule;
58
59 import javax.inject.Inject;
60 import javax.inject.Provider;
61 import javax.jcr.Node;
62 import javax.jcr.RepositoryException;
63 import javax.jcr.Session;
64 import javax.ws.rs.GET;
65 import javax.ws.rs.Path;
66 import javax.ws.rs.PathParam;
67 import javax.ws.rs.Produces;
68 import javax.ws.rs.core.MediaType;
69 import java.io.IOException;
70 import java.io.OutputStream;
71 import java.util.HashMap;
72 import java.util.LinkedList;
73 import java.util.Map;
74
75
76
77
78
79
80 @Path("/{path:template-annotations}/v1")
81 public class TemplateAnnotationEndpoint<D extends EndpointDefinition> extends AbstractEndpoint<D> {
82
83 private static final String OPENING_TAG = "<!-- ";
84 private static final String CLOSING_TAG = " -->\n";
85
86 private final AnnotationRenderingEngine renderingEngine;
87 private final TemplateDefinitionAssignment templateDefinitionAssignment;
88 private final Provider<TemplatingModule> templateModuleProvider;
89 private final ServerConfiguration serverConfiguration;
90 private final RenderableVariationResolver variationResolver;
91 private final Provider<WebContext> contextProvider;
92
93 @Inject
94 public TemplateAnnotationEndpoint(D endpointDefinition, AnnotationRenderingEngine renderingEngine, TemplateDefinitionAssignment templateDefinitionAssignment, Provider<TemplatingModule> templatingModuleProvider, ServerConfiguration serverConfiguration, RenderableVariationResolver variationResolver, Provider<WebContext> contextProvider) {
95 super(endpointDefinition);
96 this.renderingEngine = renderingEngine;
97 this.templateDefinitionAssignment = templateDefinitionAssignment;
98 this.templateModuleProvider = templatingModuleProvider;
99 this.serverConfiguration = serverConfiguration;
100 this.variationResolver = variationResolver;
101 this.contextProvider = contextProvider;
102 }
103
104 @Path("/{nodePath:(.+)?}")
105 @GET
106 @Produces(MediaType.APPLICATION_JSON)
107 public Map<String, String> getTemplateAnnotations(@PathParam("nodePath") String path) throws Exception {
108 Session session = contextProvider.get().getJCRSession(RepositoryConstants.WEBSITE);
109 Map<String, String> pageData = new HashMap<>();
110 Node node = session.getRootNode().getNode(path);
111 pageData.put(node.getPath(), renderAnnotations(node));
112 NodeUtil.visit(node, subNode -> pageData.put(subNode.getPath(), renderAnnotations(subNode)), new AreasAndComponentsPredicate());
113 return pageData;
114 }
115
116 private String renderAnnotations(Node node) {
117 contextProvider.get().getAggregationState().setPreviewMode(false);
118 StringBuilder outputBuilder = new StringBuilder();
119 RenderingContext renderingContext = renderingEngine.getRenderingContext();
120 try {
121 renderingContext.push(node, getTemplateDefinition(node), new OutputProvider() {
122 @Override
123 public Appendable getAppendable() {
124 return outputBuilder;
125 }
126
127 @Override
128 public OutputStream getOutputStream() {
129 return null;
130 }
131 });
132 TemplatingElement contentElement = createTemplatingElement(node);
133 contentElement.begin(outputBuilder);
134 return unwrapComment(outputBuilder.toString());
135 } catch (RepositoryException | RegistrationException | IOException | RenderException e) {
136 throw new RuntimeException("Failed to render template annotation for node " + node, e);
137 }
138 }
139
140 private String unwrapComment(String htmlComment) {
141 String annotation = htmlComment.substring(OPENING_TAG.length());
142 annotation = annotation.substring(0, annotation.indexOf(CLOSING_TAG));
143 return annotation;
144 }
145
146 private TemplateDefinition getTemplateDefinition(Node content) throws RepositoryException, RegistrationException {
147
148
149
150
151
152 Node pageNode = content;
153 LinkedList<String> parents = new LinkedList<>();
154 while (pageNode.getPrimaryNodeType().getName().equals(NodeTypes.Area.NAME)) {
155 parents.addFirst(pageNode.getName());
156 pageNode = pageNode.getParent();
157 }
158 TemplateDefinition definition = templateDefinitionAssignment.getAssignedTemplateDefinition(pageNode);
159 if (parents.size() > 0) {
160 parents.removeLast();
161 for (String parent : parents) {
162 definition = definition.getAreas().get(parent);
163 }
164 }
165 if (!content.getPrimaryNodeType().getName().equals(NodeTypes.Area.NAME) || definition.getAreas().containsKey(content.getName())) {
166 return definition;
167 } else {
168 throw new RuntimeException("Area " + content.getName() + " is not defined in template definition of " + pageNode);
169 }
170 }
171
172 private TemplatingElement createTemplatingElement(Node node) throws RepositoryException {
173 String nodeType = node.getPrimaryNodeType().getName();
174 AbstractContentTemplatingElement contentElement = null;
175 if (NodeTypes.Page.NAME.equals(nodeType)) {
176 contentElement = new PageElement(serverConfiguration, renderingEngine.getRenderingContext(), templateModuleProvider, contextProvider.get());
177 } else if (NodeTypes.Component.NAME.equals(nodeType)) {
178 contentElement = new ComponentElement(serverConfiguration, renderingEngine.getRenderingContext(), renderingEngine, templateDefinitionAssignment, contextProvider.get(), templateModuleProvider);
179 } else if (NodeTypes.Area.NAME.equals(nodeType)) {
180 contentElement = new AreaElement(serverConfiguration, renderingEngine.getRenderingContext(), renderingEngine, variationResolver, templateModuleProvider, contextProvider.get());
181 ((AreaElement) contentElement).setName(node.getName());
182 }
183 contentElement.setContent(node);
184 contentElement.setWorkspace(RepositoryConstants.WEBSITE);
185 contentElement.setNodeIdentifier(node.getIdentifier());
186 contentElement.setPath(node.getPath());
187 return contentElement;
188 }
189
190 private static class AreasAndComponentsPredicate extends AbstractPredicate<Node> {
191 @Override
192 public boolean evaluateTyped(Node node) {
193 try {
194 String nodeType = node.getPrimaryNodeType().getName();
195 return nodeType.equals(NodeTypes.Area.NAME) || nodeType.equals(NodeTypes.Component.NAME);
196 } catch (RepositoryException e) {
197 new RuntimeException("Failed to access node [" + node + "]", e);
198 }
199 return false;
200 }
201 }
202 }