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