001/*
002 *  Copyright 2010 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.workflow;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.LinkedHashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.commons.io.IOUtils;
031import org.apache.commons.lang.StringUtils;
032
033import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
034import org.ametys.cms.contenttype.ContentTypesHelper;
035import org.ametys.cms.contenttype.MetadataDefinition;
036import org.ametys.cms.contenttype.MetadataManager;
037import org.ametys.cms.contenttype.RepeaterDefinition;
038import org.ametys.cms.repository.Content;
039import org.ametys.cms.repository.ModifiableWorkflowAwareContent;
040import org.ametys.cms.repository.WorkflowAwareContent;
041import org.ametys.core.user.UserIdentity;
042import org.ametys.core.util.JSONUtils;
043import org.ametys.plugins.repository.AmetysObjectResolver;
044import org.ametys.plugins.repository.lock.LockHelper;
045import org.ametys.plugins.repository.lock.LockableAmetysObject;
046import org.ametys.plugins.repository.metadata.BinaryMetadata;
047import org.ametys.plugins.repository.metadata.CompositeMetadata;
048import org.ametys.plugins.repository.metadata.File;
049import org.ametys.plugins.repository.metadata.Folder;
050import org.ametys.plugins.repository.metadata.Resource;
051import org.ametys.plugins.repository.metadata.RichText;
052import org.ametys.plugins.repository.metadata.UnknownMetadataException;
053import org.ametys.plugins.repository.version.VersionableAmetysObject;
054import org.ametys.runtime.parameter.ParameterHelper;
055
056import com.opensymphony.module.propertyset.PropertySet;
057import com.opensymphony.workflow.WorkflowException;
058
059/**
060 * OSWorkflow function to restore an old revision of a content.
061 * Builds a Map with the old content's metadata values, and passes it to the
062 * {@link EditContentFunction}, which does the real job.
063 */
064public class RestoreRevisionFunction extends AbstractContentFunction
065{
066    
067    /** The ametys object resolver. */
068    protected AmetysObjectResolver _resolver;
069    
070    /** The content type extension point. */
071    protected ContentTypeExtensionPoint _cTypeEP;
072    
073    /** The content type helper. */
074    protected ContentTypesHelper _cTypeHelper;
075    
076    /** The JSON conversion utilities. */
077    protected JSONUtils _jsonUtils;
078    
079    @Override
080    public void service(ServiceManager manager) throws ServiceException
081    {
082        super.service(manager);
083        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
084        _cTypeEP = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
085        _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
086        _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE);
087    }
088    
089    @Override
090    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
091    {
092        WorkflowAwareContent content = getContent(transientVars);
093        UserIdentity user = getUser(transientVars);
094        
095        if (!(content instanceof ModifiableWorkflowAwareContent))
096        {
097            throw new IllegalArgumentException("The provided content " + content.getId() + " is not a ModifiableWorkflowAwareContent.");
098        }
099        
100        ModifiableWorkflowAwareContent modifiableContent = (ModifiableWorkflowAwareContent) content;
101        
102        if (content instanceof LockableAmetysObject)
103        {
104            LockableAmetysObject lockableContent = (LockableAmetysObject) content;
105            if (lockableContent.isLocked() && !LockHelper.isLockOwner(lockableContent, user))
106            {
107                throw new WorkflowException ("The user '" + user + "' try to restore the content '" + content.getId() + "', but this content is locked by the user '" + user + "'");
108            }
109            else if (lockableContent.isLocked())
110            {
111                lockableContent.unlock();
112            }
113        }
114        
115        String contentVersion = (String) getContextParameters(transientVars).get("contentVersion");
116        
117        Content oldContent = _resolver.resolveById(content.getId());
118        if (oldContent instanceof VersionableAmetysObject)
119        {
120            ((VersionableAmetysObject) oldContent).switchToRevision(contentVersion);
121        }
122        
123        Map<String, Object> results = getResultsMap(transientVars);
124        Map<String, Object> brokenReferences = new HashMap<>();
125        results.put("brokenReferences", brokenReferences);
126        
127        Map<String, Object> parameters = getContextParameters(transientVars);
128        Map<String, Object> newValues = new HashMap<>();
129        parameters.put(EditContentFunction.FORM_RAW_VALUES, newValues);
130        
131        parameters.put(EditContentFunction.QUIT, Boolean.TRUE);
132        
133        // Recursively get the old content's metadata values, and fill the "new values" map with them.
134        // Additionally, detect broken references.
135        processMetadatas(content, oldContent, newValues, brokenReferences);
136        
137        modifiableContent.setLastContributor(user);
138        modifiableContent.setLastModified(new Date());
139        // Remove the proposal date.
140        modifiableContent.setProposalDate(null);
141        
142        // Commit changes
143        modifiableContent.saveChanges();
144    }
145    
146    /**
147     * Process the old content metadatas.
148     * @param content the current content (the one being modified).
149     * @param oldContent the old content.
150     * @param newValues the Map to fill with the old content's values.
151     * @param brokenReferences the Map of broken references (which could not be restored).
152     * @throws WorkflowException if an error occurs.
153     */
154    protected void processMetadatas(Content content, Content oldContent, Map<String, Object> newValues, Map<String, Object> brokenReferences) throws WorkflowException
155    {
156        CompositeMetadata metaHolder = oldContent.getMetadataHolder();
157        
158        // Browse the current content's model.
159        Set<String> metadataNames = _cTypeHelper.getMetadataNames(content);
160        for (String metaName : metadataNames)
161        {
162            MetadataDefinition metaDef = _cTypeHelper.getMetadataDefinition(metaName, content);
163            
164            processMetadata(metaHolder, metaName, metaDef, metaName, newValues, brokenReferences);
165        }
166    }
167    
168    /**
169     * Process the old content metadatas.
170     * @param metaHolder the source metadata holder.
171     * @param metaDef the metadata definition.
172     * @param metaPath the metadata path.
173     * @param newValues the Map to fill with the old content's values.
174     * @param brokenReferences the Map of broken references (which could not be restored).
175     * @throws WorkflowException if an error occurs.
176     */
177    protected void processMetadatas(CompositeMetadata metaHolder, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues, Map<String, Object> brokenReferences) throws WorkflowException
178    {
179        Set<String> subMetadataNames = metaDef.getMetadataNames();
180        for (String subMetaName : subMetadataNames)
181        {
182            MetadataDefinition subMetaDef = metaDef.getMetadataDefinition(subMetaName);
183            String subMetaPath = metaPath + "." + subMetaName;
184            
185            processMetadata(metaHolder, subMetaName, subMetaDef, subMetaPath, newValues, brokenReferences);
186        }
187    }
188    
189    /**
190     * Process a metadata from the old content.
191     * @param metaHolder the source metadata holder.
192     * @param metaName the metadata name.
193     * @param metaDef the metadata definition.
194     * @param metaPath the metadata path.
195     * @param newValues the Map to fill with the old content's values.
196     * @param brokenReferences the Map of broken references (which could not be restored).
197     * @throws WorkflowException if an error occurs.
198     */
199    protected void processMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues, Map<String, Object> brokenReferences) throws WorkflowException
200    {
201        if (metaDef != null)
202        {
203            switch (metaDef.getType())
204            {
205                case STRING:
206                case LONG:
207                case DOUBLE:
208                case BOOLEAN:
209                    processStringMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
210                    break;
211                case USER:
212                    processUserMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
213                    break;
214                case DATE:
215                    processDateMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
216                    break;
217                case DATETIME:
218                    processDatetimeMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
219                    break;
220                case GEOCODE:
221                    processGeocodeMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
222                    break;
223                case RICH_TEXT:
224                    processRichtextMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
225                    break;
226                case BINARY:
227                    processBinaryMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
228                    break;
229                case FILE:
230                    processFileMetadata(metaHolder, metaName, metaDef, metaPath, newValues);
231                    break;
232                case REFERENCE:
233                    processReferenceMetadata(metaHolder, metaName, metaDef, metaPath, newValues, brokenReferences);
234                    break;
235                case CONTENT:
236                    processContentMetadata(metaHolder, metaName, metaDef, metaPath, newValues, brokenReferences);
237                    break;
238                case SUB_CONTENT:
239                    break;
240                case COMPOSITE:
241                    processCompositeMetadata(metaHolder, metaName, metaDef, metaPath, newValues, brokenReferences);
242                    break;
243                default:
244                    break;
245            }
246        }
247    }
248    
249    /**
250     * Get the value from a String old content metadata and put it in the new values map.
251     * @param metaHolder the source metadata holder.
252     * @param metaName the metadata name.
253     * @param metaDef the metadata definition.
254     * @param metaPath the metadata path.
255     * @param newValues the Map to fill with the old content's values.
256     */
257    protected void processStringMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
258    {
259        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
260        
261        if (metaDef.isMultiple())
262        {
263            String[] valuesArr = metaHolder.getStringArray(metaName, new String[0]);
264            newValues.put(valueKey, Arrays.asList(valuesArr));
265        }
266        else
267        {
268            String value = metaHolder.getString(metaName, null);
269            newValues.put(valueKey, value);
270        }
271    }
272    
273    /**
274     * Get the value from a Date old content metadata and put it in the new values map.
275     * @param metaHolder the source metadata holder.
276     * @param metaName the metadata name.
277     * @param metaDef the metadata definition.
278     * @param metaPath the metadata path.
279     * @param newValues the Map to fill with the old content's values.
280     */
281    protected void processDateMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
282    {
283        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
284        
285        if (metaDef.isMultiple())
286        {
287            Date[] valuesArr = metaHolder.getDateArray(metaName, new Date[0]);
288            List<String> values = new ArrayList<>();
289            for (Date value : valuesArr)
290            {
291                values.add(ParameterHelper.valueToString(value));
292            }
293            
294            newValues.put(valueKey, values);
295        }
296        else
297        {
298            Date value = metaHolder.getDate(metaName, null);
299            newValues.put(valueKey, StringUtils.trimToEmpty(ParameterHelper.valueToString(value)));
300        }
301    }
302    
303    /**
304     * Get the value from a DateTime old content metadata and put it in the new values map.
305     * @param metaHolder the source metadata holder.
306     * @param metaName the metadata name.
307     * @param metaDef the metadata definition.
308     * @param metaPath the metadata path.
309     * @param newValues the Map to fill with the old content's values.
310     */
311    protected void processDatetimeMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
312    {
313        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
314        
315        if (metaDef.isMultiple())
316        {
317            Date[] valuesArr = metaHolder.getDateArray(metaName, new Date[0]);
318            List<String> values = new ArrayList<>();
319            for (Date value : valuesArr)
320            {
321                values.add(ParameterHelper.valueToString(value));
322            }
323            
324            newValues.put(valueKey, values);
325        }
326        else
327        {
328            Date value = metaHolder.getDate(metaName, null);
329            newValues.put(valueKey, StringUtils.trimToEmpty(ParameterHelper.valueToString(value)));
330        }
331    }
332    
333    /**
334     * Get the value from a geocode old content metadata and put it in the new values map.
335     * @param metaHolder the source metadata holder.
336     * @param metaName the metadata name.
337     * @param metaDef the metadata definition.
338     * @param metaPath the metadata path.
339     * @param newValues the Map to fill with the old content's values.
340     */
341    protected void processGeocodeMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
342    {
343        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
344        
345        Map<String, Object> values = new HashMap<>();
346        
347        try
348        {
349            CompositeMetadata meta = metaHolder.getCompositeMetadata(metaName);
350            
351            values.put("longitude", meta.getDouble("longitude"));
352            values.put("latitude", meta.getDouble("latitude"));
353            
354            String jsonValues = _jsonUtils.convertObjectToJson(values);
355            
356            newValues.put(valueKey, jsonValues);
357        }
358        catch (UnknownMetadataException e)
359        {
360            // The composite metadata doesn't exist: store null
361            newValues.put(valueKey, null);
362        }
363    }
364    
365    /**
366     * Get the value from a user old content metadata and put it in the new values map.
367     * @param metaHolder the source metadata holder.
368     * @param metaName the metadata name.
369     * @param metaDef the metadata definition.
370     * @param metaPath the metadata path.
371     * @param newValues the Map to fill with the old content's values.
372     */
373    protected void processUserMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
374    {
375        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
376        
377        Map<String, Object> values = new HashMap<>();
378        
379        try
380        {
381            CompositeMetadata meta = metaHolder.getCompositeMetadata(metaName);
382            
383            values.put("login", meta.getString("login"));
384            values.put("populationId", meta.getString("populationId"));
385            
386            String jsonValues = _jsonUtils.convertObjectToJson(values);
387            
388            newValues.put(valueKey, jsonValues);
389        }
390        catch (UnknownMetadataException e)
391        {
392            // The composite metadata doesn't exist: store null
393            newValues.put(valueKey, null);
394        }
395    }
396    
397    /**
398     * Get the value from a RichText old content metadata and put it in the new values map.
399     * @param metaHolder the source metadata holder.
400     * @param metaName the metadata name.
401     * @param metaDef the metadata definition.
402     * @param metaPath the metadata path.
403     * @param newValues the Map to fill with the old content's values.
404     * @throws WorkflowException if an error occurs.
405     */
406    protected void processRichtextMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues) throws WorkflowException
407    {
408        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
409        String formatKey = EditContentFunction.INTERNAL_FORM_ELEMENTS_PREFIX + metaPath + ".format";
410        String addDataKey = EditContentFunction.INTERNAL_FORM_ELEMENTS_PREFIX + metaPath + ".additionalData";
411        
412        try
413        {
414            RichText richText = metaHolder.getRichText(metaName);
415            String docbookContent = IOUtils.toString(richText.getInputStream(), "UTF-8");
416            
417            Folder dataFolder = richText.getAdditionalDataFolder();
418            Map<String, Resource> datas = new LinkedHashMap<>();
419            for (File file : dataFolder.getFiles())
420            {
421                datas.put(file.getName(), file.getResource());
422            }
423            
424            newValues.put(valueKey, docbookContent);
425            newValues.put(formatKey, "docbook");
426            newValues.put(addDataKey, datas);
427        }
428        catch (UnknownMetadataException e)
429        {
430            // The rich-text metadata doesn't exist: store null
431            newValues.put(valueKey, null);
432        }
433        catch (IOException e)
434        {
435            // Error reading the rich-text content.
436            throw new WorkflowException("Error reading the rich-text content for metadata " + metaPath, e);
437        }
438    }
439    
440    /**
441     * Get the value from a binary old content metadata and put it in the new values map.
442     * @param metaHolder the source metadata holder.
443     * @param metaName the metadata name.
444     * @param metaDef the metadata definition.
445     * @param metaPath the metadata path.
446     * @param newValues the Map to fill with the old content's values.
447     */
448    protected void processBinaryMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
449    {
450        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
451        String rawValueKey = EditContentFunction.INTERNAL_FORM_ELEMENTS_PREFIX + metaPath + ".rawValue";
452        
453        if (metaHolder.hasMetadata(metaName))
454        {
455            BinaryMetadata value = metaHolder.getBinaryMetadata(metaName);
456            
457            newValues.put(valueKey, EditContentFunction.UNTOUCHED_BINARY);
458            newValues.put(rawValueKey, value);
459        }
460        else
461        {
462            newValues.put(valueKey, null);
463        }
464    }
465    
466    /**
467     * Get the value from a File old content metadata and put it in the new values map.
468     * @param metaHolder the source metadata holder.
469     * @param metaName the metadata name.
470     * @param metaDef the metadata definition.
471     * @param metaPath the metadata path.
472     * @param newValues the Map to fill with the old content's values.
473     */
474    protected void processFileMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues)
475    {
476        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
477        
478        if (metaHolder.hasMetadata(metaName))
479        {
480            Map<String, Object> values = new HashMap<>();
481            
482            if (org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType.BINARY.equals(metaHolder.getType(metaName)))
483            {
484                BinaryMetadata value = metaHolder.getBinaryMetadata(metaName);
485                values.put("type", "metadata");
486                values.put("id", "modified");
487                
488                String rawValueKey = EditContentFunction.INTERNAL_FORM_ELEMENTS_PREFIX + metaPath + ".rawValue";
489                newValues.put(rawValueKey, value);
490            }
491            else
492            {
493                // If it's not a binary, it's an explorer resource ID: process it as a simple string.
494                String value = metaHolder.getString(metaName, null);
495                values.put("type", "explorer");
496                values.put("id", value);
497            }
498            
499            String jsonValues = _jsonUtils.convertObjectToJson(values);
500            newValues.put(valueKey, jsonValues);
501        }
502        else
503        {
504            newValues.put(valueKey, null);
505        }
506    }
507    
508    /**
509     * Get the value from a Reference old content metadata and put it in the new values map.
510     * @param metaHolder the source metadata holder.
511     * @param metaName the metadata name.
512     * @param metaDef the metadata definition.
513     * @param metaPath the metadata path.
514     * @param newValues the Map to fill with the old content's values.
515     * @param brokenReferences the Map of broken references (which could not be restored). 
516     */
517    protected void processReferenceMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues, Map<String, Object> brokenReferences)
518    {
519        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
520        
521        Map<String, Object> values = new HashMap<>();
522        
523        try
524        {
525            CompositeMetadata meta = metaHolder.getCompositeMetadata(metaName);
526            
527            values.put("value", meta.getString("value"));
528            values.put("type", meta.getString("type"));
529            
530            String jsonValues = _jsonUtils.convertObjectToJson(values);
531            
532            newValues.put(valueKey, jsonValues);
533        }
534        catch (UnknownMetadataException e)
535        {
536            // The composite metadata doesn't exist: store null
537            newValues.put(valueKey, null);
538        }
539    }
540    
541    /**
542     * Get the value from a Content old content metadata and put it in the new values map.
543     * @param metaHolder the source metadata holder.
544     * @param metaName the metadata name.
545     * @param metaDef the metadata definition.
546     * @param metaPath the metadata path.
547     * @param newValues the Map to fill with the old content's values.
548     * @param brokenReferences the Map of broken references (which could not be restored).
549     */
550    protected void processContentMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues, Map<String, Object> brokenReferences)
551    {
552        String valueKey = EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath;
553        
554        if (metaDef.isMultiple())
555        {
556            String[] contentIds = metaHolder.getStringArray(metaName, new String[0]);
557            List<String> validIds = new ArrayList<>(contentIds.length);
558            
559            for (String value : contentIds)
560            {
561                if (_resolver.hasAmetysObjectForId(value))
562                {
563                    validIds.add(value);
564                }
565                else if (!brokenReferences.containsKey(metaName))
566                {
567                    brokenReferences.put(metaName, metaDef.getLabel());
568                }
569            }
570            
571            // Set the value (even if it's an empty array, to delete potentially existing values).
572            newValues.put(valueKey, validIds);
573        }
574        else
575        {
576            String value = metaHolder.getString(metaName, null);
577            if (value != null)
578            {
579                if (_resolver.hasAmetysObjectForId(value))
580                {
581                    newValues.put(valueKey, value);
582                }
583                else
584                {
585                    brokenReferences.put(metaName, metaDef.getLabel());
586                }
587            }
588            else
589            {
590                // Set the value even if it's null, to delete a potentially existing value.
591                newValues.put(valueKey, null);
592            }
593        }
594    }
595    
596    /**
597     * Get the value from a Composite old content metadata and put it in the new values map.
598     * @param metaHolder the source metadata holder.
599     * @param metaName the metadata name.
600     * @param metaDef the metadata definition.
601     * @param metaPath the metadata path.
602     * @param newValues the Map to fill with the old content's values.
603     * @param brokenReferences the Map of broken references (which could not be restored).
604     * @throws WorkflowException if an error occurs. 
605     */
606    protected void processCompositeMetadata(CompositeMetadata metaHolder, String metaName, MetadataDefinition metaDef, String metaPath, Map<String, Object> newValues, Map<String, Object> brokenReferences) throws WorkflowException
607    {
608        if (metaDef instanceof RepeaterDefinition)
609        {
610            if (metaHolder.hasMetadata(metaName))
611            {
612                CompositeMetadata repeaterMetaHolder = metaHolder.getCompositeMetadata(metaName);
613                
614                String[] entryNames = repeaterMetaHolder.getMetadataNames();
615                Arrays.sort(entryNames, MetadataManager.REPEATER_ENTRY_COMPARATOR);
616                
617                newValues.put("_" + EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath + ".size", Integer.toString(entryNames.length));
618                
619                if (entryNames.length < 1)
620                {
621                    newValues.put(EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath, null);
622                }
623                
624                for (String entryName : entryNames)
625                {
626                    CompositeMetadata entryMetaHolder = repeaterMetaHolder.getCompositeMetadata(entryName);
627                    String entryPath = metaPath + "." + entryName;
628                    
629                    processMetadatas(entryMetaHolder, metaDef, entryPath, newValues, brokenReferences);
630                }
631            }
632            else
633            {
634                newValues.put(EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath, null);
635                newValues.put("_" + EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath + ".size", "0");
636            }
637        }
638        else
639        {
640            if (metaHolder.hasMetadata(metaName))
641            {
642                CompositeMetadata compositeMetaHolder = metaHolder.getCompositeMetadata(metaName);
643                processMetadatas(compositeMetaHolder, metaDef, metaPath, newValues, brokenReferences);
644            }
645            else
646            {
647                newValues.put(EditContentFunction.FORM_ELEMENTS_PREFIX + metaPath, null);
648            }
649        }
650    }
651    
652}