001    /**
002     * Copyright (c) 2000-2012 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.portlet.dynamicdatamapping.service.impl;
016    
017    import com.liferay.portal.LocaleException;
018    import com.liferay.portal.kernel.dao.orm.QueryUtil;
019    import com.liferay.portal.kernel.exception.PortalException;
020    import com.liferay.portal.kernel.exception.SystemException;
021    import com.liferay.portal.kernel.language.LanguageUtil;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.util.ArrayUtil;
025    import com.liferay.portal.kernel.util.CharPool;
026    import com.liferay.portal.kernel.util.LocaleUtil;
027    import com.liferay.portal.kernel.util.OrderByComparator;
028    import com.liferay.portal.kernel.util.StringPool;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.kernel.xml.Document;
031    import com.liferay.portal.kernel.xml.DocumentException;
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.ResourceConstants;
037    import com.liferay.portal.model.User;
038    import com.liferay.portal.service.ServiceContext;
039    import com.liferay.portlet.dynamicdatamapping.RequiredStructureException;
040    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateElementException;
041    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateStructureKeyException;
042    import com.liferay.portlet.dynamicdatamapping.StructureNameException;
043    import com.liferay.portlet.dynamicdatamapping.StructureXsdException;
044    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
045    import com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants;
046    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
047    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
048    import com.liferay.portlet.dynamicdatamapping.service.base.DDMStructureLocalServiceBaseImpl;
049    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
050    
051    import java.util.ArrayList;
052    import java.util.Date;
053    import java.util.HashSet;
054    import java.util.Iterator;
055    import java.util.List;
056    import java.util.Locale;
057    import java.util.Map;
058    import java.util.Set;
059    
060    /**
061     * @author Brian Wing Shun Chan
062     * @author Bruno Basto
063     * @author Marcellus Tavares
064     */
065    public class DDMStructureLocalServiceImpl
066            extends DDMStructureLocalServiceBaseImpl {
067    
068            public DDMStructure addStructure(
069                            long userId, long groupId, long classNameId, String structureKey,
070                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
071                            String xsd, String storageType, int type,
072                            ServiceContext serviceContext)
073                    throws PortalException, SystemException {
074    
075                    // Structure
076    
077                    User user = userPersistence.findByPrimaryKey(userId);
078    
079                    if (Validator.isNull(structureKey)) {
080                            structureKey = String.valueOf(counterLocalService.increment());
081                    }
082    
083                    try {
084                            xsd = DDMXMLUtil.formatXML(xsd);
085                    }
086                    catch (Exception e) {
087                            throw new StructureXsdException();
088                    }
089    
090                    Date now = new Date();
091    
092                    validate(groupId, structureKey, nameMap, xsd);
093    
094                    long structureId = counterLocalService.increment();
095    
096                    DDMStructure structure = ddmStructurePersistence.create(structureId);
097    
098                    structure.setUuid(serviceContext.getUuid());
099                    structure.setGroupId(serviceContext.getScopeGroupId());
100                    structure.setCompanyId(user.getCompanyId());
101                    structure.setUserId(user.getUserId());
102                    structure.setUserName(user.getFullName());
103                    structure.setCreateDate(serviceContext.getCreateDate(now));
104                    structure.setModifiedDate(serviceContext.getModifiedDate(now));
105                    structure.setClassNameId(classNameId);
106                    structure.setStructureKey(structureKey);
107                    structure.setNameMap(nameMap);
108                    structure.setDescriptionMap(descriptionMap);
109                    structure.setXsd(xsd);
110                    structure.setStorageType(storageType);
111                    structure.setType(type);
112    
113                    ddmStructurePersistence.update(structure, false);
114    
115                    // Resources
116    
117                    if (serviceContext.isAddGroupPermissions() ||
118                            serviceContext.isAddGuestPermissions()) {
119    
120                            addStructureResources(
121                                    structure, serviceContext.isAddGroupPermissions(),
122                                    serviceContext.isAddGuestPermissions());
123                    }
124                    else {
125                            addStructureResources(
126                                    structure, serviceContext.getGroupPermissions(),
127                                    serviceContext.getGuestPermissions());
128                    }
129    
130                    return structure;
131            }
132    
133            public void addStructureResources(
134                            DDMStructure structure, boolean addGroupPermissions,
135                            boolean addGuestPermissions)
136                    throws PortalException, SystemException {
137    
138                    resourceLocalService.addResources(
139                            structure.getCompanyId(), structure.getGroupId(),
140                            structure.getUserId(), DDMStructure.class.getName(),
141                            structure.getStructureId(), false, addGroupPermissions,
142                            addGuestPermissions);
143            }
144    
145            public void addStructureResources(
146                            DDMStructure structure, String[] groupPermissions,
147                            String[] guestPermissions)
148                    throws PortalException, SystemException {
149    
150                    resourceLocalService.addModelResources(
151                            structure.getCompanyId(), structure.getGroupId(),
152                            structure.getUserId(), DDMStructure.class.getName(),
153                            structure.getStructureId(), groupPermissions, guestPermissions);
154            }
155    
156            public DDMStructure copyStructure(
157                            long userId, long structureId, Map<Locale, String> nameMap,
158                            Map<Locale, String> descriptionMap, ServiceContext serviceContext)
159                    throws PortalException, SystemException {
160    
161                    DDMStructure structure = getStructure(structureId);
162    
163                    return addStructure(
164                            userId, structure.getGroupId(), structure.getClassNameId(), null,
165                            nameMap, descriptionMap, structure.getXsd(),
166                            structure.getStorageType(), structure.getType(), serviceContext);
167            }
168    
169            public void deleteStructure(DDMStructure structure)
170                    throws PortalException, SystemException {
171    
172                    if (ddmStructureLinkPersistence.countByStructureId(
173                                    structure.getStructureId()) > 0) {
174    
175                            throw new RequiredStructureException();
176                    }
177    
178                    // Structure
179    
180                    ddmStructurePersistence.remove(structure);
181    
182                    // Resources
183    
184                    resourceLocalService.deleteResource(
185                            structure.getCompanyId(), DDMStructure.class.getName(),
186                            ResourceConstants.SCOPE_INDIVIDUAL, structure.getStructureId());
187            }
188    
189            public void deleteStructure(long structureId)
190                    throws PortalException, SystemException {
191    
192                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
193                            structureId);
194    
195                    deleteStructure(structure);
196            }
197    
198            public void deleteStructure(long groupId, String structureKey)
199                    throws PortalException, SystemException {
200    
201                    DDMStructure structure = ddmStructurePersistence.findByG_S(
202                            groupId, structureKey);
203    
204                    deleteStructure(structure);
205            }
206    
207            public void deleteStructures(long groupId)
208                    throws PortalException, SystemException {
209    
210                    List<DDMStructure> structures = ddmStructurePersistence.findByGroupId(
211                            groupId);
212    
213                    for (DDMStructure structure : structures) {
214                            deleteStructure(structure);
215                    }
216            }
217    
218            public DDMStructure fetchStructure(long structureId)
219                    throws SystemException {
220    
221                    return ddmStructurePersistence.fetchByPrimaryKey(structureId);
222            }
223    
224            public DDMStructure fetchStructure(long groupId, String structureKey)
225                    throws SystemException {
226    
227                    return ddmStructurePersistence.fetchByG_S(groupId, structureKey);
228            }
229    
230            public List<DDMStructure> getClassStructures(long classNameId)
231                    throws SystemException {
232    
233                    return ddmStructurePersistence.findByClassNameId(classNameId);
234            }
235    
236            public List<DDMStructure> getClassStructures(
237                            long classNameId, int start, int end)
238                    throws SystemException {
239    
240                    return ddmStructurePersistence.findByClassNameId(
241                            classNameId, start, end);
242            }
243    
244            public List<DDMStructure> getClassStructures(
245                            long classNameId, OrderByComparator orderByComparator)
246                    throws SystemException {
247    
248                    return ddmStructurePersistence.findByClassNameId(
249                            classNameId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
250                            orderByComparator);
251            }
252    
253            public List<DDMStructure> getDLFileEntryTypeStructures(
254                            long dlFileEntryTypeId)
255                    throws SystemException {
256    
257                    return dlFileEntryTypePersistence.getDDMStructures(dlFileEntryTypeId);
258            }
259    
260            public DDMStructure getStructure(long structureId)
261                    throws PortalException, SystemException {
262    
263                    return ddmStructurePersistence.findByPrimaryKey(structureId);
264            }
265    
266            public DDMStructure getStructure(long groupId, String structureKey)
267                    throws PortalException, SystemException {
268    
269                    return ddmStructurePersistence.findByG_S(groupId, structureKey);
270            }
271    
272            public List<DDMStructure> getStructure(
273                            long groupId, String name, String description)
274                    throws SystemException {
275    
276                    return ddmStructurePersistence.findByG_N_D(groupId, name, description);
277            }
278    
279            /**
280             * @deprecated {@link #getStructures}
281             */
282            public List<DDMStructure> getStructureEntries() throws SystemException {
283                    return getStructures();
284            }
285    
286            /**
287             * @deprecated {@link #getStructures(long)}
288             */
289            public List<DDMStructure> getStructureEntries(long groupId)
290                    throws SystemException {
291    
292                    return getStructures(groupId);
293            }
294    
295            /**
296             * @deprecated {@link #getStructures(long, int, int)}
297             */
298            public List<DDMStructure> getStructureEntries(
299                            long groupId, int start, int end)
300                    throws SystemException {
301    
302                    return getStructures(groupId, start, end);
303            }
304    
305            public List<DDMStructure> getStructures() throws SystemException {
306                    return ddmStructurePersistence.findAll();
307            }
308    
309            public List<DDMStructure> getStructures(long groupId)
310                    throws SystemException {
311    
312                    return ddmStructurePersistence.findByGroupId(groupId);
313            }
314    
315            public List<DDMStructure> getStructures(long groupId, int start, int end)
316                    throws SystemException {
317    
318                    return ddmStructurePersistence.findByGroupId(groupId, start, end);
319            }
320    
321            public int getStructuresCount(long groupId) throws SystemException {
322                    return ddmStructurePersistence.countByGroupId(groupId);
323            }
324    
325            public List<DDMStructure> search(
326                            long companyId, long[] groupIds, long[] classNameIds,
327                            String keywords, int start, int end,
328                            OrderByComparator orderByComparator)
329                    throws SystemException {
330    
331                    return ddmStructureFinder.findByKeywords(
332                            companyId, groupIds, classNameIds, keywords, start, end,
333                            orderByComparator);
334            }
335    
336            public List<DDMStructure> search(
337                            long companyId, long[] groupIds, long[] classNameIds, String name,
338                            String description, String storageType, int type,
339                            boolean andOperator, int start, int end,
340                            OrderByComparator orderByComparator)
341                    throws SystemException {
342    
343                    return ddmStructureFinder.findByC_G_C_N_D_S_T(
344                            companyId, groupIds, classNameIds, name, description, storageType,
345                            type, andOperator, start, end, orderByComparator);
346            }
347    
348            public int searchCount(
349                            long companyId, long[] groupIds, long[] classNameIds,
350                            String keywords)
351                    throws SystemException {
352    
353                    return ddmStructureFinder.countByKeywords(
354                            companyId, groupIds, classNameIds, keywords);
355            }
356    
357            public int searchCount(
358                            long companyId, long[] groupIds, long[] classNameIds, String name,
359                            String description, String storageType, int type,
360                            boolean andOperator)
361                    throws SystemException {
362    
363                    return ddmStructureFinder.countByC_G_C_N_D_S_T(
364                            companyId, groupIds, classNameIds, name, description, storageType,
365                            type, andOperator);
366            }
367    
368            public DDMStructure updateStructure(
369                            long structureId, Map<Locale, String> nameMap,
370                            Map<Locale, String> descriptionMap, String xsd,
371                            ServiceContext serviceContext)
372                    throws PortalException, SystemException {
373    
374                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
375                            structureId);
376    
377                    return doUpdateStructure(
378                            nameMap, descriptionMap, xsd, serviceContext, structure);
379            }
380    
381            public DDMStructure updateStructure(
382                            long groupId, String structureKey, Map<Locale, String> nameMap,
383                            Map<Locale, String> descriptionMap, String xsd,
384                            ServiceContext serviceContext)
385                    throws PortalException, SystemException {
386    
387                    DDMStructure structure = ddmStructurePersistence.findByG_S(
388                            groupId, structureKey);
389    
390                    return doUpdateStructure(
391                            nameMap, descriptionMap, xsd, serviceContext, structure);
392            }
393    
394            protected void appendNewStructureRequiredFields(
395                    DDMStructure structure, Document templateDocument) {
396    
397                    String xsd = structure.getXsd();
398    
399                    Document structureDocument = null;
400    
401                    try {
402                            structureDocument = SAXReaderUtil.read(xsd);
403                    }
404                    catch (DocumentException de) {
405                            if (_log.isWarnEnabled()) {
406                                    _log.warn(de, de);
407                            }
408    
409                            return;
410                    }
411    
412                    Element templateElement = templateDocument.getRootElement();
413    
414                    XPath structureXPath = SAXReaderUtil.createXPath(
415                            "//dynamic-element[.//meta-data/entry[@name=\"required\"]=" +
416                                    "\"true\"]");
417    
418                    List<Node> nodes = structureXPath.selectNodes(structureDocument);
419    
420                    Iterator<Node> itr = nodes.iterator();
421    
422                    while (itr.hasNext()) {
423                            Element element = (Element)itr.next();
424    
425                            String name = element.attributeValue("name");
426    
427                            XPath templateXPath = SAXReaderUtil.createXPath(
428                                    "//dynamic-element[@name=\"" + name + "\"]");
429    
430                            if (!templateXPath.booleanValueOf(templateDocument)) {
431                                    templateElement.add(element.createCopy());
432                            }
433                    }
434            }
435    
436            protected DDMStructure doUpdateStructure(
437                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
438                            String xsd, ServiceContext serviceContext, DDMStructure structure)
439                    throws PortalException, SystemException {
440    
441                    try {
442                            xsd = DDMXMLUtil.formatXML(xsd);
443                    }
444                    catch (Exception e) {
445                            throw new StructureXsdException();
446                    }
447    
448                    validate(nameMap, xsd);
449    
450                    structure.setModifiedDate(serviceContext.getModifiedDate(null));
451                    structure.setNameMap(nameMap);
452                    structure.setDescriptionMap(descriptionMap);
453                    structure.setXsd(xsd);
454    
455                    ddmStructurePersistence.update(structure, false);
456    
457                    syncStructureTemplatesFields(structure);
458    
459                    return structure;
460            }
461    
462            protected void syncStructureTemplatesFields(DDMStructure structure)
463                    throws PortalException, SystemException {
464    
465                    List<DDMTemplate> templates = ddmTemplateLocalService.getTemplates(
466                            structure.getStructureId(),
467                            DDMTemplateConstants.TEMPLATE_TYPE_DETAIL);
468    
469                    for (DDMTemplate template : templates) {
470                            String script = template.getScript();
471    
472                            Document templateDocument = null;
473    
474                            try {
475                                    templateDocument = SAXReaderUtil.read(script);
476                            }
477                            catch (DocumentException de) {
478                                    if (_log.isWarnEnabled()) {
479                                            _log.warn(de, de);
480                                    }
481    
482                                    continue;
483                            }
484    
485                            Element templateRootElement = templateDocument.getRootElement();
486    
487                            syncStructureTemplatesFields(template, templateRootElement);
488    
489                            appendNewStructureRequiredFields(structure, templateDocument);
490    
491                            try {
492                                    script = DDMXMLUtil.formatXML(templateDocument.asXML());
493                            }
494                            catch (Exception e) {
495                                    throw new StructureXsdException();
496                            }
497    
498                            template.setScript(script);
499    
500                            ddmTemplatePersistence.update(template, false);
501                    }
502            }
503    
504            protected void syncStructureTemplatesFields(
505                            DDMTemplate template, Element templateElement)
506                    throws PortalException, SystemException {
507    
508                    DDMStructure structure = template.getStructure();
509    
510                    List<Element> dynamicElementElements = templateElement.elements(
511                            "dynamic-element");
512    
513                    for (Element dynamicElementElement : dynamicElementElements) {
514                            String dataType = dynamicElementElement.attributeValue("dataType");
515                            String fieldName = dynamicElementElement.attributeValue("name");
516    
517                            if (Validator.isNull(dataType)) {
518                                    continue;
519                            }
520    
521                            if (!structure.hasField(fieldName)) {
522                                    templateElement.remove(dynamicElementElement);
523    
524                                    continue;
525                            }
526    
527                            String mode = template.getMode();
528    
529                            if (mode.equals(DDMTemplateConstants.TEMPLATE_MODE_CREATE)) {
530                                    boolean fieldRequired = structure.getFieldRequired(fieldName);
531    
532                                    List<Element> metadataElements = dynamicElementElement.elements(
533                                            "meta-data");
534    
535                                    for (Element metadataElement : metadataElements) {
536                                            for (Element metadataEntryElement :
537                                                            metadataElement.elements()) {
538    
539                                                    String attributeName =
540                                                            metadataEntryElement.attributeValue("name");
541    
542                                                    if (fieldRequired && attributeName.equals("required")) {
543                                                            metadataEntryElement.setText("true");
544                                                    }
545                                            }
546                                    }
547                            }
548    
549                            syncStructureTemplatesFields(template, dynamicElementElement);
550                    }
551            }
552    
553            protected void validate(List<Element> elements, Set<String> names)
554                    throws PortalException {
555    
556                    for (Element element : elements) {
557                            String elementName = element.getName();
558    
559                            if (elementName.equals("meta-data")) {
560                                    continue;
561                            }
562    
563                            String name = element.attributeValue("name", StringPool.BLANK);
564                            String type = element.attributeValue("type", StringPool.BLANK);
565    
566                            if (Validator.isNull(name) ||
567                                    name.startsWith(DDMStructureConstants.XSD_NAME_RESERVED)) {
568    
569                                    throw new StructureXsdException();
570                            }
571    
572                            char[] charArray = name.toCharArray();
573    
574                            for (int i = 0; i < charArray.length; i++) {
575                                    if (!Character.isLetterOrDigit(charArray[i]) &&
576                                            (charArray[i] != CharPool.DASH) &&
577                                            (charArray[i] != CharPool.UNDERLINE)) {
578    
579                                            throw new StructureXsdException();
580                                    }
581                            }
582    
583                            String path = name;
584    
585                            Element parentElement = element.getParent();
586    
587                            while (!parentElement.isRootElement()) {
588                                    path =
589                                            parentElement.attributeValue("name", StringPool.BLANK) +
590                                                    StringPool.SLASH + path;
591    
592                                    parentElement = parentElement.getParent();
593                            }
594    
595                            path = path.toLowerCase();
596    
597                            if (names.contains(path)) {
598                                    throw new StructureDuplicateElementException();
599                            }
600                            else {
601                                    names.add(path);
602                            }
603    
604                            if (Validator.isNull(type)) {
605                                    throw new StructureXsdException();
606                            }
607    
608                            validate(element.elements(), names);
609                    }
610            }
611    
612            protected void validate(
613                            long groupId, String structureKey, Map<Locale, String> nameMap,
614                            String xsd)
615                    throws PortalException, SystemException {
616    
617                    DDMStructure structure = ddmStructurePersistence.fetchByG_S(
618                            groupId, structureKey);
619    
620                    if (structure != null) {
621                            throw new StructureDuplicateStructureKeyException();
622                    }
623    
624                    validate(nameMap, xsd);
625            }
626    
627            protected void validate(Map<Locale, String> nameMap, String xsd)
628                    throws PortalException {
629    
630                    if (Validator.isNull(xsd)) {
631                            throw new StructureXsdException();
632                    }
633                    else {
634                            try {
635                                    List<Element> elements = new ArrayList<Element>();
636    
637                                    Document document = SAXReaderUtil.read(xsd);
638    
639                                    Element rootElement = document.getRootElement();
640    
641                                    if (rootElement.elements().isEmpty()) {
642                                            throw new StructureXsdException();
643                                    }
644    
645                                    Locale contentDefaultLocale = LocaleUtil.fromLanguageId(
646                                            rootElement.attributeValue("default-locale"));
647    
648                                    validateLanguages(nameMap, contentDefaultLocale);
649    
650                                    elements.addAll(rootElement.elements());
651    
652                                    Set<String> elNames = new HashSet<String>();
653    
654                                    validate(elements, elNames);
655                            }
656                            catch (LocaleException le) {
657                                    throw le;
658                            }
659                            catch (StructureDuplicateElementException sdee) {
660                                    throw sdee;
661                            }
662                            catch (StructureNameException sne) {
663                                    throw sne;
664                            }
665                            catch (StructureXsdException sxe) {
666                                    throw sxe;
667                            }
668                            catch (Exception e) {
669                                    throw new StructureXsdException();
670                            }
671                    }
672            }
673    
674            protected void validateLanguages(
675                            Map<Locale, String> nameMap, Locale contentDefaultLocale)
676                    throws PortalException {
677    
678                    String name = nameMap.get(contentDefaultLocale);
679    
680                    if (Validator.isNull(name)) {
681                            throw new StructureNameException();
682                    }
683    
684                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
685    
686                    if (!ArrayUtil.contains(availableLocales, contentDefaultLocale)) {
687                            LocaleException le = new LocaleException();
688    
689                            le.setSourceAvailableLocales(new Locale[] {contentDefaultLocale});
690                            le.setTargetAvailableLocales(availableLocales);
691    
692                            throw le;
693                    }
694            }
695    
696            private static Log _log = LogFactoryUtil.getLog(
697                    DDMStructureLocalServiceImpl.class);
698    
699    }