001    /**
002     * Copyright (c) 2000-present 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.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.language.LanguageUtil;
021    import com.liferay.portal.kernel.search.Indexer;
022    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
023    import com.liferay.portal.kernel.systemevent.SystemEvent;
024    import com.liferay.portal.kernel.transaction.TransactionCommitCallbackRegistryUtil;
025    import com.liferay.portal.kernel.util.ArrayUtil;
026    import com.liferay.portal.kernel.util.GroupThreadLocal;
027    import com.liferay.portal.kernel.util.LocaleUtil;
028    import com.liferay.portal.kernel.util.OrderByComparator;
029    import com.liferay.portal.kernel.util.StringPool;
030    import com.liferay.portal.kernel.util.StringUtil;
031    import com.liferay.portal.kernel.util.Validator;
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.ResourceConstants;
038    import com.liferay.portal.model.SystemEventConstants;
039    import com.liferay.portal.model.User;
040    import com.liferay.portal.security.auth.CompanyThreadLocal;
041    import com.liferay.portal.service.ServiceContext;
042    import com.liferay.portal.util.PortalUtil;
043    import com.liferay.portal.util.PropsValues;
044    import com.liferay.portlet.dynamicdatamapping.NoSuchStructureException;
045    import com.liferay.portlet.dynamicdatamapping.RequiredStructureException;
046    import com.liferay.portlet.dynamicdatamapping.StructureDefinitionException;
047    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateElementException;
048    import com.liferay.portlet.dynamicdatamapping.StructureDuplicateStructureKeyException;
049    import com.liferay.portlet.dynamicdatamapping.StructureNameException;
050    import com.liferay.portlet.dynamicdatamapping.model.DDMForm;
051    import com.liferay.portlet.dynamicdatamapping.model.DDMFormField;
052    import com.liferay.portlet.dynamicdatamapping.model.DDMStructure;
053    import com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants;
054    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplate;
055    import com.liferay.portlet.dynamicdatamapping.model.DDMTemplateConstants;
056    import com.liferay.portlet.dynamicdatamapping.service.base.DDMStructureLocalServiceBaseImpl;
057    import com.liferay.portlet.dynamicdatamapping.util.DDMFormTemplateSynchonizer;
058    import com.liferay.portlet.dynamicdatamapping.util.DDMXMLUtil;
059    import com.liferay.portlet.journal.model.JournalArticle;
060    import com.liferay.portlet.journal.model.JournalFolderConstants;
061    
062    import java.util.ArrayList;
063    import java.util.Date;
064    import java.util.HashSet;
065    import java.util.List;
066    import java.util.Locale;
067    import java.util.Map;
068    import java.util.Set;
069    import java.util.concurrent.Callable;
070    
071    /**
072     * Provides the local service for accessing, adding, deleting, and updating
073     * dynamic data mapping (DDM) structures.
074     *
075     * <p>
076     * DDM structures (structures) are used in Liferay to store structured content
077     * like document types, dynamic data definitions, or web contents.
078     * </p>
079     *
080     * <p>
081     * Structures support inheritance via parent structures. They also support
082     * multi-language names and descriptions.
083     * </p>
084     *
085     * <p>
086     * Structures can be related to many models in Liferay, such as those for web
087     * contents, dynamic data lists, and documents. This relationship can be
088     * established via the model's class name ID.
089     * </p>
090     *
091     * @author Brian Wing Shun Chan
092     * @author Bruno Basto
093     * @author Marcellus Tavares
094     * @author Juan Fern??ndez
095     */
096    public class DDMStructureLocalServiceImpl
097            extends DDMStructureLocalServiceBaseImpl {
098    
099            /**
100             * Adds a structure referencing its parent structure.
101             *
102             * @param  userId the primary key of the structure's creator/owner
103             * @param  groupId the primary key of the group
104             * @param  parentStructureId the primary key of the parent structure
105             *         (optionally {@link
106             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants#DEFAULT_PARENT_STRUCTURE_ID})
107             * @param  classNameId the primary key of the class name for the structure's
108             *         related model
109             * @param  structureKey the unique string identifying the structure
110             *         (optionally <code>null</code>)
111             * @param  nameMap the structure's locales and localized names
112             * @param  descriptionMap the structure's locales and localized descriptions
113             * @param  definition the structure's XML schema definition
114             * @param  storageType the structure's storage type. It can be "xml" or
115             *         "expando". For more information, see {@link
116             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
117             * @param  type the structure's type. For more information, see {@link
118             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
119             * @param  serviceContext the service context to be applied. Can set the
120             *         UUID, creation date, modification date, guest permissions, and
121             *         group permissions for the structure.
122             * @return the structure
123             * @throws PortalException if a user with the primary key could not be
124             *         found, if the XSD was not well-formed, or if a portal exception
125             *         occurred
126             */
127            @Override
128            public DDMStructure addStructure(
129                            long userId, long groupId, long parentStructureId, long classNameId,
130                            String structureKey, Map<Locale, String> nameMap,
131                            Map<Locale, String> descriptionMap, String definition,
132                            String storageType, int type, ServiceContext serviceContext)
133                    throws PortalException {
134    
135                    // Structure
136    
137                    User user = userPersistence.findByPrimaryKey(userId);
138    
139                    if (Validator.isNull(structureKey)) {
140                            structureKey = String.valueOf(counterLocalService.increment());
141                    }
142                    else {
143                            structureKey = StringUtil.toUpperCase(structureKey.trim());
144                    }
145    
146                    try {
147                            definition = DDMXMLUtil.validateXML(definition);
148                            definition = DDMXMLUtil.formatXML(definition);
149                    }
150                    catch (Exception e) {
151                            throw new StructureDefinitionException();
152                    }
153    
154                    Date now = new Date();
155    
156                    validate(
157                            groupId, parentStructureId, classNameId, structureKey, nameMap,
158                            definition);
159    
160                    long structureId = counterLocalService.increment();
161    
162                    DDMStructure structure = ddmStructurePersistence.create(structureId);
163    
164                    structure.setUuid(serviceContext.getUuid());
165                    structure.setGroupId(groupId);
166                    structure.setCompanyId(user.getCompanyId());
167                    structure.setUserId(user.getUserId());
168                    structure.setUserName(user.getFullName());
169                    structure.setCreateDate(serviceContext.getCreateDate(now));
170                    structure.setModifiedDate(serviceContext.getModifiedDate(now));
171                    structure.setParentStructureId(parentStructureId);
172                    structure.setClassNameId(classNameId);
173                    structure.setStructureKey(structureKey);
174                    structure.setNameMap(nameMap);
175                    structure.setDescriptionMap(descriptionMap);
176                    structure.setDefinition(definition);
177                    structure.setStorageType(storageType);
178                    structure.setType(type);
179    
180                    ddmStructurePersistence.update(structure);
181    
182                    // Resources
183    
184                    if (serviceContext.isAddGroupPermissions() ||
185                            serviceContext.isAddGuestPermissions()) {
186    
187                            addStructureResources(
188                                    structure, serviceContext.isAddGroupPermissions(),
189                                    serviceContext.isAddGuestPermissions());
190                    }
191                    else {
192                            addStructureResources(
193                                    structure, serviceContext.getGroupPermissions(),
194                                    serviceContext.getGuestPermissions());
195                    }
196    
197                    return structure;
198            }
199    
200            /**
201             * Adds a structure referencing a default parent structure, using the portal
202             * property <code>dynamic.data.lists.storage.type</code> storage type and
203             * default structure type.
204             *
205             * @param  userId the primary key of the structure's creator/owner
206             * @param  groupId the primary key of the group
207             * @param  classNameId the primary key of the class name for the structure's
208             *         related model
209             * @param  nameMap the structure's locales and localized names
210             * @param  descriptionMap the structure's locales and localized descriptions
211             * @param  definition the structure's XML schema definition
212             * @param  serviceContext the service context to be applied. Can set the
213             *         UUID, creation date, modification date, guest permissions, and
214             *         group permissions for the structure.
215             * @return the structure
216             * @throws PortalException if a user with the primary key could not be
217             *         found, if the XSD was not well-formed, or if a portal exception
218             *         occurred
219             */
220            @Override
221            public DDMStructure addStructure(
222                            long userId, long groupId, long classNameId,
223                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
224                            String definition, ServiceContext serviceContext)
225                    throws PortalException {
226    
227                    return addStructure(
228                            userId, groupId, DDMStructureConstants.DEFAULT_PARENT_STRUCTURE_ID,
229                            classNameId, null, nameMap, descriptionMap, definition,
230                            PropsValues.DYNAMIC_DATA_LISTS_STORAGE_TYPE,
231                            DDMStructureConstants.TYPE_DEFAULT, serviceContext);
232            }
233    
234            /**
235             * Adds a structure referencing a default parent structure if the parent
236             * structure is not found.
237             *
238             * @param  userId the primary key of the structure's creator/owner
239             * @param  groupId the primary key of the group
240             * @param  parentStructureKey the unique string identifying the parent
241             *         structure (optionally <code>null</code>)
242             * @param  classNameId the primary key of the class name for the structure's
243             *         related model
244             * @param  structureKey the unique string identifying the structure
245             *         (optionally <code>null</code>)
246             * @param  nameMap the structure's locales and localized names
247             * @param  descriptionMap the structure's locales and localized descriptions
248             * @param  definition the structure's XML schema definition
249             * @param  storageType the structure's storage type. It can be "xml" or
250             *         "expando". For more information, see {@link
251             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
252             * @param  type the structure's type. For more information, see {@link
253             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
254             * @param  serviceContext the service context to be applied. Can set the
255             *         UUID, creation date, modification date, guest permissions and
256             *         group permissions for the structure.
257             * @return the structure
258             * @throws PortalException if a user with the primary key could not be
259             *         found, if the XSD was not well-formed, or if a portal exception
260             *         occurred
261             */
262            @Override
263            public DDMStructure addStructure(
264                            long userId, long groupId, String parentStructureKey,
265                            long classNameId, String structureKey, Map<Locale, String> nameMap,
266                            Map<Locale, String> descriptionMap, String definition,
267                            String storageType, int type, ServiceContext serviceContext)
268                    throws PortalException {
269    
270                    DDMStructure parentStructure = fetchStructure(
271                            groupId, classNameId, parentStructureKey);
272    
273                    long parentStructureId =
274                            DDMStructureConstants.DEFAULT_PARENT_STRUCTURE_ID;
275    
276                    if (parentStructure != null) {
277                            parentStructureId = parentStructure.getStructureId();
278                    }
279    
280                    return addStructure(
281                            userId, groupId, parentStructureId, classNameId, structureKey,
282                            nameMap, descriptionMap, definition, storageType, type,
283                            serviceContext);
284            }
285    
286            /**
287             * Adds the resources to the structure.
288             *
289             * @param  structure the structure to add resources to
290             * @param  addGroupPermissions whether to add group permissions
291             * @param  addGuestPermissions whether to add guest permissions
292             * @throws PortalException if a portal exception occurred
293             */
294            @Override
295            public void addStructureResources(
296                            DDMStructure structure, boolean addGroupPermissions,
297                            boolean addGuestPermissions)
298                    throws PortalException {
299    
300                    resourceLocalService.addResources(
301                            structure.getCompanyId(), structure.getGroupId(),
302                            structure.getUserId(), DDMStructure.class.getName(),
303                            structure.getStructureId(), false, addGroupPermissions,
304                            addGuestPermissions);
305            }
306    
307            /**
308             * Adds the model resources with the permissions to the structure.
309             *
310             * @param  structure the structure to add resources to
311             * @param  groupPermissions the group permissions to be added
312             * @param  guestPermissions the guest permissions to be added
313             * @throws PortalException if a portal exception occurred
314             */
315            @Override
316            public void addStructureResources(
317                            DDMStructure structure, String[] groupPermissions,
318                            String[] guestPermissions)
319                    throws PortalException {
320    
321                    resourceLocalService.addModelResources(
322                            structure.getCompanyId(), structure.getGroupId(),
323                            structure.getUserId(), DDMStructure.class.getName(),
324                            structure.getStructureId(), groupPermissions, guestPermissions);
325            }
326    
327            /**
328             * Copies a structure, creating a new structure with all the values
329             * extracted from the original one. The new structure supports a new name
330             * and description.
331             *
332             * @param  userId the primary key of the structure's creator/owner
333             * @param  structureId the primary key of the structure to be copied
334             * @param  nameMap the new structure's locales and localized names
335             * @param  descriptionMap the new structure's locales and localized
336             *         descriptions
337             * @param  serviceContext the service context to be applied. Can set the
338             *         UUID, creation date, modification date, guest permissions, and
339             *         group permissions for the structure.
340             * @return the new structure
341             * @throws PortalException if a portal exception occurred
342             */
343            @Override
344            public DDMStructure copyStructure(
345                            long userId, long structureId, Map<Locale, String> nameMap,
346                            Map<Locale, String> descriptionMap, ServiceContext serviceContext)
347                    throws PortalException {
348    
349                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
350                            structureId);
351    
352                    return addStructure(
353                            userId, structure.getGroupId(), structure.getParentStructureId(),
354                            structure.getClassNameId(), null, nameMap, descriptionMap,
355                            structure.getDefinition(), structure.getStorageType(),
356                            structure.getType(), serviceContext);
357            }
358    
359            @Override
360            public DDMStructure copyStructure(
361                            long userId, long structureId, ServiceContext serviceContext)
362                    throws PortalException {
363    
364                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
365                            structureId);
366    
367                    return addStructure(
368                            userId, structure.getGroupId(), structure.getParentStructureId(),
369                            structure.getClassNameId(), null, structure.getNameMap(),
370                            structure.getDescriptionMap(), structure.getDefinition(),
371                            structure.getStorageType(), structure.getType(), serviceContext);
372            }
373    
374            /**
375             * Deletes the structure and its resources.
376             *
377             * <p>
378             * Before deleting the structure, this method verifies whether the structure
379             * is required by another entity. If it is needed, an exception is thrown.
380             * </p>
381             *
382             * @param  structure the structure to be deleted
383             * @throws PortalException if a portal exception occurred
384             */
385            @Override
386            @SystemEvent(type = SystemEventConstants.TYPE_DELETE)
387            public void deleteStructure(DDMStructure structure) throws PortalException {
388                    if (!GroupThreadLocal.isDeleteInProcess()) {
389                            if (ddmStructureLinkPersistence.countByStructureId(
390                                            structure.getStructureId()) > 0) {
391    
392                                    throw new RequiredStructureException(
393                                            RequiredStructureException.REFERENCED_STRUCTURE_LINK);
394                            }
395    
396                            if (ddmStructurePersistence.countByParentStructureId(
397                                            structure.getStructureId()) > 0) {
398    
399                                    throw new RequiredStructureException(
400                                            RequiredStructureException.REFERENCED_STRUCTURE);
401                            }
402    
403                            long classNameId = classNameLocalService.getClassNameId(
404                                    DDMStructure.class);
405    
406                            if (ddmTemplatePersistence.countByG_C_C(
407                                            structure.getGroupId(), classNameId,
408                                            structure.getPrimaryKey()) > 0) {
409    
410                                    throw new RequiredStructureException(
411                                            RequiredStructureException.REFERENCED_TEMPLATE);
412                            }
413                    }
414    
415                    // Structure
416    
417                    ddmStructurePersistence.remove(structure);
418    
419                    // Resources
420    
421                    resourceLocalService.deleteResource(
422                            structure.getCompanyId(), DDMStructure.class.getName(),
423                            ResourceConstants.SCOPE_INDIVIDUAL, structure.getStructureId());
424            }
425    
426            /**
427             * Deletes the structure and its resources.
428             *
429             * <p>
430             * Before deleting the structure, the system verifies whether the structure
431             * is required by another entity. If it is needed, an exception is thrown.
432             * </p>
433             *
434             * @param  structureId the primary key of the structure to be deleted
435             * @throws PortalException if a portal exception occurred
436             */
437            @Override
438            public void deleteStructure(long structureId) throws PortalException {
439                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
440                            structureId);
441    
442                    ddmStructureLocalService.deleteStructure(structure);
443            }
444    
445            /**
446             * Deletes the matching structure and its resources.
447             *
448             * <p>
449             * Before deleting the structure, the system verifies whether the structure
450             * is required by another entity. If it is needed, an exception is thrown.
451             * </p>
452             *
453             * @param  groupId the primary key of the group
454             * @param  classNameId the primary key of the class name for the structure's
455             *         related model
456             * @param  structureKey the unique string identifying the structure
457             * @throws PortalException if a portal exception occurred
458             */
459            @Override
460            public void deleteStructure(
461                            long groupId, long classNameId, String structureKey)
462                    throws PortalException {
463    
464                    structureKey = getStructureKey(structureKey);
465    
466                    DDMStructure structure = ddmStructurePersistence.findByG_C_S(
467                            groupId, classNameId, structureKey);
468    
469                    ddmStructureLocalService.deleteStructure(structure);
470            }
471    
472            /**
473             * Deletes all the structures of the group.
474             *
475             * <p>
476             * Before deleting the structures, the system verifies whether each
477             * structure is required by another entity. If any of the structures are
478             * needed, an exception is thrown.
479             * </p>
480             *
481             * @param  groupId the primary key of the group
482             * @throws PortalException if a portal exception occurred
483             */
484            @Override
485            public void deleteStructures(long groupId) throws PortalException {
486                    List<DDMStructure> structures = ddmStructurePersistence.findByGroupId(
487                            groupId);
488    
489                    deleteStructures(structures);
490            }
491    
492            @Override
493            public void deleteStructures(long groupId, long classNameId)
494                    throws PortalException {
495    
496                    List<DDMStructure> structures = ddmStructurePersistence.findByG_C(
497                            groupId, classNameId);
498    
499                    deleteStructures(structures);
500            }
501    
502            /**
503             * Returns the structure with the ID.
504             *
505             * @param  structureId the primary key of the structure
506             * @return the structure with the structure ID, or <code>null</code> if a
507             *         matching structure could not be found
508             */
509            @Override
510            public DDMStructure fetchStructure(long structureId) {
511                    return ddmStructurePersistence.fetchByPrimaryKey(structureId);
512            }
513    
514            /**
515             * Returns the structure matching the class name ID, structure key, and
516             * group.
517             *
518             * @param  groupId the primary key of the group
519             * @param  classNameId the primary key of the class name for the structure's
520             *         related model
521             * @param  structureKey the unique string identifying the structure
522             * @return the matching structure, or <code>null</code> if a matching
523             *         structure could not be found
524             */
525            @Override
526            public DDMStructure fetchStructure(
527                    long groupId, long classNameId, String structureKey) {
528    
529                    structureKey = getStructureKey(structureKey);
530    
531                    return ddmStructurePersistence.fetchByG_C_S(
532                            groupId, classNameId, structureKey);
533            }
534    
535            /**
536             * Returns the structure matching the class name ID, structure key, and
537             * group, optionally searching ancestor sites (that have sharing enabled)
538             * and global scoped sites.
539             *
540             * <p>
541             * This method first searches in the group. If the structure is still not
542             * found and <code>includeAncestorStructures</code> is set to
543             * <code>true</code>, this method searches the group's ancestor sites (that
544             * have sharing enabled) and lastly searches global scoped sites.
545             * </p>
546             *
547             * @param  groupId the primary key of the group
548             * @param  classNameId the primary key of the class name for the structure's
549             *         related model
550             * @param  structureKey the unique string identifying the structure
551             * @param  includeAncestorStructures whether to include ancestor sites (that
552             *         have sharing enabled) and include global scoped sites in the
553             *         search
554             * @return the matching structure, or <code>null</code> if a matching
555             *         structure could not be found
556             * @throws PortalException if a portal exception occurred
557             */
558            @Override
559            public DDMStructure fetchStructure(
560                            long groupId, long classNameId, String structureKey,
561                            boolean includeAncestorStructures)
562                    throws PortalException {
563    
564                    structureKey = getStructureKey(structureKey);
565    
566                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
567                            groupId, classNameId, structureKey);
568    
569                    if (structure != null) {
570                            return structure;
571                    }
572    
573                    if (!includeAncestorStructures) {
574                            return null;
575                    }
576    
577                    for (long ancestorSiteGroupId :
578                                    PortalUtil.getAncestorSiteGroupIds(groupId)) {
579    
580                            structure = ddmStructurePersistence.fetchByG_C_S(
581                                    ancestorSiteGroupId, classNameId, structureKey);
582    
583                            if (structure != null) {
584                                    return structure;
585                            }
586                    }
587    
588                    return null;
589            }
590    
591            /**
592             * @deprecated As of 6.2.0, replaced by {@link #getClassStructures(long,
593             *             long)}
594             */
595            @Deprecated
596            @Override
597            public List<DDMStructure> getClassStructures(long classNameId) {
598                    return ddmStructurePersistence.findByClassNameId(classNameId);
599            }
600    
601            /**
602             * @deprecated As of 6.2.0, replaced by {@link #getClassStructures(long,
603             *             long, int, int)}
604             */
605            @Deprecated
606            @Override
607            public List<DDMStructure> getClassStructures(
608                    long classNameId, int start, int end) {
609    
610                    return ddmStructurePersistence.findByClassNameId(
611                            classNameId, start, end);
612            }
613    
614            /**
615             * Returns all the structures matching the class name ID.
616             *
617             * @param  companyId the primary key of the structure's company
618             * @param  classNameId the primary key of the class name for the structure's
619             *         related model
620             * @return the structures matching the class name ID
621             */
622            @Override
623            public List<DDMStructure> getClassStructures(
624                    long companyId, long classNameId) {
625    
626                    return ddmStructurePersistence.findByC_C(companyId, classNameId);
627            }
628    
629            /**
630             * Returns a range of all the structures matching the class name ID.
631             *
632             * <p>
633             * Useful when paginating results. Returns a maximum of <code>end -
634             * start</code> instances. <code>start</code> and <code>end</code> are not
635             * primary keys, they are indexes in the result set. Thus, <code>0</code>
636             * refers to the first result in the set. Setting both <code>start</code>
637             * and <code>end</code> to {@link
638             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
639             * result set.
640             * </p>
641             *
642             * @param  companyId the primary key of the structure's company
643             * @param  classNameId the primary key of the class name for the structure's
644             *         related model
645             * @param  start the lower bound of the range of structures to return
646             * @param  end the upper bound of the range of structures to return (not
647             *         inclusive)
648             * @return the range of matching structures
649             */
650            @Override
651            public List<DDMStructure> getClassStructures(
652                    long companyId, long classNameId, int start, int end) {
653    
654                    return ddmStructurePersistence.findByC_C(
655                            companyId, classNameId, start, end);
656            }
657    
658            /**
659             * Returns all the structures matching the class name ID ordered by the
660             * comparator.
661             *
662             * @param  companyId the primary key of the structure's company
663             * @param  classNameId the primary key of the class name for the structure's
664             *         related model
665             * @param  orderByComparator the comparator to order the structures
666             *         (optionally <code>null</code>)
667             * @return the matching structures ordered by the comparator
668             */
669            @Override
670            public List<DDMStructure> getClassStructures(
671                    long companyId, long classNameId,
672                    OrderByComparator<DDMStructure> orderByComparator) {
673    
674                    return ddmStructurePersistence.findByC_C(
675                            companyId, classNameId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
676                            orderByComparator);
677            }
678    
679            /**
680             * @deprecated As of 6.2.0, replaced by {@link #getClassStructures(long,
681             *             long, OrderByComparator)}
682             */
683            @Deprecated
684            @Override
685            public List<DDMStructure> getClassStructures(
686                    long classNameId, OrderByComparator<DDMStructure> orderByComparator) {
687    
688                    return ddmStructurePersistence.findByClassNameId(
689                            classNameId, QueryUtil.ALL_POS, QueryUtil.ALL_POS,
690                            orderByComparator);
691            }
692    
693            /**
694             * Returns all the structures for the document library file entry type.
695             *
696             * @param  dlFileEntryTypeId the primary key of the document library file
697             *         entry type
698             * @return the structures for the document library file entry type
699             */
700            @Override
701            public List<DDMStructure> getDLFileEntryTypeStructures(
702                    long dlFileEntryTypeId) {
703    
704                    return dlFileEntryTypePersistence.getDDMStructures(dlFileEntryTypeId);
705            }
706    
707            @Override
708            public List<DDMStructure> getJournalFolderStructures(
709                            long[] groupIds, long journalFolderId, int restrictionType)
710                    throws PortalException {
711    
712                    if (restrictionType ==
713                                    JournalFolderConstants.
714                                            RESTRICTION_TYPE_DDM_STRUCTURES_AND_WORKFLOW) {
715    
716                            return journalFolderPersistence.getDDMStructures(journalFolderId);
717                    }
718    
719                    List<DDMStructure> structures = null;
720    
721                    journalFolderId =
722                            journalFolderLocalService.getOverridedDDMStructuresFolderId(
723                                    journalFolderId);
724    
725                    if (journalFolderId !=
726                                    JournalFolderConstants.DEFAULT_PARENT_FOLDER_ID) {
727    
728                            structures = journalFolderPersistence.getDDMStructures(
729                                    journalFolderId);
730                    }
731                    else {
732                            long classNameId = classNameLocalService.getClassNameId(
733                                    JournalArticle.class);
734    
735                            structures = ddmStructurePersistence.findByG_C(
736                                    groupIds, classNameId);
737                    }
738    
739                    return structures;
740            }
741    
742            /**
743             * Returns the structure with the ID.
744             *
745             * @param  structureId the primary key of the structure
746             * @return the structure with the ID
747             * @throws PortalException if a structure with the ID could not be found
748             */
749            @Override
750            public DDMStructure getStructure(long structureId) throws PortalException {
751                    return ddmStructurePersistence.findByPrimaryKey(structureId);
752            }
753    
754            /**
755             * Returns the structure matching the class name ID, structure key, and
756             * group.
757             *
758             * @param  groupId the primary key of the structure's group
759             * @param  classNameId the primary key of the class name for the structure's
760             *         related model
761             * @param  structureKey the unique string identifying the structure
762             * @return the matching structure
763             * @throws PortalException if a matching structure could not be found
764             */
765            @Override
766            public DDMStructure getStructure(
767                            long groupId, long classNameId, String structureKey)
768                    throws PortalException {
769    
770                    structureKey = getStructureKey(structureKey);
771    
772                    return ddmStructurePersistence.findByG_C_S(
773                            groupId, classNameId, structureKey);
774            }
775    
776            /**
777             * Returns the structure matching the class name ID, structure key, and
778             * group, optionally searching ancestor sites (that have sharing enabled)
779             * and global scoped sites.
780             *
781             * <p>
782             * This method first searches in the group. If the structure is still not
783             * found and <code>includeAncestorStructures</code> is set to
784             * <code>true</code>, this method searches the group's ancestor sites (that
785             * have sharing enabled) and lastly searches global scoped sites.
786             * </p>
787             *
788             * @param  groupId the primary key of the structure's group
789             * @param  classNameId the primary key of the class name for the structure's
790             *         related model
791             * @param  structureKey the unique string identifying the structure
792             * @param  includeAncestorStructures whether to include ancestor sites (that
793             *         have sharing enabled) and include global scoped sites in the
794             *         search in the search
795             * @return the matching structure
796             * @throws PortalException if a matching structure could not be found
797             */
798            @Override
799            public DDMStructure getStructure(
800                            long groupId, long classNameId, String structureKey,
801                            boolean includeAncestorStructures)
802                    throws PortalException {
803    
804                    structureKey = getStructureKey(structureKey);
805    
806                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
807                            groupId, classNameId, structureKey);
808    
809                    if (structure != null) {
810                            return structure;
811                    }
812    
813                    if (!includeAncestorStructures) {
814                            throw new NoSuchStructureException(
815                                    "No DDMStructure exists with the structure key " +
816                                            structureKey);
817                    }
818    
819                    for (long curGroupId : PortalUtil.getAncestorSiteGroupIds(groupId)) {
820                            structure = ddmStructurePersistence.fetchByG_C_S(
821                                    curGroupId, classNameId, structureKey);
822    
823                            if (structure != null) {
824                                    return structure;
825                            }
826                    }
827    
828                    throw new NoSuchStructureException(
829                            "No DDMStructure exists with the structure key " +
830                                    structureKey + " in the ancestor groups");
831            }
832    
833            /**
834             * Returns all the structures matching the group, name, and description.
835             *
836             * @param  groupId the primary key of the structure's group
837             * @param  name the structure's name
838             * @param  description the structure's description
839             * @return the matching structures
840             */
841            @Override
842            public List<DDMStructure> getStructure(
843                    long groupId, String name, String description) {
844    
845                    return ddmStructurePersistence.findByG_N_D(groupId, name, description);
846            }
847    
848            /**
849             * @deprecated As of 6.2.0, replaced by {@link #getStructures}
850             */
851            @Deprecated
852            @Override
853            public List<DDMStructure> getStructureEntries() {
854                    return getStructures();
855            }
856    
857            /**
858             * @deprecated As of 6.2.0, replaced by {@link #getStructures(long)}
859             */
860            @Deprecated
861            @Override
862            public List<DDMStructure> getStructureEntries(long groupId) {
863                    return getStructures(groupId);
864            }
865    
866            /**
867             * @deprecated As of 6.2.0, replaced by {@link #getStructures(long, int,
868             *             int)}
869             */
870            @Deprecated
871            @Override
872            public List<DDMStructure> getStructureEntries(
873                    long groupId, int start, int end) {
874    
875                    return getStructures(groupId, start, end);
876            }
877    
878            /**
879             * Returns all the structures present in the system.
880             *
881             * @return the structures present in the system
882             */
883            @Override
884            public List<DDMStructure> getStructures() {
885                    return ddmStructurePersistence.findAll();
886            }
887    
888            /**
889             * Returns all the structures present in the group.
890             *
891             * @param  groupId the primary key of the group
892             * @return the structures present in the group
893             */
894            @Override
895            public List<DDMStructure> getStructures(long groupId) {
896                    return ddmStructurePersistence.findByGroupId(groupId);
897            }
898    
899            /**
900             * Returns a range of all the structures belonging to the group.
901             *
902             * <p>
903             * Useful when paginating results. Returns a maximum of <code>end -
904             * start</code> instances. <code>start</code> and <code>end</code> are not
905             * primary keys, they are indexes in the result set. Thus, <code>0</code>
906             * refers to the first result in the set. Setting both <code>start</code>
907             * and <code>end</code> to {@link
908             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
909             * result set.
910             * </p>
911             *
912             * @param  groupId the primary key of the group
913             * @param  start the lower bound of the range of structures to return
914             * @param  end the upper bound of the range of structures to return (not
915             *         inclusive)
916             * @return the range of matching structures
917             */
918            @Override
919            public List<DDMStructure> getStructures(long groupId, int start, int end) {
920                    return ddmStructurePersistence.findByGroupId(groupId, start, end);
921            }
922    
923            /**
924             * Returns all the structures matching class name ID and group.
925             *
926             * @param  groupId the primary key of the group
927             * @param  classNameId the primary key of the class name for the structure's
928             *         related model
929             * @return the matching structures
930             */
931            @Override
932            public List<DDMStructure> getStructures(long groupId, long classNameId) {
933                    return ddmStructurePersistence.findByG_C(groupId, classNameId);
934            }
935    
936            /**
937             * Returns a range of all the structures that match the class name ID and
938             * group.
939             *
940             * <p>
941             * Useful when paginating results. Returns a maximum of <code>end -
942             * start</code> instances. <code>start</code> and <code>end</code> are not
943             * primary keys, they are indexes in the result set. Thus, <code>0</code>
944             * refers to the first result in the set. Setting both <code>start</code>
945             * and <code>end</code> to {@link
946             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
947             * result set.
948             * </p>
949             *
950             * @param  groupId the primary key of the group
951             * @param  classNameId the primary key of the class name for the structure's
952             *         related model
953             * @param  start the lower bound of the range of structures to return
954             * @param  end the upper bound of the range of structures to return (not
955             *         inclusive)
956             * @return the range of matching structures
957             */
958            @Override
959            public List<DDMStructure> getStructures(
960                    long groupId, long classNameId, int start, int end) {
961    
962                    return ddmStructurePersistence.findByG_C(
963                            groupId, classNameId, start, end);
964            }
965    
966            /**
967             * Returns an ordered range of all the structures matching the class name ID
968             * and group.
969             *
970             * <p>
971             * Useful when paginating results. Returns a maximum of <code>end -
972             * start</code> instances. <code>start</code> and <code>end</code> are not
973             * primary keys, they are indexes in the result set. Thus, <code>0</code>
974             * refers to the first result in the set. Setting both <code>start</code>
975             * and <code>end</code> to {@link
976             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
977             * result set.
978             * </p>
979             *
980             * @param  groupId the primary key of the group
981             * @param  classNameId the primary key of the class name for the structure's
982             *         related model
983             * @param  start the lower bound of the range of structures to return
984             * @param  end the upper bound of the range of structures to return (not
985             *         inclusive)
986             * @param  orderByComparator the comparator to order the structures
987             *         (optionally <code>null</code>)
988             * @return the range of matching structures ordered by the comparator
989             */
990            @Override
991            public List<DDMStructure> getStructures(
992                    long groupId, long classNameId, int start, int end,
993                    OrderByComparator<DDMStructure> orderByComparator) {
994    
995                    return ddmStructurePersistence.findByG_C(
996                            groupId, classNameId, start, end, orderByComparator);
997            }
998    
999            @Override
1000            public List<DDMStructure> getStructures(
1001                    long groupId, String name, String description) {
1002    
1003                    return ddmStructurePersistence.findByG_N_D(groupId, name, description);
1004            }
1005    
1006            /**
1007             * Returns all the structures belonging to the groups.
1008             *
1009             * @param  groupIds the primary keys of the groups
1010             * @return the structures belonging to the groups
1011             */
1012            @Override
1013            public List<DDMStructure> getStructures(long[] groupIds) {
1014                    return ddmStructurePersistence.findByGroupId(groupIds);
1015            }
1016    
1017            /**
1018             * Returns all the structures matching the class name ID and belonging to
1019             * the groups.
1020             *
1021             * @param  groupIds the primary keys of the groups
1022             * @param  classNameId the primary key of the class name for the structure's
1023             *         related model
1024             * @return the matching structures
1025             */
1026            @Override
1027            public List<DDMStructure> getStructures(long[] groupIds, long classNameId) {
1028                    return ddmStructurePersistence.findByG_C(groupIds, classNameId);
1029            }
1030    
1031            /**
1032             * Returns a range of all the structures matching the class name ID and
1033             * belonging to the groups.
1034             *
1035             * <p>
1036             * Useful when paginating results. Returns a maximum of <code>end -
1037             * start</code> instances. <code>start</code> and <code>end</code> are not
1038             * primary keys, they are indexes in the result set. Thus, <code>0</code>
1039             * refers to the first result in the set. Setting both <code>start</code>
1040             * and <code>end</code> to {@link
1041             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
1042             * result set.
1043             * </p>
1044             *
1045             * @param  groupIds the primary keys of the groups
1046             * @param  classNameId the primary key of the class name for the structure's
1047             *         related model
1048             * @param  start the lower bound of the range of structures to return
1049             * @param  end the upper bound of the range of structures to return (not
1050             *         inclusive)
1051             * @return the range of matching structures
1052             */
1053            @Override
1054            public List<DDMStructure> getStructures(
1055                    long[] groupIds, long classNameId, int start, int end) {
1056    
1057                    return ddmStructurePersistence.findByG_C(
1058                            groupIds, classNameId, start, end);
1059            }
1060    
1061            /**
1062             * Returns the number of structures belonging to the group.
1063             *
1064             * @param  groupId the primary key of the group
1065             * @return the number of structures belonging to the group
1066             */
1067            @Override
1068            public int getStructuresCount(long groupId) {
1069                    return ddmStructurePersistence.countByGroupId(groupId);
1070            }
1071    
1072            /**
1073             * Returns the number of structures matching the class name ID and group.
1074             *
1075             * @param  groupId the primary key of the group
1076             * @param  classNameId the primary key of the class name for the structure's
1077             *         related model
1078             * @return the number of matching structures
1079             */
1080            @Override
1081            public int getStructuresCount(long groupId, long classNameId) {
1082                    return ddmStructurePersistence.countByG_C(groupId, classNameId);
1083            }
1084    
1085            /**
1086             * Returns the number of structures matching the class name ID and belonging
1087             * to the groups.
1088             *
1089             * @param  groupIds the primary keys of the groups
1090             * @param  classNameId the primary key of the class name for the structure's
1091             *         related model
1092             * @return the number of matching structures
1093             */
1094            @Override
1095            public int getStructuresCount(long[] groupIds, long classNameId) {
1096                    return ddmStructurePersistence.countByG_C(groupIds, classNameId);
1097            }
1098    
1099            /**
1100             * Returns an ordered range of all the structures matching the groups and
1101             * class name IDs, and matching the keywords in the structure names and
1102             * descriptions.
1103             *
1104             * <p>
1105             * Useful when paginating results. Returns a maximum of <code>end -
1106             * start</code> instances. <code>start</code> and <code>end</code> are not
1107             * primary keys, they are indexes in the result set. Thus, <code>0</code>
1108             * refers to the first result in the set. Setting both <code>start</code>
1109             * and <code>end</code> to {@link
1110             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
1111             * result set.
1112             * </p>
1113             *
1114             * @param  companyId the primary key of the structure's company
1115             * @param  groupIds the primary keys of the groups
1116             * @param  classNameIds the primary keys of the class names of the models
1117             *         the structures are related to
1118             * @param  keywords the keywords (space separated), which may occur in the
1119             *         structure's name or description (optionally <code>null</code>)
1120             * @param  start the lower bound of the range of structures to return
1121             * @param  end the upper bound of the range of structures to return (not
1122             *         inclusive)
1123             * @param  orderByComparator the comparator to order the structures
1124             *         (optionally <code>null</code>)
1125             * @return the range of matching structures ordered by the comparator
1126             */
1127            @Override
1128            public List<DDMStructure> search(
1129                    long companyId, long[] groupIds, long[] classNameIds, String keywords,
1130                    int start, int end, OrderByComparator<DDMStructure> orderByComparator) {
1131    
1132                    return ddmStructureFinder.findByKeywords(
1133                            companyId, groupIds, classNameIds, keywords, start, end,
1134                            orderByComparator);
1135            }
1136    
1137            /**
1138             * Returns an ordered range of all the structures matching the groups, class
1139             * name IDs, name keyword, description keyword, storage type, and type.
1140             *
1141             * <p>
1142             * Useful when paginating results. Returns a maximum of <code>end -
1143             * start</code> instances. <code>start</code> and <code>end</code> are not
1144             * primary keys, they are indexes in the result set. Thus, <code>0</code>
1145             * refers to the first result in the set. Setting both <code>start</code>
1146             * and <code>end</code> to {@link
1147             * com.liferay.portal.kernel.dao.orm.QueryUtil#ALL_POS} will return the full
1148             * result set.
1149             * </p>
1150             *
1151             * @param  companyId the primary key of the structure's company
1152             * @param  groupIds the primary keys of the groups
1153             * @param  classNameIds the primary keys of the class names of the models
1154             *         the structures are related to
1155             * @param  name the name keywords
1156             * @param  description the description keywords
1157             * @param  storageType the structure's storage type. It can be "xml" or
1158             *         "expando". For more information, see {@link
1159             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
1160             * @param  type the structure's type. For more information, see {@link
1161             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
1162             * @param  andOperator whether every field must match its keywords, or just
1163             *         one field
1164             * @param  start the lower bound of the range of structures to return
1165             * @param  end the upper bound of the range of structures to return (not
1166             *         inclusive)
1167             * @param  orderByComparator the comparator to order the structures
1168             *         (optionally <code>null</code>)
1169             * @return the range of matching structures ordered by the comparator
1170             */
1171            @Override
1172            public List<DDMStructure> search(
1173                    long companyId, long[] groupIds, long[] classNameIds, String name,
1174                    String description, String storageType, int type, boolean andOperator,
1175                    int start, int end, OrderByComparator<DDMStructure> orderByComparator) {
1176    
1177                    return ddmStructureFinder.findByC_G_C_N_D_S_T(
1178                            companyId, groupIds, classNameIds, name, description, storageType,
1179                            type, andOperator, start, end, orderByComparator);
1180            }
1181    
1182            /**
1183             * Returns the number of structures matching the groups and class name IDs,
1184             * and matching the keywords in the structure names and descriptions.
1185             *
1186             * @param  companyId the primary key of the structure's company
1187             * @param  groupIds the primary keys of the groups
1188             * @param  classNameIds the primary keys of the class names of the models
1189             *         the structures are related to
1190             * @param  keywords the keywords (space separated), which may occur in the
1191             *         structure's name or description (optionally <code>null</code>)
1192             * @return the number of matching structures
1193             */
1194            @Override
1195            public int searchCount(
1196                    long companyId, long[] groupIds, long[] classNameIds, String keywords) {
1197    
1198                    return ddmStructureFinder.countByKeywords(
1199                            companyId, groupIds, classNameIds, keywords);
1200            }
1201    
1202            /**
1203             * Returns the number of structures matching the groups, class name IDs,
1204             * name keyword, description keyword, storage type, and type
1205             *
1206             * @param  companyId the primary key of the structure's company
1207             * @param  groupIds the primary keys of the groups
1208             * @param  classNameIds the primary keys of the class names of the models
1209             *         the structure's are related to
1210             * @param  name the name keywords
1211             * @param  description the description keywords
1212             * @param  storageType the structure's storage type. It can be "xml" or
1213             *         "expando". For more information, see {@link
1214             *         com.liferay.portlet.dynamicdatamapping.storage.StorageType}.
1215             * @param  type the structure's type. For more information, see {@link
1216             *         com.liferay.portlet.dynamicdatamapping.model.DDMStructureConstants}.
1217             * @param  andOperator whether every field must match its keywords, or just
1218             *         one field
1219             * @return the number of matching structures
1220             */
1221            @Override
1222            public int searchCount(
1223                    long companyId, long[] groupIds, long[] classNameIds, String name,
1224                    String description, String storageType, int type, boolean andOperator) {
1225    
1226                    return ddmStructureFinder.countByC_G_C_N_D_S_T(
1227                            companyId, groupIds, classNameIds, name, description, storageType,
1228                            type, andOperator);
1229            }
1230    
1231            /**
1232             * Updates the structure matching the structure ID, replacing its XSD with a
1233             * new one.
1234             *
1235             * @param  structureId the primary key of the structure
1236             * @param  definition the structure's new XML schema definition
1237             * @param  serviceContext the service context to be applied. Can set the
1238             *         structure's modification date.
1239             * @return the updated structure
1240             * @throws PortalException if a matching structure could not be found, if
1241             *         the XSD was not well-formed, or if a portal exception occurred
1242             */
1243            @Override
1244            public DDMStructure updateDefinition(
1245                            long structureId, String definition, ServiceContext serviceContext)
1246                    throws PortalException {
1247    
1248                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
1249                            structureId);
1250    
1251                    return doUpdateStructure(
1252                            structure.getParentStructureId(), structure.getNameMap(),
1253                            structure.getDescriptionMap(), definition, serviceContext,
1254                            structure);
1255            }
1256    
1257            /**
1258             * Updates the structure matching the class name ID, structure key, and
1259             * group, replacing its old parent structure, name map, description map, and
1260             * XSD with new ones.
1261             *
1262             * @param  groupId the primary key of the group
1263             * @param  parentStructureId the primary key of the new parent structure
1264             * @param  classNameId the primary key of the class name for the structure's
1265             *         related model
1266             * @param  structureKey the unique string identifying the structure
1267             * @param  nameMap the structure's new locales and localized names
1268             * @param  descriptionMap the structure's new locales and localized
1269             *         description
1270             * @param  definition the structure's new XML schema definition
1271             * @param  serviceContext the service context to be applied. Can set the
1272             *         structure's modification date.
1273             * @return the updated structure
1274             * @throws PortalException if a matching structure could not be found, if
1275             *         the XSD was not well-formed, or if a portal exception occurred
1276             */
1277            @Override
1278            public DDMStructure updateStructure(
1279                            long groupId, long parentStructureId, long classNameId,
1280                            String structureKey, Map<Locale, String> nameMap,
1281                            Map<Locale, String> descriptionMap, String definition,
1282                            ServiceContext serviceContext)
1283                    throws PortalException {
1284    
1285                    structureKey = getStructureKey(structureKey);
1286    
1287                    DDMStructure structure = ddmStructurePersistence.findByG_C_S(
1288                            groupId, classNameId, structureKey);
1289    
1290                    return doUpdateStructure(
1291                            parentStructureId, nameMap, descriptionMap, definition,
1292                            serviceContext, structure);
1293            }
1294    
1295            /**
1296             * Updates the structure matching the structure ID, replacing its old parent
1297             * structure, name map, description map, and XSD with new ones.
1298             *
1299             * @param  structureId the primary key of the structure
1300             * @param  parentStructureId the primary key of the new parent structure
1301             * @param  nameMap the structure's new locales and localized names
1302             * @param  descriptionMap the structure's new locales and localized
1303             *         descriptions
1304             * @param  definition the structure's new XML schema definition
1305             * @param  serviceContext the service context to be applied. Can set the
1306             *         structure's modification date.
1307             * @return the updated structure
1308             * @throws PortalException if a matching structure could not be found, if
1309             *         the XSD was not well-formed, or if a portal exception occurred
1310             */
1311            @Override
1312            public DDMStructure updateStructure(
1313                            long structureId, long parentStructureId,
1314                            Map<Locale, String> nameMap, Map<Locale, String> descriptionMap,
1315                            String definition, ServiceContext serviceContext)
1316                    throws PortalException {
1317    
1318                    DDMStructure structure = ddmStructurePersistence.findByPrimaryKey(
1319                            structureId);
1320    
1321                    return doUpdateStructure(
1322                            parentStructureId, nameMap, descriptionMap, definition,
1323                            serviceContext, structure);
1324            }
1325    
1326            protected Set<Long> deleteStructures(List<DDMStructure> structures)
1327                    throws PortalException {
1328    
1329                    Set<Long> deletedStructureIds = new HashSet<Long>();
1330    
1331                    for (DDMStructure structure : structures) {
1332                            if (deletedStructureIds.contains(structure.getStructureId())) {
1333                                    continue;
1334                            }
1335    
1336                            if (!GroupThreadLocal.isDeleteInProcess()) {
1337                                    List<DDMStructure> childDDMStructures =
1338                                            ddmStructurePersistence.findByParentStructureId(
1339                                                    structure.getStructureId());
1340    
1341                                    deletedStructureIds.addAll(
1342                                            deleteStructures(childDDMStructures));
1343                            }
1344    
1345                            ddmStructureLocalService.deleteStructure(structure);
1346    
1347                            deletedStructureIds.add(structure.getStructureId());
1348                    }
1349    
1350                    return deletedStructureIds;
1351            }
1352    
1353            protected DDMStructure doUpdateStructure(
1354                            long parentStructureId, Map<Locale, String> nameMap,
1355                            Map<Locale, String> descriptionMap, String definition,
1356                            ServiceContext serviceContext, DDMStructure structure)
1357                    throws PortalException {
1358    
1359                    try {
1360                            definition = DDMXMLUtil.validateXML(definition);
1361                            definition = DDMXMLUtil.formatXML(definition);
1362                    }
1363                    catch (Exception e) {
1364                            throw new StructureDefinitionException();
1365                    }
1366    
1367                    DDMForm parentDDMForm = getParentDDMForm(parentStructureId);
1368    
1369                    validate(nameMap, parentDDMForm, definition);
1370    
1371                    structure.setModifiedDate(serviceContext.getModifiedDate(null));
1372                    structure.setParentStructureId(parentStructureId);
1373                    structure.setNameMap(nameMap);
1374                    structure.setDescriptionMap(descriptionMap);
1375                    structure.setDefinition(definition);
1376    
1377                    ddmStructurePersistence.update(structure);
1378    
1379                    syncStructureTemplatesFields(structure);
1380    
1381                    Indexer indexer = IndexerRegistryUtil.getIndexer(
1382                            structure.getClassName());
1383    
1384                    if (indexer != null) {
1385                            List<Long> ddmStructureIds = getChildrenStructureIds(
1386                                    structure.getGroupId(), structure.getStructureId());
1387    
1388                            indexer.reindexDDMStructures(ddmStructureIds);
1389                    }
1390    
1391                    return structure;
1392            }
1393    
1394            protected void getChildrenStructureIds(
1395                            List<Long> structureIds, long groupId, long parentStructureId)
1396                    throws PortalException {
1397    
1398                    List<DDMStructure> structures = ddmStructurePersistence.findByG_P(
1399                            groupId, parentStructureId);
1400    
1401                    for (DDMStructure structure : structures) {
1402                            structureIds.add(structure.getStructureId());
1403    
1404                            getChildrenStructureIds(
1405                                    structureIds, structure.getGroupId(),
1406                                    structure.getStructureId());
1407                    }
1408            }
1409    
1410            protected List<Long> getChildrenStructureIds(long groupId, long structureId)
1411                    throws PortalException {
1412    
1413                    List<Long> structureIds = new ArrayList<Long>();
1414    
1415                    getChildrenStructureIds(structureIds, groupId, structureId);
1416    
1417                    structureIds.add(0, structureId);
1418    
1419                    return structureIds;
1420            }
1421    
1422            protected Set<String> getDDMFormFieldsNames(DDMForm ddmForm) {
1423                    Map<String, DDMFormField> ddmFormFieldsMap =
1424                            ddmForm.getDDMFormFieldsMap(true);
1425    
1426                    Set<String> ddmFormFieldsNames = new HashSet<String>(
1427                            ddmFormFieldsMap.size());
1428    
1429                    for (String ddmFormFieldName : ddmFormFieldsMap.keySet()) {
1430                            ddmFormFieldsNames.add(StringUtil.toLowerCase(ddmFormFieldName));
1431                    }
1432    
1433                    return ddmFormFieldsNames;
1434            }
1435    
1436            protected Set<String> getElementNames(Document document) {
1437                    Set<String> elementNames = new HashSet<String>();
1438    
1439                    XPath xPathSelector = SAXReaderUtil.createXPath("//dynamic-element");
1440    
1441                    List<Node> nodes = xPathSelector.selectNodes(document);
1442    
1443                    for (Node node : nodes) {
1444                            Element element = (Element)node;
1445    
1446                            String name = StringUtil.toLowerCase(
1447                                    element.attributeValue("name"));
1448    
1449                            elementNames.add(name);
1450                    }
1451    
1452                    return elementNames;
1453            }
1454    
1455            protected DDMForm getParentDDMForm(long parentStructureId) {
1456                    DDMStructure parentStructure =
1457                            ddmStructurePersistence.fetchByPrimaryKey(parentStructureId);
1458    
1459                    if (parentStructure == null) {
1460                            return null;
1461                    }
1462    
1463                    return parentStructure.getFullHierarchyDDMForm();
1464            }
1465    
1466            protected String getStructureKey(String structureKey) {
1467                    if (structureKey != null) {
1468                            structureKey = structureKey.trim();
1469    
1470                            return StringUtil.toUpperCase(structureKey);
1471                    }
1472    
1473                    return StringPool.BLANK;
1474            }
1475    
1476            protected List<DDMTemplate> getStructureTemplates(
1477                    DDMStructure structure, String type) {
1478    
1479                    long classNameId = classNameLocalService.getClassNameId(
1480                            DDMStructure.class);
1481    
1482                    return ddmTemplateLocalService.getTemplates(
1483                            structure.getGroupId(), classNameId, structure.getStructureId(),
1484                            type);
1485            }
1486    
1487            protected void syncStructureTemplatesFields(final DDMStructure structure) {
1488                    TransactionCommitCallbackRegistryUtil.registerCallback(
1489                            new Callable<Void>() {
1490    
1491                                    @Override
1492                                    public Void call() throws Exception {
1493                                            DDMFormTemplateSynchonizer ddmFormTemplateSynchonizer =
1494                                                    new DDMFormTemplateSynchonizer(structure.getDDMForm());
1495    
1496                                            List<DDMTemplate> templates = getStructureTemplates(
1497                                                    structure, DDMTemplateConstants.TEMPLATE_TYPE_FORM);
1498    
1499                                            ddmFormTemplateSynchonizer.setDDMFormTemplates(templates);
1500    
1501                                            ddmFormTemplateSynchonizer.synchronize();
1502    
1503                                            return null;
1504                                    }
1505    
1506                            });
1507            }
1508    
1509            protected void validate(DDMForm parentDDMForm, Document childDocument)
1510                    throws PortalException {
1511    
1512                    Set<String> parentElementNames = getDDMFormFieldsNames(parentDDMForm);
1513    
1514                    for (String childElementName : getElementNames(childDocument)) {
1515                            if (parentElementNames.contains(childElementName)) {
1516                                    throw new StructureDuplicateElementException();
1517                            }
1518                    }
1519            }
1520    
1521            protected void validate(Document document) throws PortalException {
1522                    XPath xPathSelector = SAXReaderUtil.createXPath("//dynamic-element");
1523    
1524                    List<Node> nodes = xPathSelector.selectNodes(document);
1525    
1526                    Set<String> elementNames = new HashSet<String>();
1527    
1528                    for (Node node : nodes) {
1529                            Element element = (Element)node;
1530    
1531                            String name = StringUtil.toLowerCase(
1532                                    element.attributeValue("name"));
1533    
1534                            if (name.startsWith(DDMStructureConstants.XSD_NAME_RESERVED)) {
1535                                    throw new StructureDefinitionException();
1536                            }
1537    
1538                            if (elementNames.contains(name)) {
1539                                    throw new StructureDuplicateElementException();
1540                            }
1541    
1542                            elementNames.add(name);
1543                    }
1544            }
1545    
1546            protected void validate(
1547                            long groupId, long parentStructureId, long classNameId,
1548                            String structureKey, Map<Locale, String> nameMap, String definition)
1549                    throws PortalException {
1550    
1551                    structureKey = getStructureKey(structureKey);
1552    
1553                    DDMStructure structure = ddmStructurePersistence.fetchByG_C_S(
1554                            groupId, classNameId, structureKey);
1555    
1556                    if (structure != null) {
1557                            StructureDuplicateStructureKeyException sdske =
1558                                    new StructureDuplicateStructureKeyException();
1559    
1560                            sdske.setStructureKey(structure.getStructureKey());
1561    
1562                            throw sdske;
1563                    }
1564    
1565                    DDMForm parentDDMForm = getParentDDMForm(parentStructureId);
1566    
1567                    validate(nameMap, parentDDMForm, definition);
1568            }
1569    
1570            protected void validate(
1571                            Map<Locale, String> nameMap, DDMForm parentDDMForm,
1572                            String childDefinition)
1573                    throws PortalException {
1574    
1575                    try {
1576                            Document document = SAXReaderUtil.read(childDefinition);
1577    
1578                            Element rootElement = document.getRootElement();
1579    
1580                            Locale contentDefaultLocale = LocaleUtil.fromLanguageId(
1581                                    rootElement.attributeValue("default-locale"));
1582    
1583                            validate(nameMap, contentDefaultLocale);
1584    
1585                            validate(document);
1586    
1587                            if (parentDDMForm != null) {
1588                                    validate(parentDDMForm, document);
1589                            }
1590                    }
1591                    catch (LocaleException le) {
1592                            throw le;
1593                    }
1594                    catch (StructureDuplicateElementException sdee) {
1595                            throw sdee;
1596                    }
1597                    catch (StructureNameException sne) {
1598                            throw sne;
1599                    }
1600                    catch (StructureDefinitionException sde) {
1601                            throw sde;
1602                    }
1603                    catch (Exception e) {
1604                            throw new StructureDefinitionException();
1605                    }
1606            }
1607    
1608            protected void validate(
1609                            Map<Locale, String> nameMap, Locale contentDefaultLocale)
1610                    throws PortalException {
1611    
1612                    String name = nameMap.get(contentDefaultLocale);
1613    
1614                    if (Validator.isNull(name)) {
1615                            throw new StructureNameException();
1616                    }
1617    
1618                    Locale[] availableLocales = LanguageUtil.getAvailableLocales();
1619    
1620                    if (!ArrayUtil.contains(availableLocales, contentDefaultLocale)) {
1621                            Long companyId = CompanyThreadLocal.getCompanyId();
1622    
1623                            LocaleException le = new LocaleException(
1624                                    LocaleException.TYPE_CONTENT,
1625                                    "The locale " + contentDefaultLocale +
1626                                            " is not available in company " + companyId);
1627    
1628                            le.setSourceAvailableLocales(new Locale[] {contentDefaultLocale});
1629                            le.setTargetAvailableLocales(availableLocales);
1630    
1631                            throw le;
1632                    }
1633            }
1634    
1635    }