001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.verify;
016    
017    import com.liferay.portal.kernel.dao.orm.QueryUtil;
018    import com.liferay.portal.kernel.json.JSONFactoryUtil;
019    import com.liferay.portal.kernel.json.JSONObject;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.repository.model.FileEntry;
023    import com.liferay.portal.kernel.repository.model.FileVersion;
024    import com.liferay.portal.kernel.repository.model.Folder;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.GetterUtil;
027    import com.liferay.portal.kernel.util.MimeTypesUtil;
028    import com.liferay.portal.kernel.util.StringBundler;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.Validator;
031    import com.liferay.portal.kernel.xml.Attribute;
032    import com.liferay.portal.kernel.xml.Document;
033    import com.liferay.portal.kernel.xml.Element;
034    import com.liferay.portal.kernel.xml.Node;
035    import com.liferay.portal.kernel.xml.SAXReaderUtil;
036    import com.liferay.portal.kernel.xml.XPath;
037    import com.liferay.portal.model.AuditedModel;
038    import com.liferay.portal.model.BaseModel;
039    import com.liferay.portal.model.CompanyConstants;
040    import com.liferay.portal.model.User;
041    import com.liferay.portal.service.ServiceContext;
042    import com.liferay.portal.service.UserLocalServiceUtil;
043    import com.liferay.portal.util.PortalUtil;
044    import com.liferay.portlet.documentlibrary.NoSuchFolderException;
045    import com.liferay.portlet.documentlibrary.model.DLFileEntryMetadata;
046    import com.liferay.portlet.documentlibrary.model.DLFileVersion;
047    import com.liferay.portlet.documentlibrary.model.DLFolderConstants;
048    import com.liferay.portlet.documentlibrary.model.DLSyncConstants;
049    import com.liferay.portlet.documentlibrary.service.DLAppLocalServiceUtil;
050    import com.liferay.portlet.documentlibrary.service.DLFileEntryLocalServiceUtil;
051    import com.liferay.portlet.documentlibrary.service.DLFileEntryMetadataLocalServiceUtil;
052    import com.liferay.portlet.documentlibrary.store.DLStoreUtil;
053    import com.liferay.portlet.dynamicdatalists.model.DDLRecord;
054    import com.liferay.portlet.dynamicdatalists.model.DDLRecordModel;
055    import com.liferay.portlet.dynamicdatalists.model.DDLRecordSet;
056    import com.liferay.portlet.dynamicdatalists.model.DDLRecordVersion;
057    import com.liferay.portlet.dynamicdatalists.service.DDLRecordLocalServiceUtil;
058    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
059    import com.liferay.portlet.dynamicdatamapping.model.DDMStructureLink;
060    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
061    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
062    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLinkLocalServiceUtil;
063    import com.liferay.portlet.dynamicdatamapping.service.DDMStructureLocalServiceUtil;
064    import com.liferay.portlet.dynamicdatamapping.service.DDMTemplateLocalServiceUtil;
065    import com.liferay.portlet.dynamicdatamapping.storage.Field;
066    import com.liferay.portlet.dynamicdatamapping.storage.FieldConstants;
067    import com.liferay.portlet.dynamicdatamapping.storage.Fields;
068    import com.liferay.portlet.dynamicdatamapping.storage.StorageEngineUtil;
069    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
070    
071    import java.io.File;
072    import java.io.Serializable;
073    
074    import java.util.HashMap;
075    import java.util.HashSet;
076    import java.util.List;
077    import java.util.Map;
078    import java.util.Set;
079    
080    /**
081     * @author Marcellus Tavares
082     */
083    public class VerifyDynamicDataMapping extends VerifyProcess {
084    
085            protected FileEntry addFileEntry(
086                            long companyId, long userId, long groupId, long folderId,
087                            String fileName, String filePath, int status)
088                    throws Exception {
089    
090                    String contentType = MimeTypesUtil.getContentType(fileName);
091    
092                    String title = fileName;
093    
094                    int index = title.indexOf(CharPool.PERIOD);
095    
096                    if (index > 0) {
097                            title = title.substring(0, index);
098                    }
099    
100                    try {
101                            File file = DLStoreUtil.getFile(
102                                    companyId, CompanyConstants.SYSTEM, filePath);
103    
104                            ServiceContext serviceContext = createServiceContext();
105    
106                            FileEntry fileEntry = DLAppLocalServiceUtil.addFileEntry(
107                                    userId, groupId, folderId, fileName, contentType, title,
108                                    StringPool.BLANK, StringPool.BLANK, file, serviceContext);
109    
110                            updateFileEntryStatus(fileEntry, status, serviceContext);
111    
112                            return fileEntry;
113                    }
114                    catch (Exception e) {
115                            if (_log.isWarnEnabled()) {
116                                    _log.warn("Unable to add file entry " + fileName, e);
117                            }
118    
119                            return null;
120                    }
121            }
122    
123            protected Folder addFolder(
124                            long userId, long groupId, long primaryKey, String fieldName)
125                    throws Exception {
126    
127                    Folder ddmFolder = addFolder(
128                            userId, groupId, DLFolderConstants.DEFAULT_PARENT_FOLDER_ID, "DDM",
129                            StringPool.BLANK);
130    
131                    Folder primaryKeyFolder = addFolder(
132                            userId, groupId, ddmFolder.getFolderId(),
133                            String.valueOf(primaryKey), StringPool.BLANK);
134    
135                    return addFolder(
136                            userId, groupId, primaryKeyFolder.getFolderId(), fieldName,
137                            StringPool.BLANK);
138            }
139    
140            protected Folder addFolder(
141                            long userId, long groupId, long parentFolderId, String name,
142                            String description)
143                    throws Exception {
144    
145                    try {
146                            return DLAppLocalServiceUtil.getFolder(
147                                    groupId, parentFolderId, name);
148                    }
149                    catch (NoSuchFolderException nsfe) {
150                            return DLAppLocalServiceUtil.addFolder(
151                                    userId, groupId, parentFolderId, name, description,
152                                    createServiceContext());
153                    }
154            }
155    
156            protected boolean checkDuplicateNames(DDMStructure structure)
157                    throws Exception {
158    
159                    String xml =
160                            "<root>" + getFullStructureXML(structure, StringPool.BLANK) +
161                                    "</root>";
162    
163                    Document document = SAXReaderUtil.read(xml);
164    
165                    Set<String> duplicateElementNames =
166                            getDuplicateElementNames(
167                                    document.getRootElement(), new HashSet<String>(),
168                                    new HashSet<String>());
169    
170                    if (duplicateElementNames.isEmpty()) {
171                            return false;
172                    }
173    
174                    if (!_log.isWarnEnabled()) {
175                            return true;
176                    }
177    
178                    StringBundler sb = new StringBundler(
179                            duplicateElementNames.size() * 2 + 7);
180    
181                    sb.append("Structure with class name ID ");
182                    sb.append(structure.getClassNameId());
183                    sb.append(" and structure key = ");
184                    sb.append(structure.getStructureKey());
185                    sb.append(" contains more than one element that is identified by the ");
186                    sb.append("same name either within itself or within any of its ");
187                    sb.append("parent structures. The duplicate element names are: ");
188    
189                    for (String duplicateElementName : duplicateElementNames) {
190                            sb.append(duplicateElementName);
191                            sb.append(StringPool.COMMA_AND_SPACE);
192                    }
193    
194                    sb.setIndex(sb.index() - 1);
195    
196                    _log.warn(sb.toString());
197    
198                    return true;
199            }
200    
201            protected boolean createDefaultMetadataElement(
202                    Element dynamicElementElement, String defaultLanguageId) {
203    
204                    boolean hasDefaultMetadataElement = hasDefaultMetadataElement(
205                            dynamicElementElement, defaultLanguageId);
206    
207                    if (hasDefaultMetadataElement) {
208                            return false;
209                    }
210    
211                    Element metadataElement = dynamicElementElement.addElement("meta-data");
212    
213                    metadataElement.addAttribute("locale", defaultLanguageId);
214    
215                    Element entryElement = metadataElement.addElement("entry");
216    
217                    entryElement.addAttribute("name", "label");
218                    entryElement.addCDATA(StringPool.BLANK);
219    
220                    return true;
221            }
222    
223            protected ServiceContext createServiceContext() {
224                    ServiceContext serviceContext = new ServiceContext();
225    
226                    serviceContext.setAddGroupPermissions(true);
227                    serviceContext.setAddGuestPermissions(true);
228    
229                    return serviceContext;
230            }
231    
232            @Override
233            protected void doVerify() throws Exception {
234                    setUpClassNameIds();
235    
236                    List<DDMStructure> structures =
237                            DDMStructureLocalServiceUtil.getStructures();
238    
239                    for (DDMStructure structure : structures) {
240                            if (checkDuplicateNames(structure)) {
241                                    throw new VerifyException(
242                                            "Duplicate element name found in structures");
243                            }
244                    }
245    
246                    for (DDMStructure structure : structures) {
247                            verifyStructure(structure);
248    
249                            updateFileUploadReferences(structure);
250                    }
251            }
252    
253            protected Set<String> getDuplicateElementNames(
254                    Element element, Set<String> elementNames,
255                    Set<String> duplicateElementNames) {
256    
257                    String elementName = element.attributeValue("name");
258    
259                    if (!elementNames.add(elementName)) {
260                            duplicateElementNames.add(elementName);
261                    }
262    
263                    List<Element> dynamicElements = element.elements("dynamic-element");
264    
265                    for (Element dynamicElement : dynamicElements) {
266                            duplicateElementNames = getDuplicateElementNames(
267                                    dynamicElement, elementNames, duplicateElementNames);
268                    }
269    
270                    return duplicateElementNames;
271            }
272    
273            protected String getFileUploadPath(BaseModel<?> baseModel)
274                    throws Exception {
275    
276                    StringBundler sb = new StringBundler(7);
277    
278                    long primaryKey = 0;
279    
280                    String version = StringPool.BLANK;
281    
282                    if (baseModel instanceof DDLRecordModel) {
283                            DDLRecord ddlRecord = (DDLRecord)baseModel;
284    
285                            primaryKey = ddlRecord.getPrimaryKey();
286    
287                            DDLRecordVersion ddlRecordVersion = ddlRecord.getRecordVersion();
288    
289                            version = ddlRecordVersion.getVersion();
290                    }
291                    else {
292                            DLFileEntryMetadata dlFileEntryMetadata =
293                                    (DLFileEntryMetadata)baseModel;
294    
295                            primaryKey = dlFileEntryMetadata.getPrimaryKey();
296    
297                            DLFileVersion dlFileVersion = dlFileEntryMetadata.getFileVersion();
298    
299                            version = dlFileVersion.getVersion();
300                    }
301    
302                    sb.append("ddm");
303                    sb.append(StringPool.SLASH);
304                    sb.append(baseModel.getModelClassName());
305                    sb.append(StringPool.SLASH);
306                    sb.append(primaryKey);
307                    sb.append(StringPool.SLASH);
308                    sb.append(version);
309    
310                    return sb.toString();
311            }
312    
313            protected String getFullStructureXML(DDMStructure structure, String xml)
314                    throws Exception {
315    
316                    if (structure.getParentStructureId() != 0) {
317                            DDMStructure parentStructure =
318                                    DDMStructureLocalServiceUtil.getStructure(
319                                            structure.getParentStructureId());
320    
321                            xml = getFullStructureXML(parentStructure, xml);
322                    }
323    
324                    Document document = SAXReaderUtil.read(structure.getXsd());
325    
326                    Element rootElement = document.getRootElement();
327    
328                    List<Element> dynamicElements = rootElement.elements("dynamic-element");
329    
330                    for (Element dynamicElement : dynamicElements) {
331                            xml += dynamicElement.asXML();
332                    }
333    
334                    return xml;
335            }
336    
337            protected String getJSON(FileEntry fileEntry) {
338                    JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
339    
340                    jsonObject.put("groupId", fileEntry.getGroupId());
341                    jsonObject.put("uuid", fileEntry.getUuid());
342    
343                    return jsonObject.toString();
344            }
345    
346            protected long getUserId(AuditedModel auditedModel) throws Exception {
347                    User user = UserLocalServiceUtil.fetchUser(auditedModel.getUserId());
348    
349                    if (user != null) {
350                            return user.getUserId();
351                    }
352    
353                    User defaultUser = UserLocalServiceUtil.getDefaultUser(
354                            auditedModel.getCompanyId());
355    
356                    if (_log.isWarnEnabled()) {
357                            _log.warn(
358                                    "Using default user " + defaultUser.getUserId() +
359                                            " for audited model " + auditedModel.getModelClassName() +
360                                                    " with primary key " + auditedModel.getPrimaryKeyObj());
361                    }
362    
363                    return defaultUser.getUserId();
364            }
365    
366            protected boolean hasDefaultMetadataElement(
367                    Element dynamicElementElement, String defaultLanguageId) {
368    
369                    List<Element> metadataElements = dynamicElementElement.elements(
370                            "meta-data");
371    
372                    for (Element metadataElement : metadataElements) {
373                            String languageId = metadataElement.attributeValue("locale");
374    
375                            if (languageId.equals(defaultLanguageId)) {
376                                    return true;
377                            }
378                    }
379    
380                    return false;
381            }
382    
383            protected boolean hasFileUploadFields(DDMStructure structure)
384                    throws Exception {
385    
386                    Map<String, Map<String, String>> fieldsMap = structure.getFieldsMap();
387    
388                    for (Map<String, String> field : fieldsMap.values()) {
389                            String dataType = field.get(FieldConstants.DATA_TYPE);
390    
391                            if (dataType.equals("file-upload")) {
392                                    return true;
393                            }
394                    }
395    
396                    return false;
397            }
398    
399            protected void setUpClassNameIds() {
400                    _ddlRecordSetClassNameId = PortalUtil.getClassNameId(
401                            DDLRecordSet.class);
402                    _ddmStructureClassNameId = PortalUtil.getClassNameId(
403                            DDMStructure.class);
404                    _dlFileEntryMetadataClassNameId = PortalUtil.getClassNameId(
405                            DLFileEntryMetadata.class);
406            }
407    
408            protected void updateDDLFileUploadReferences(long ddlRecordSetId)
409                    throws Exception {
410    
411                    List<DDLRecord> ddlRecords = DDLRecordLocalServiceUtil.getRecords(
412                            ddlRecordSetId);
413    
414                    for (DDLRecord ddlRecord : ddlRecords) {
415                            updateFileUploadReferences(
416                                    ddlRecord.getCompanyId(), ddlRecord.getDDMStorageId(),
417                                    getUserId(ddlRecord), ddlRecord.getGroupId(), ddlRecord,
418                                    ddlRecord.getStatus());
419                    }
420            }
421    
422            protected void updateDLFileUploadReferences(long dlFileEntryMetadataId)
423                    throws Exception {
424    
425                    DLFileEntryMetadata dlFileEntryMetadata =
426                            DLFileEntryMetadataLocalServiceUtil.getFileEntryMetadata(
427                                    dlFileEntryMetadataId);
428    
429                    FileEntry fileEntry = DLAppLocalServiceUtil.getFileEntry(
430                            dlFileEntryMetadata.getFileEntryId());
431    
432                    FileVersion fileVersion = fileEntry.getFileVersion();
433    
434                    updateFileUploadReferences(
435                            fileEntry.getCompanyId(), dlFileEntryMetadata.getDDMStorageId(),
436                            getUserId(fileEntry), fileEntry.getGroupId(), dlFileEntryMetadata,
437                            fileVersion.getStatus());
438            }
439    
440            protected void updateFieldValues(
441                            long storageId, Map<String, String> fieldValues)
442                    throws Exception {
443    
444                    Fields fields = new Fields();
445    
446                    for (Map.Entry<String, String> entry : fieldValues.entrySet()) {
447                            Field field = new Field(
448                                    storageId, entry.getKey(), entry.getValue());
449    
450                            fields.put(field);
451                    }
452    
453                    ServiceContext serviceContext = new ServiceContext();
454    
455                    StorageEngineUtil.update(storageId, fields, true, serviceContext);
456            }
457    
458            protected void updateFileEntryStatus(
459                            FileEntry fileEntry, int status, ServiceContext serviceContext)
460                    throws Exception {
461    
462                    FileVersion fileVersion = fileEntry.getFileVersion();
463    
464                    Map<String, Serializable> workflowContext =
465                            new HashMap<String, Serializable>();
466    
467                    workflowContext.put("event", DLSyncConstants.EVENT_ADD);
468    
469                    DLFileEntryLocalServiceUtil.updateStatus(
470                            fileVersion.getUserId(), fileVersion.getFileVersionId(), status,
471                            workflowContext, serviceContext);
472            }
473    
474            protected void updateFileUploadReferences(DDMStructure structure)
475                    throws Exception {
476    
477                    if (!hasFileUploadFields(structure)) {
478                            return;
479                    }
480    
481                    List<DDMStructureLink> structureLinks =
482                            DDMStructureLinkLocalServiceUtil.getStructureLinks(
483                                    structure.getStructureId(), QueryUtil.ALL_POS,
484                                    QueryUtil.ALL_POS);
485    
486                    for (DDMStructureLink structureLink : structureLinks) {
487                            updateFileUploadReferences(structureLink);
488                    }
489    
490                    updateStructure(structure, updateXSD(structure.getXsd()));
491    
492                    List<DDMTemplate> templates = DDMTemplateLocalServiceUtil.getTemplates(
493                            structure.getGroupId(), _ddmStructureClassNameId,
494                            structure.getStructureId(),
495                            DDMTemplateConstants.TEMPLATE_TYPE_FORM);
496    
497                    for (DDMTemplate template : templates) {
498                            updateTemplate(template, updateXSD(template.getScript()));
499                    }
500            }
501    
502            protected void updateFileUploadReferences(DDMStructureLink structureLink)
503                    throws Exception {
504    
505                    long classNameId = structureLink.getClassNameId();
506    
507                    if (classNameId == _ddlRecordSetClassNameId) {
508                            updateDDLFileUploadReferences(structureLink.getClassPK());
509                    }
510                    else if (classNameId == _dlFileEntryMetadataClassNameId) {
511                            updateDLFileUploadReferences(structureLink.getClassPK());
512                    }
513            }
514    
515            protected void updateFileUploadReferences(
516                            long companyId, long storageId, long userId, long groupId,
517                            BaseModel<?> baseModel, int status)
518                    throws Exception {
519    
520                    Map<String, String> fieldValues = new HashMap<String, String>();
521    
522                    Fields fields = StorageEngineUtil.getFields(storageId);
523    
524                    for (Field field : fields) {
525                            String dataType = field.getDataType();
526    
527                            if (!dataType.equals("file-upload") ||
528                                    Validator.isNull(field.getValue())) {
529    
530                                    continue;
531                            }
532    
533                            long primaryKey = GetterUtil.getLong(baseModel.getPrimaryKeyObj());
534    
535                            Folder folder = addFolder(
536                                    userId, groupId, primaryKey, field.getName());
537    
538                            String valueString = String.valueOf(field.getValue());
539    
540                            JSONObject jsonObject = JSONFactoryUtil.createJSONObject(
541                                    valueString);
542    
543                            String filePath =
544                                    getFileUploadPath(baseModel) + StringPool.SLASH +
545                                            field.getName();
546    
547                            FileEntry fileEntry = addFileEntry(
548                                    companyId, userId, groupId, folder.getFolderId(),
549                                    jsonObject.getString("name"), filePath, status);
550    
551                            if (fileEntry != null) {
552                                    fieldValues.put(field.getName(), getJSON(fileEntry));
553                            }
554                    }
555    
556                    updateFieldValues(storageId, fieldValues);
557            }
558    
559            protected void updateStructure(DDMStructure structure, String xsd)
560                    throws Exception {
561    
562                    xsd = DDMXMLUtil.formatXML(xsd);
563    
564                    structure.setXsd(xsd);
565    
566                    DDMStructureLocalServiceUtil.updateDDMStructure(structure);
567            }
568    
569            protected void updateTemplate(DDMTemplate template, String script)
570                    throws Exception {
571    
572                    script = DDMXMLUtil.formatXML(script);
573    
574                    template.setScript(script);
575    
576                    DDMTemplateLocalServiceUtil.updateDDMTemplate(template);
577            }
578    
579            protected String updateXSD(String xsd) throws Exception {
580                    Document document = SAXReaderUtil.read(xsd);
581    
582                    Element rootElement = document.getRootElement();
583    
584                    List<Element> dynamicElementElements = rootElement.elements(
585                            "dynamic-element");
586    
587                    for (Element dynamicElementElement : dynamicElementElements) {
588                            updateXSDDynamicElement(dynamicElementElement);
589                    }
590    
591                    return document.asXML();
592            }
593    
594            protected void updateXSDDynamicElement(Element element) {
595                    String dataType = element.attributeValue("dataType");
596    
597                    if (Validator.equals(dataType, "file-upload")) {
598                            element.addAttribute("dataType", "document-library");
599                            element.addAttribute("type", "ddm-documentlibrary");
600                    }
601    
602                    List<Element> dynamicElementElements = element.elements(
603                            "dynamic-element");
604    
605                    for (Element dynamicElementElement : dynamicElementElements) {
606                            updateXSDDynamicElement(dynamicElementElement);
607                    }
608    
609                    Attribute attribute = element.attribute("autoGeneratedName");
610    
611                    if (attribute != null) {
612                            element.remove(attribute);
613                    }
614            }
615    
616            protected void verifyStructure(DDMStructure structure) throws Exception {
617                    boolean modified = false;
618    
619                    String defaultLanguageId = structure.getDefaultLanguageId();
620    
621                    XPath xPathSelector = SAXReaderUtil.createXPath("//dynamic-element");
622    
623                    Document document = structure.getDocument();
624    
625                    List<Node> nodes = xPathSelector.selectNodes(document);
626    
627                    for (Node node : nodes) {
628                            Element dynamicElementElement = (Element)node;
629    
630                            updateStructure(structure, updateXSD(structure.getXsd()));
631    
632                            if (createDefaultMetadataElement(
633                                            dynamicElementElement, defaultLanguageId)) {
634    
635                                    modified = true;
636                            }
637                    }
638    
639                    if (modified) {
640                            updateStructure(structure, document.asXML());
641                    }
642            }
643    
644            private static Log _log = LogFactoryUtil.getLog(
645                    VerifyDynamicDataMapping.class);
646    
647            private long _ddlRecordSetClassNameId;
648            private long _ddmStructureClassNameId;
649            private long _dlFileEntryMetadataClassNameId;
650    
651    }