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.web.repository.page.jcr;
017
018import java.util.ArrayList;
019import java.util.Collection;
020import java.util.Collections;
021import java.util.HashSet;
022import java.util.List;
023import java.util.NoSuchElementException;
024import java.util.Set;
025
026import javax.jcr.ItemExistsException;
027import javax.jcr.Node;
028import javax.jcr.RepositoryException;
029import javax.jcr.Value;
030
031import org.apache.commons.lang.StringUtils;
032import org.slf4j.Logger;
033import org.slf4j.LoggerFactory;
034
035import org.ametys.cms.tag.jcr.TaggableAmetysObjectHelper;
036import org.ametys.plugins.explorer.resources.ResourceCollection;
037import org.ametys.plugins.explorer.resources.jcr.JCRResourcesCollectionFactory;
038import org.ametys.plugins.repository.AmetysObject;
039import org.ametys.plugins.repository.AmetysObjectIterable;
040import org.ametys.plugins.repository.AmetysObjectIterator;
041import org.ametys.plugins.repository.AmetysRepositoryException;
042import org.ametys.plugins.repository.CollectionIterable;
043import org.ametys.plugins.repository.CopiableAmetysObject;
044import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
045import org.ametys.plugins.repository.RepositoryConstants;
046import org.ametys.plugins.repository.RepositoryIntegrityViolationException;
047import org.ametys.plugins.repository.TraversableAmetysObject;
048import org.ametys.plugins.repository.UnknownAmetysObjectException;
049import org.ametys.plugins.repository.data.holder.ModifiableModelLessDataHolder;
050import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelLessDataHolder;
051import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
052import org.ametys.plugins.repository.data.repositorydata.impl.JCRRepositoryData;
053import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
054import org.ametys.plugins.repository.jcr.SimpleAmetysObject;
055import org.ametys.plugins.repository.metadata.ModifiableCompositeMetadata;
056import org.ametys.runtime.model.type.ModelItemTypeConstants;
057import org.ametys.web.repository.page.ModifiablePage;
058import org.ametys.web.repository.page.ModifiableZone;
059import org.ametys.web.repository.page.MoveablePage;
060import org.ametys.web.repository.page.Page;
061import org.ametys.web.repository.page.PagesContainer;
062import org.ametys.web.repository.page.UnknownZoneException;
063import org.ametys.web.repository.site.Site;
064import org.ametys.web.repository.sitemap.Sitemap;
065
066/**
067 * {@link Page} implementation stored into a JCR node using
068 * <code>ametys:defaultPage</code> node type.
069 * 
070 * @param <F> the actual type of factory.
071 */
072public class DefaultPage<F extends DefaultPageFactory> extends DefaultTraversableAmetysObject<F> implements ModifiablePage, CopiableAmetysObject, MoveablePage
073{
074    /** Constant for title metadata. */
075    public static final String METADATA_TITLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":title";
076
077    /** Constant for long title metadata. */
078    public static final String METADATA_LONG_TITLE = "long-title";
079
080    /** Constant for title metadata. */
081    public static final String METADATA_TYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":type";
082
083    /** Constant for title metadata. */
084    public static final String METADATA_URL = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":url";
085
086    /** Constant for title metadata. */
087    public static final String METADATA_URLTYPE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":url-type";
088
089    /** Constant for template metadata. */
090    public static final String METADATA_TEMPLATE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":template";
091
092    /** Constant for referers metadata. */
093    public static final String METADATA_REFERERS = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":referers";
094    
095    /** Constant for the visible attribute. */
096    public static final String METADATA_VISIBLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":visible";
097
098    /** Constant for robots metadata. */
099    public static final String METADATA_ROBOTS_DISALLOW = "robots-disallow";
100    
101    /** Constant for the zones node name. */
102    public static final String ZONES_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":zones";
103
104    /** Constant for the zones node type. */
105    public static final String ZONES_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":zones";
106
107    /** Constant for the zone node type. */
108    public static final String ZONE_NODE_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":zone";
109    
110    /** Constant for the attachment node name. */
111    public static final String ATTACHMENTS_NODE_NAME = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":attachments";
112    
113    /** Constants for site Metadata* */
114    public static final String METADATA_SITE = "site";
115    
116    /** Constants for sitemap Metadata* */
117    public static final String METADATA_SITEMAP = "sitemap";
118
119    /** Constant for publication start date metadata. */
120    public static final String METADATA_PUBLICATION_START_DATE = "publicationStartDate";
121    
122    /** Constant for publication end date metadata. */
123    public static final String METADATA_PUBLICATION_END_DATE = "publicationEndDate";
124    
125    private static final Logger __LOGGER = LoggerFactory.getLogger(DefaultPage.class);
126    
127    /**
128     * Creates a {@link DefaultPage}.
129     * 
130     * @param node the node backing this {@link AmetysObject}.
131     * @param parentPath the parent path in the Ametys hierarchy.
132     * @param factory the {@link DefaultPageFactory} which creates the
133     *            AmetysObject.
134     */
135    public DefaultPage(Node node, String parentPath, F factory)
136    {
137        super(node, parentPath, factory);
138    }
139
140    @Override
141    public Set<String> getTags() throws AmetysRepositoryException
142    {
143        return TaggableAmetysObjectHelper.getTags(this);
144    }
145    
146    @Override
147    public void tag(String tag) throws AmetysRepositoryException
148    {
149        TaggableAmetysObjectHelper.tag(this, tag);
150    }
151
152    @Override
153    public void untag(String tag) throws AmetysRepositoryException
154    {
155        TaggableAmetysObjectHelper.untag(this, tag);
156    }
157
158    @Override
159    public String getTitle() throws AmetysRepositoryException
160    {
161        try
162        {
163            return getNode().getProperty(METADATA_TITLE).getString();
164        }
165        catch (RepositoryException e)
166        {
167            throw new AmetysRepositoryException("Unable to get title property", e);
168        }
169    }
170
171    @Override
172    public void setTitle(String title) throws AmetysRepositoryException
173    {
174        try
175        {
176            getNode().setProperty(METADATA_TITLE, title);
177        }
178        catch (RepositoryException e)
179        {
180            throw new AmetysRepositoryException("Unable to set title property", e);
181        }
182    }
183
184    @Override
185    public String getLongTitle() throws AmetysRepositoryException
186    {
187        String longTitle = getValue(METADATA_LONG_TITLE);
188        if (StringUtils.isNotBlank(longTitle))
189        {
190            return longTitle;
191        }
192        else
193        {
194            return this.getTitle();
195        }
196    }
197
198    @Override
199    public void setLongTitle(String title) throws AmetysRepositoryException
200    {
201        String newTitle = StringUtils.trimToNull(title);
202        setValue(METADATA_LONG_TITLE, newTitle, ModelItemTypeConstants.STRING_TYPE_ID);
203    }
204
205    @Override
206    public Sitemap getSitemap() throws AmetysRepositoryException
207    {
208        try
209        {
210            Node node = getNode();
211
212            do
213            {
214                node = node.getParent();
215            }
216            while (!node.getPrimaryNodeType().getName().equals(Sitemap.NODE_TYPE));
217
218            return _getFactory().resolveAmetysObject(node);
219        }
220        catch (RepositoryException e)
221        {
222            throw new AmetysRepositoryException("Unable to get sitemap", e);
223        }
224    }
225    
226    @Override
227    public int getDepth() throws AmetysRepositoryException
228    {
229        try
230        {
231            int depth = 1;
232            Node node = getNode().getParent();
233            while (!node.getPrimaryNodeType().getName().equals(Sitemap.NODE_TYPE))
234            {
235                depth++;
236                node = node.getParent();
237            }
238
239            return depth;
240        }
241        catch (RepositoryException e)
242        {
243            throw new AmetysRepositoryException("Unable to get depth", e);
244        }
245    }
246
247    @Override
248    public Site getSite() throws AmetysRepositoryException
249    {
250        return getSitemap().getSite();
251    }
252    
253    @Override
254    public String getSiteName()
255    {
256        return getValue(METADATA_SITE);
257    }
258    
259    @Override
260    public void setSiteName(String siteName)
261    {
262        setValue(METADATA_SITE, siteName);
263    }
264    
265    @Override
266    public String getSitemapName() throws AmetysRepositoryException
267    {
268        return getValue(METADATA_SITEMAP);
269    }
270    
271    @Override
272    public void setSitemapName(String sitemapName) throws AmetysRepositoryException
273    {
274        setValue(METADATA_SITEMAP, sitemapName);
275    }
276
277    @Override
278    public String getPathInSitemap() throws AmetysRepositoryException
279    {
280        String nodePath = getPath();
281        String sitemapPath = getSitemap().getPath();
282        return nodePath.substring(sitemapPath.length() + 1);
283    }
284
285    @Override
286    public PageType getType() throws AmetysRepositoryException
287    {
288        try
289        {
290            return PageType.valueOf(getNode().getProperty(METADATA_TYPE).getString());
291        }
292        catch (RepositoryException e)
293        {
294            throw new AmetysRepositoryException("Unable to get type property", e);
295        }
296    }
297
298    @Override
299    public void setType(PageType type) throws AmetysRepositoryException
300    {
301        try
302        {
303            getNode().setProperty(METADATA_TYPE, type.name());
304        }
305        catch (RepositoryException e)
306        {
307            throw new AmetysRepositoryException("Unable to set type property", e);
308        }
309    }
310
311    @Override
312    public String getURL() throws AmetysRepositoryException
313    {
314        try
315        {
316            return getNode().getProperty(METADATA_URL).getString();
317        }
318        catch (RepositoryException e)
319        {
320            throw new AmetysRepositoryException("Unable to get url property", e);
321        }
322    }
323    
324    @Override
325    public LinkType getURLType() throws AmetysRepositoryException
326    {
327        try
328        {
329            return LinkType.valueOf(getNode().getProperty(METADATA_URLTYPE).getString());
330        }
331        catch (RepositoryException e)
332        {
333            throw new AmetysRepositoryException("Unable to get url type property", e);
334        }
335    }
336
337    @Override
338    public void setURL(LinkType type, String url) throws AmetysRepositoryException
339    {
340        try
341        {
342            getNode().setProperty(METADATA_URLTYPE, type.toString());
343            getNode().setProperty(METADATA_URL, url);
344        }
345        catch (RepositoryException e)
346        {
347            throw new AmetysRepositoryException("Unable to set url property", e);
348        }
349    }
350
351    @Override
352    public String getTemplate() throws AmetysRepositoryException
353    {
354        try
355        {
356            Node node = getNode();
357            
358            if (node.hasProperty(METADATA_TEMPLATE))
359            {
360                return node.getProperty(METADATA_TEMPLATE).getString();
361            }
362            else
363            {
364                return null;
365            }
366        }
367        catch (RepositoryException e)
368        {
369            throw new AmetysRepositoryException("Unable to get template property", e);
370        }
371    }
372
373    @Override
374    public void setTemplate(String template) throws AmetysRepositoryException
375    {
376        try
377        {
378            getNode().setProperty(METADATA_TEMPLATE, template);
379        }
380        catch (RepositoryException e)
381        {
382            throw new AmetysRepositoryException("Unable to set template property", e);
383        }
384    }
385
386    @Override
387    public AmetysObjectIterable<ModifiableZone> getZones() throws AmetysRepositoryException
388    {
389        try
390        {
391            return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).getChildren();
392        }
393        catch (UnknownAmetysObjectException e)
394        {
395            Collection<ModifiableZone> emptyCollection = Collections.emptyList();
396            return new CollectionIterable<>(emptyCollection);
397        }
398    }
399
400    @Override
401    public boolean hasZone(String name) throws AmetysRepositoryException
402    {
403        if (hasChild(ZONES_NODE_NAME))
404        {
405            return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).hasChild(name);
406        }
407        else
408        {
409            return false;
410        }
411    }
412    
413    @Override
414    public ModifiableZone getZone(String name) throws UnknownZoneException, AmetysRepositoryException
415    {
416        return ((TraversableAmetysObject) getChild(ZONES_NODE_NAME)).getChild(name);
417    }
418
419    @Override
420    public ModifiableZone createZone(String name) throws AmetysRepositoryException
421    {
422        ModifiableTraversableAmetysObject zones;
423        if (hasChild(ZONES_NODE_NAME))
424        {
425            zones = getChild(ZONES_NODE_NAME);
426        }
427        else
428        {
429            zones = createChild(ZONES_NODE_NAME, ZONES_NODE_TYPE);
430        }
431        
432        return zones.createChild(name, ZONE_NODE_TYPE); 
433    }
434
435    @Override
436    public ResourceCollection getRootAttachments() throws AmetysRepositoryException
437    {
438        ResourceCollection attachments;
439        if (hasChild(ATTACHMENTS_NODE_NAME))
440        {
441            attachments = getChild(ATTACHMENTS_NODE_NAME);
442        }
443        else
444        {
445            attachments = createChild(ATTACHMENTS_NODE_NAME, JCRResourcesCollectionFactory.RESOURCESCOLLECTION_NODETYPE);
446            
447            try
448            {
449                getNode().getSession().save();
450            }
451            catch (RepositoryException e)
452            {
453                throw new AmetysRepositoryException("Unable to save session", e);
454            }
455        }
456        
457        return attachments;
458    }
459    
460    @Override
461    public Set<String> getReferers() throws AmetysRepositoryException
462    {
463        try
464        {
465            Node node = getNode();
466
467            if (!node.hasProperty(METADATA_REFERERS))
468            {
469                return Collections.emptySet();
470            }
471
472            Value[] values = getNode().getProperty(METADATA_REFERERS).getValues();
473            Set<String> ids = new HashSet<>(values.length);
474
475            for (Value value : values)
476            {
477                ids.add(value.getString());
478            }
479
480            return ids;
481        }
482        catch (RepositoryException e)
483        {
484            throw new AmetysRepositoryException("Unable to get referers property", e);
485        }
486    }
487
488    @Override
489    public void addReferer(String ametysObjectId) throws AmetysRepositoryException
490    {
491        try
492        {
493            Set<String> ids = null;
494            Node node = getNode();
495
496            if (!node.hasProperty(METADATA_REFERERS))
497            {
498                ids = Collections.singleton(ametysObjectId);
499            }
500            else
501            {
502                Value[] values = getNode().getProperty(METADATA_REFERERS).getValues();
503                ids = new HashSet<>(values.length + 1);
504
505                for (Value value : values)
506                {
507                    ids.add(value.getString());
508                }
509
510                ids.add(ametysObjectId);
511            }
512
513            node.setProperty(METADATA_REFERERS, ids.toArray(new String[ids.size()]));
514        }
515        catch (RepositoryException e)
516        {
517            throw new AmetysRepositoryException("Unable to add an id into referers property", e);
518        }
519    }
520
521    @Override
522    public void removeReferer(String ametysObjectId) throws AmetysRepositoryException
523    {
524        try
525        {
526            Node node = getNode();
527
528            if (node.hasProperty(METADATA_REFERERS))
529            {
530                Value[] values = getNode().getProperty(METADATA_REFERERS).getValues();
531                Set<String> ids = new HashSet<>(values.length + 1);
532
533                for (Value value : values)
534                {
535                    ids.add(value.getString());
536                }
537
538                ids.remove(ametysObjectId);
539                node.setProperty(METADATA_REFERERS, ids.toArray(new String[ids.size()]));
540            }
541        }
542        catch (RepositoryException e)
543        {
544            throw new AmetysRepositoryException("Unable to remove an id from referers property", e);
545        }
546    }
547
548    @Override
549    public AmetysObjectIterable< ? extends Page> getChildrenPages() throws AmetysRepositoryException
550    {
551        return getChildrenPages(true);
552    }
553    
554    @Override
555    public AmetysObjectIterable< ? extends Page> getChildrenPages(boolean includeInvisiblePage) throws AmetysRepositoryException
556    {
557        List<Page> childrenPages = new ArrayList<>();
558        for (AmetysObject childObject : getChildren())
559        {
560            if (childObject instanceof Page)
561            {
562                Page page = (Page) childObject;
563                if (includeInvisiblePage || page.isVisible())
564                {
565                    childrenPages.add(page);
566                }
567            }
568        }
569        return new CollectionIterable<>(childrenPages);
570    }
571    
572    @Override
573    public Page getChildPageAt(int index) throws UnknownAmetysObjectException, AmetysRepositoryException
574    {
575        if (index < 0)
576        {
577            throw new AmetysRepositoryException("Child page index cannot be negative");
578        }
579        
580        AmetysObjectIterable< ? extends Page> childPages = getChildrenPages();
581        AmetysObjectIterator< ? extends Page> it = childPages.iterator();
582        
583        try
584        {
585            it.skip(index);
586            return it.next();
587        }
588        catch (NoSuchElementException e)
589        {
590            throw new UnknownAmetysObjectException("There's no child page at index " + index + " for page " + this.getId());
591        }
592    }
593
594    @Override
595    public DefaultPage copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException
596    {
597        try
598        {
599            int index = 1;
600            String originalName = name == null ? getName() : name;
601            String pageName = originalName;
602            while (parent.hasChild(pageName))
603            {
604                // Find unused name on new parent node
605                pageName = originalName + "-" + index++;
606            }
607            
608            DefaultPage cPage = parent.createChild(pageName, "ametys:defaultPage");
609            cPage.setType(getType());
610            cPage.setTitle(getTitle());
611            
612            if (PageType.CONTAINER.equals(getType()))
613            {
614                cPage.setTemplate(getTemplate());
615            }
616            else if (PageType.LINK.equals(getType()))
617            {
618                cPage.setURL(getURLType(), getURL());
619            }
620            
621            // Copy metadata
622            copyTo(cPage);
623            
624            // Copy zones
625            for (ModifiableZone zone : getZones())
626            {
627                if (zone instanceof CopiableAmetysObject)
628                {
629                    ((CopiableAmetysObject) zone).copyTo(cPage, null);
630                }
631            }
632            
633            // Copy tags
634            Set<String> tags = getTags();
635            for (String tag : tags)
636            {
637                cPage.tag(tag);
638            }
639            
640            // Update sitemap name
641            if (parent instanceof PagesContainer)
642            {
643                cPage.setSitemapName(((PagesContainer) parent).getSitemapName());
644            }
645            
646            parent.saveChanges();
647            
648            // Copy attachments
649            ResourceCollection rootAttachments = getRootAttachments();
650            if (rootAttachments instanceof SimpleAmetysObject)
651            {
652                Node resourcesNode = ((SimpleAmetysObject) rootAttachments).getNode();
653                getNode().getSession().getWorkspace().copy(resourcesNode.getPath(), cPage.getNode().getPath() + "/" + resourcesNode.getName());
654            }
655            
656            // Copy sub-pages
657            AmetysObjectIterable< ? extends Page> childrenPages = getChildrenPages();
658            for (Page page : childrenPages)
659            {
660                // Avoid infinite loop : do not copy the page itself
661                if (page instanceof CopiableAmetysObject && restrictTo.contains(page.getId()))
662                {
663                    ((CopiableAmetysObject) page).copyTo(cPage, null, restrictTo);
664                }
665            }
666            
667            return cPage;
668        }
669        catch (RepositoryException e)
670        {
671            throw new AmetysRepositoryException(e);
672        }
673    }
674    
675    @Override
676    public DefaultPage copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException
677    {
678        return copyTo (parent, name, new ArrayList<String>());
679    }
680    
681    @Override
682    public void moveTo(AmetysObject newParent, boolean renameIfExist) throws AmetysRepositoryException, RepositoryIntegrityViolationException
683    {
684        Node node = getNode();
685
686        try
687        {
688            if (getParent().equals(newParent))
689            {
690                // Just order current node to the end
691                node.getParent().orderBefore(node.getName(), null);
692            }
693            else
694            {
695                if (!canMoveTo(newParent))
696                {
697                    throw new AmetysRepositoryException("DefaultPage " + toString() + " can only be moved to a CompositePages and ");
698                }
699                PagesContainer newParentPage = (PagesContainer) newParent;
700                
701                String sitemapNodePath = getSitemap().getNode().getPath();
702                String newPath = sitemapNodePath + (newParentPage instanceof Sitemap ? "" : '/' + newParentPage.getPathInSitemap());
703
704                String name = node.getName();
705                
706                if (renameIfExist)
707                {
708                    // Find unused name on new parent node
709                    int index = 1;
710                    while (node.getSession().getRootNode().hasNode(newPath.substring(1) + "/" + name))
711                    {
712                        name = name + "-" + index++;
713                    }
714                }
715
716                try
717                {
718                    // Move node
719                    node.getSession().move(node.getPath(), newPath + "/" + name);
720                }
721                catch (ItemExistsException e)
722                {
723                    throw new AmetysRepositoryException(String.format("A page already exists for in parent path '%s'", newPath), e);
724                }
725                
726                // recompute name in case it has changed
727                _invalidateName();
728
729                // Invalidate parent path as the parent path has changed
730                _invalidateParentPath();
731            }
732        }
733        catch (RepositoryException e)
734        {
735            throw new AmetysRepositoryException(String.format("Unable to move page '%s' to node '%s'", this, newParent.getId()), e);
736        }
737    }
738    
739    @Override
740    public boolean canMoveTo(AmetysObject newParent) throws AmetysRepositoryException
741    {
742        return  newParent instanceof PagesContainer && ((PagesContainer) newParent).getSitemap().equals(getSitemap());
743    }
744
745    @Override
746    public void orderBefore(AmetysObject siblingNode) throws AmetysRepositoryException
747    {
748        Node node = getNode();
749        try
750        {
751            node.getParent().orderBefore(node.getName(), siblingNode != null ? siblingNode.getName() : null);
752        }
753        catch (RepositoryException e)
754        {
755            throw new AmetysRepositoryException(String.format("Unable to order page '%s' before sibling '%s'", this, siblingNode != null ? siblingNode.getName() : ""), e);
756        }
757    }
758
759    @Override
760    public boolean isVisible() throws AmetysRepositoryException
761    {
762        try
763        {
764            if (getNode().hasProperty(METADATA_VISIBLE))
765            {
766                return getNode().getProperty(METADATA_VISIBLE).getBoolean();
767            }
768            else
769            {
770                return true;
771            }
772        }
773        catch (RepositoryException e)
774        {
775            throw new AmetysRepositoryException("Unable to get visible property", e);
776        }
777    }
778    
779    @Override
780    public void setVisible(boolean isVisible) throws AmetysRepositoryException
781    {
782        try
783        {
784            getNode().setProperty(METADATA_VISIBLE, isVisible);
785        }
786        catch (RepositoryException e)
787        {
788            throw new AmetysRepositoryException("Unable to set visible property", e);
789        }
790    }
791
792    public ModifiableModelLessDataHolder getDataHolder()
793    {
794        ModifiableRepositoryData repositoryData = new JCRRepositoryData(getNode());
795        return new DefaultModifiableModelLessDataHolder(_getFactory().getPageDataTypeExtensionPoint(), repositoryData);
796    }
797    
798    @Override
799    public ModifiableCompositeMetadata getMetadataHolder()
800    {
801        __LOGGER.warn("The getMetadataHolder method of DefaultPage is deprecated. Use the getDataHolder instead. StackTrace: ", new Exception());
802        return super.getMetadataHolder();
803    }
804}