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.importexport.postprocessors;
35
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.Map;
40 import java.util.Set;
41 import javax.jcr.Node;
42 import javax.jcr.NodeIterator;
43 import javax.jcr.Property;
44 import javax.jcr.PropertyIterator;
45 import javax.jcr.RepositoryException;
46
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import info.magnolia.cms.core.MetaData;
51 import info.magnolia.jcr.util.NodeTypes;
52 import info.magnolia.jcr.util.NodeUtil;
53
54
55
56
57
58
59
60
61 public class MetaDataAsMixinConversionHelper {
62
63 private static final int PERIODIC_SAVE_FREQUENCY = 20;
64 private static final String DEPRECATED_DELETION_DATE_PROPERTY_NAME = "mgnl:deletedOn";
65
66 private final Logger logger = LoggerFactory.getLogger(MetaDataAsMixinConversionHelper.class);
67
68 private final HashMap<String, String> propertyNameMapping = new HashMap<String, String>();
69
70
71
72
73 private final Set<String> propertyNamesExpectedInMetaDataAfterConversion = new HashSet<String>();
74
75 private boolean deleteMetaDataIfEmptied = false;
76 private boolean periodicSaves = false;
77
78 public MetaDataAsMixinConversionHelper() {
79 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.CREATION_DATE, NodeTypes.Created.CREATED);
80 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.LAST_ACTION, NodeTypes.Activatable.LAST_ACTIVATED);
81 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.ACTIVATOR_ID, NodeTypes.Activatable.LAST_ACTIVATED_BY);
82 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.ACTIVATED, NodeTypes.Activatable.ACTIVATION_STATUS);
83 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.TEMPLATE, NodeTypes.Renderable.TEMPLATE);
84 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.LAST_MODIFIED, NodeTypes.LastModified.LAST_MODIFIED);
85 propertyNameMapping.put(NodeTypes.MGNL_PREFIX + MetaData.AUTHOR_ID, NodeTypes.LastModified.LAST_MODIFIED_BY);
86 propertyNameMapping.put("mgnl:comment", NodeTypes.Versionable.COMMENT);
87
88
89 propertyNamesExpectedInMetaDataAfterConversion.add(NodeTypes.MGNL_PREFIX + MetaData.TITLE);
90 propertyNamesExpectedInMetaDataAfterConversion.add(NodeTypes.MGNL_PREFIX + MetaData.TEMPLATE_TYPE);
91
92
93
94 propertyNamesExpectedInMetaDataAfterConversion.addAll(propertyNameMapping.keySet());
95 }
96
97 public boolean isDeleteMetaDataIfEmptied() {
98 return deleteMetaDataIfEmptied;
99 }
100
101 public void setDeleteMetaDataIfEmptied(boolean deleteMetaDataIfEmptied) {
102 this.deleteMetaDataIfEmptied = deleteMetaDataIfEmptied;
103 }
104
105 public boolean isPeriodicSaves() {
106 return periodicSaves;
107 }
108
109
110
111
112 public void setPeriodicSaves(boolean periodicSaves) {
113 this.periodicSaves = periodicSaves;
114 }
115
116 public void convertNodeAndChildren(Node startNode) throws RepositoryException {
117
118 int nodesProcessed = 0;
119 ArrayList<Node> nodes = new ArrayList<Node>();
120 nodes.add(startNode);
121
122 while (!nodes.isEmpty()) {
123
124
125 Node node = nodes.remove(nodes.size() - 1);
126
127 processNode(node);
128
129
130 nodesProcessed++;
131 if (periodicSaves && nodesProcessed % PERIODIC_SAVE_FREQUENCY == 0) {
132 node.getSession().save();
133 }
134
135
136 NodeIterator children = node.getNodes();
137 while (children.hasNext()) {
138 Node child = children.nextNode();
139 if (!(child.getName().equals(MetaData.DEFAULT_META_NODE) && NodeUtil.isNodeType(child, NodeTypes.MetaData.NAME))) {
140 nodes.add(child);
141 }
142 }
143 }
144 }
145
146 private void processNode(Node node) throws RepositoryException {
147
148
149 if (node.hasProperty(DEPRECATED_DELETION_DATE_PROPERTY_NAME)) {
150 moveProperty(node, DEPRECATED_DELETION_DATE_PROPERTY_NAME, node, NodeTypes.Deleted.DELETED);
151 }
152
153
154 if (node.hasNode(MetaData.DEFAULT_META_NODE)) {
155 Node metaDataNode = node.getNode(MetaData.DEFAULT_META_NODE);
156 if (NodeUtil.isNodeType(metaDataNode, NodeTypes.MetaData.NAME)) {
157 moveProperties(node, metaDataNode, propertyNameMapping);
158
159 if (deleteMetaDataIfEmptied && isEmptyMetaDataNode(metaDataNode)) {
160 metaDataNode.remove();
161 }
162 }
163 }
164 }
165
166
167
168
169 private boolean isEmptyMetaDataNode(Node node) throws RepositoryException {
170 if (node.getNodes().getSize() != 0) {
171 logger.warn("MetaData node not removed because it has sub nodes {}", node.getPath());
172 return false;
173 }
174 PropertyIterator iterator = node.getProperties();
175 while (iterator.hasNext()) {
176 Property property = iterator.nextProperty();
177 if (!isExpectedMetaDataProperty(property)) {
178 logger.warn("MetaData node not removed because of unrecognized property: {}", property.getPath());
179 return false;
180 }
181 }
182 return true;
183 }
184
185
186
187
188 private boolean isExpectedMetaDataProperty(Property property) throws RepositoryException {
189 String propertyName = property.getName();
190
191
192 if (propertyName.startsWith("jcr:")) {
193 return true;
194 }
195
196
197 if (propertyName.equals(NodeTypes.MGNL_PREFIX + "Data") && property.getString().equals("MetaData")) {
198 return true;
199 }
200
201 return propertyNamesExpectedInMetaDataAfterConversion.contains(propertyName);
202 }
203
204
205
206
207
208
209
210
211 private void moveProperties(Node dstNode, Node srcNode, Map<String, String> nameMappings) throws RepositoryException {
212 for (Map.Entry<String, String> entry : nameMappings.entrySet()) {
213 String srcPropertyName = entry.getKey();
214 String dstPropertyName = entry.getValue();
215 if (!dstNode.hasProperty(dstPropertyName) && srcNode.hasProperty(srcPropertyName)) {
216 moveProperty(srcNode, srcPropertyName, dstNode, dstPropertyName);
217 }
218 }
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233 private void moveProperty(Node srcNode, String srcPropertyName, Node dstNode, String dstPropertyName) throws RepositoryException {
234 Property srcProperty = srcNode.getProperty(srcPropertyName);
235 if (srcProperty.isMultiple()) {
236 dstNode.setProperty(dstPropertyName, srcProperty.getValues());
237 } else {
238 dstNode.setProperty(dstPropertyName, srcProperty.getValue());
239 }
240 srcProperty.remove();
241 }
242 }