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