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