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.module.resources.setup;
35
36 import info.magnolia.jcr.util.NodeTypes;
37 import info.magnolia.jcr.util.NodeUtil;
38 import info.magnolia.jcr.util.PropertyUtil;
39 import info.magnolia.module.InstallContext;
40 import info.magnolia.module.delta.AbstractRepositoryTask;
41 import info.magnolia.module.delta.Task;
42 import info.magnolia.module.delta.TaskExecutionException;
43 import info.magnolia.objectfactory.Components;
44 import info.magnolia.repository.RepositoryManager;
45 import info.magnolia.resourceloader.Resource;
46 import info.magnolia.resourceloader.ResourceOrigin;
47 import info.magnolia.resourceloader.jcr.JcrResourceOrigin;
48 import info.magnolia.resourceloader.layered.LayeredResource;
49 import info.magnolia.resourceloader.layered.LayeredResourceOrigin;
50
51 import java.io.IOException;
52 import java.io.Reader;
53 import java.util.Iterator;
54
55 import javax.inject.Provider;
56 import javax.jcr.Node;
57 import javax.jcr.Property;
58 import javax.jcr.PropertyIterator;
59 import javax.jcr.RepositoryException;
60 import javax.jcr.Session;
61
62 import org.apache.commons.io.IOUtils;
63 import org.apache.commons.lang3.ObjectUtils;
64 import org.apache.commons.lang3.StringUtils;
65 import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 public class TemplateMigrationTask extends AbstractRepositoryTask {
85
86 private static final Logger log = LoggerFactory.getLogger(TemplateMigrationTask.class);
87
88 protected static final String TEMPLATES_WORKSPACE = "templates";
89
90 protected static final String PROPERTY_AUTO_IMPORT = "autoImport";
91 protected static final String PROPERTY_TEXT = "text";
92 protected static final String PROPERTY_ENABLED = "enabled";
93
94 private final Provider<ResourceOrigin> resourceOriginProvider;
95
96 private Session templatesSession = null;
97 private Session resourcesSession = null;
98
99 private final Task onSuccess;
100
101 public TemplateMigrationTask(Task onSuccess) {
102 super("Migrates templates from 'templates' workspace", "Migrates templates from 'templates' workspace to 'resources' workspace");
103
104 this.resourceOriginProvider = new Provider<ResourceOrigin>() {
105 @Override
106 public ResourceOrigin get() {
107 return Components.getComponent(ResourceOrigin.class);
108 }
109 };
110
111 this.onSuccess = onSuccess;
112 }
113
114 public TemplateMigrationTask() {
115 this(null);
116 }
117
118 @Override
119 protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
120 final RepositoryManager repositoryManager = Components.getComponent(RepositoryManager.class);
121 String infoMessage = null;
122 if (repositoryManager.hasWorkspace(TEMPLATES_WORKSPACE)) {
123 try {
124 templatesSession = installContext.getJCRSession(TEMPLATES_WORKSPACE);
125 } catch (RepositoryException e) {
126 infoMessage = String.format("Could not get session for workspace '%s'. " +
127 "Not running template migration task.", TEMPLATES_WORKSPACE);
128 }
129 } else {
130 infoMessage = String.format("Inplace-templating might not be installed. Not running template migration task.");
131 }
132
133 if (infoMessage != null) {
134 installContext.info(infoMessage);
135 log.info(infoMessage);
136
137
138
139 }
140
141 if (templatesSession != null) {
142 resourcesSession = installContext.getJCRSession(JcrResourceOrigin.RESOURCES_WORKSPACE);
143
144 final LayeredResourceOrigin resourceOrigin = (LayeredResourceOrigin) resourceOriginProvider.get();
145
146 final Iterator<Node> nodeIterator = NodeUtil.collectAllChildren(templatesSession.getRootNode(),
147 new NodeTypePredicate(NodeTypes.Content.NAME, false)).iterator();
148
149 while (nodeIterator.hasNext()) {
150 final Node templateNode = nodeIterator.next();
151
152
153
154
155 final boolean enabled = templateNode.hasProperty(PROPERTY_ENABLED) && templateNode.getProperty(PROPERTY_ENABLED).getBoolean();
156 if (!enabled) {
157 continue;
158 }
159
160
161 final boolean autoImport = templateNode.hasProperty(PROPERTY_AUTO_IMPORT) && templateNode.getProperty(PROPERTY_AUTO_IMPORT).getBoolean();
162
163 try {
164
165 final String resourceTemplatePath = templateNode.getPath() + ".ftl";
166
167
168
169 if (autoImport) {
170 if (!resourceOrigin.hasPath(resourceTemplatePath)) {
171
172 migrateTemplate(templateNode, resourceTemplatePath);
173 }
174 } else {
175
176 if (resourceOrigin.hasPath(resourceTemplatePath)) {
177 final LayeredResource resource = resourceOrigin.getByPath(resourceTemplatePath);
178
179
180 if (resource.getFirst().getOrigin() instanceof JcrResourceOrigin) {
181
182 if (!templateEqualsResource(templateNode, resource)) {
183
184
185
186 throw new TaskExecutionException(String.format("Contents of '%s:%s' and resource '%s:%s' were not equal but autoImport was set to 'false'. " +
187 "Cannot proceed with template migration.", TEMPLATES_WORKSPACE, resourceTemplatePath,
188 JcrResourceOrigin.RESOURCES_WORKSPACE, resource.getPath()));
189 }
190
191
192 } else {
193
194 migrateTemplate(templateNode, resourceTemplatePath);
195 }
196 } else {
197
198 migrateTemplate(templateNode, resourceTemplatePath);
199 }
200 }
201
202 } catch (RepositoryException e) {
203 log.info("An error occurred when handling template '{}:{}'. Will not migrate this template.",
204 TEMPLATES_WORKSPACE, NodeUtil.getPathIfPossible(templateNode), e);
205 }
206 }
207
208 }
209
210
211
212 if (onSuccess != null) {
213 onSuccess.execute(installContext);
214 }
215 }
216
217
218
219
220
221 private boolean templateEqualsResource(final Node templateNode, final Resource resource) {
222 try (final Reader reader = resource.openReader()) {
223 final String contentResource = IOUtils.toString(reader);
224 final String contentTemplate = PropertyUtil.getString(templateNode, PROPERTY_TEXT, null);
225
226 return ObjectUtils.equals(contentResource, contentTemplate);
227 } catch (IOException e) {
228 log.error("Cannot read contents of '{}:{}'.", resource.getOrigin().getName(), resource.getName(), e);
229 }
230
231 return false;
232 }
233
234
235
236
237
238 private void migrateTemplate(final Node templateNode, final String newTemplatePath) throws RepositoryException {
239 final String parentPath = StringUtils.substringBeforeLast(newTemplatePath, "/");
240 if (StringUtils.isNotBlank(parentPath) && !resourcesSession.nodeExists(parentPath)) {
241 NodeUtil.createPath(resourcesSession.getRootNode(), parentPath, NodeTypes.Folder.NAME);
242
243 log.info("Created path '{}:{}'.", JcrResourceOrigin.RESOURCES_WORKSPACE, parentPath);
244 }
245
246 copyTemplateNode(templateNode, newTemplatePath);
247
248 log.info("Migrated template '{}:{}' to '{}:{}'.", TEMPLATES_WORKSPACE, NodeUtil.getPathIfPossible(templateNode),
249 JcrResourceOrigin.RESOURCES_WORKSPACE, newTemplatePath);
250 }
251
252
253
254
255
256
257
258
259
260
261 private void copyTemplateNode(final Node templateNode, final String newTemplatePath) throws RepositoryException {
262 final Node newTemplateNode = NodeUtil.createPath(resourcesSession.getRootNode(), newTemplatePath, NodeTypes.Content.NAME);
263 final PropertyIterator propertyIterator = templateNode.getProperties();
264
265 while (propertyIterator.hasNext()) {
266 final Property property = propertyIterator.nextProperty();
267 final String propertyName = property.getName();
268
269
270 if (PROPERTY_ENABLED.equals(propertyName)) {
271 continue;
272 }
273
274
275 if (propertyName.startsWith(NodeTypes.REP_PREFIX) || propertyName.startsWith(NodeTypes.JCR_PREFIX)) {
276 continue;
277 }
278
279
280
281 if (NodeTypes.LastModified.LAST_MODIFIED.equals(propertyName) || NodeTypes.LastModified.LAST_MODIFIED_BY.equals(propertyName)) {
282 continue;
283 }
284
285
286
287
288 if (NodeTypes.Activatable.LAST_ACTIVATED.equals(propertyName) || NodeTypes.Activatable.LAST_ACTIVATED_BY.equals(propertyName) ||
289 NodeTypes.Activatable.ACTIVATION_STATUS.equals(propertyName)) {
290 continue;
291 }
292
293 newTemplateNode.setProperty(propertyName, property.getValue());
294 }
295 }
296
297 }