/*
* Weblounge: Web Content Management System
* Copyright (c) 2003 - 2011 The Weblounge Team
* http://entwinemedia.com/weblounge
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package ch.entwine.weblounge.common.impl.content;
import static ch.entwine.weblounge.common.language.Localizable.LanguageResolution.Original;
import ch.entwine.weblounge.common.content.Resource;
import ch.entwine.weblounge.common.content.ResourceContent;
import ch.entwine.weblounge.common.content.ResourceURI;
import ch.entwine.weblounge.common.content.ResourceUtils;
import ch.entwine.weblounge.common.impl.content.page.PageSecurityContext;
import ch.entwine.weblounge.common.impl.language.LocalizableContent;
import ch.entwine.weblounge.common.impl.language.LocalizableObject;
import ch.entwine.weblounge.common.impl.security.SecurityContextImpl;
import ch.entwine.weblounge.common.impl.security.SecurityUtils;
import ch.entwine.weblounge.common.impl.security.SystemRole;
import ch.entwine.weblounge.common.language.Language;
import ch.entwine.weblounge.common.language.Localizable;
import ch.entwine.weblounge.common.security.Authority;
import ch.entwine.weblounge.common.security.Permission;
import ch.entwine.weblounge.common.security.PermissionSet;
import ch.entwine.weblounge.common.security.SecurityListener;
import ch.entwine.weblounge.common.security.User;
import ch.entwine.weblounge.common.site.Site;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A <code>Resource</code> encapsulates all data that is attached with a
* resource URL.
*/
public abstract class ResourceImpl<T extends ResourceContent> extends LocalizableObject implements Resource<T> {
/** The logging facility */
private static final Logger logger = LoggerFactory.getLogger(ResourceImpl.class);
/** The uri */
protected ResourceURI uri = null;
/** Resource type */
protected String type = null;
/** Keywords */
protected List<String> subjects = null;
/** Series */
protected List<String> series = null;
/** True if this page should show up on the sitemap */
protected boolean isPromoted = false;
/** True if the page contents should be indexed */
protected boolean isIndexed = true;
/** Current page editor (and owner) */
protected User lockOwner = null;
/** Creation context */
protected CreationContext creationCtx = null;
/** Modification context */
protected ModificationContext modificationCtx = null;
/** The publishing context */
protected PublishingContext publishingCtx = null;
/** The security context */
protected SecurityContextImpl securityCtx = null;
/** The title */
protected LocalizableContent<String> title = null;
/** The description */
protected LocalizableContent<String> description = null;
/** The coverage */
protected LocalizableContent<String> coverage = null;
/** The rights declaration */
protected LocalizableContent<String> rights = null;
/** The resource content */
protected Map<Language, T> content = null;
/**
* Creates a new page for the given page uri.
*
* @param uri
* the page uri
*/
public ResourceImpl(ResourceURI uri) {
super(null);
setDefaultLanguage(uri.getSite().getDefaultLanguage());
this.uri = uri;
this.content = new HashMap<Language, T>();
this.creationCtx = new CreationContext();
this.modificationCtx = new ModificationContext();
this.publishingCtx = new PublishingContext();
this.securityCtx = new PageSecurityContext();
this.subjects = new ArrayList<String>();
this.series = new ArrayList<String>();
this.title = new LocalizableContent<String>(this);
this.description = new LocalizableContent<String>(this);
this.coverage = new LocalizableContent<String>(this);
this.rights = new LocalizableContent<String>(this);
this.creationCtx.setCreated(uri.getSite().getAdministrator(), new Date());
this.securityCtx.setOwner(uri.getSite().getAdministrator());
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setPromoted(boolean)
*/
public void setPromoted(boolean anchor) {
this.isPromoted = anchor;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setIndexed(boolean)
*/
public void setIndexed(boolean index) {
this.isIndexed = index;
}
/**
* Returns the resource type.
*
* @return the resource type
*/
public String getType() {
return type;
}
/**
* Sets the page type: news, feature, ...
*
* @param type
* the resource type
*/
public void setType(String type) {
this.type = type;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getIdentifier()
*/
public String getIdentifier() {
return uri.getIdentifier();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setIdentifier(java.lang.String)
*/
public void setIdentifier(String identifier) {
uri.setIdentifier(identifier);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getPath()
*/
public String getPath() {
return uri.getPath();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setPath(java.lang.String)
*/
public void setPath(String path) {
uri.setPath(path);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getVersion()
*/
public long getVersion() {
return uri.getVersion();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setVersion(long)
*/
public void setVersion(long version) {
uri.setVersion(version);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#addSubject(java.lang.String)
*/
public void addSubject(String subject) {
subjects.add(subject);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#addSeries(java.lang.String)
*/
public void addSeries(String series) {
this.series.add(series);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#removeSubject(java.lang.String)
*/
public void removeSubject(String subject) {
subjects.remove(subject);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#removeSeries(java.lang.String)
*/
public void removeSeries(String series) {
this.series.remove(series);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#hasSubject(java.lang.String)
*/
public boolean hasSubject(String subject) {
return subjects.contains(subject);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#hasSeries(java.lang.String)
*/
public boolean hasSeries(String series) {
return this.series.contains(series);
}
/**
* Returns the page uri.
*
* @return the page uri
*/
public ResourceURI getURI() {
return uri;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setPublished(ch.entwine.weblounge.common.security.User,
* java.util.Date, java.util.Date)
*/
public void setPublished(User publisher, Date from, Date to) {
publishingCtx.setPublished(publisher, from, to);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Publishable#getPublisher()
*/
public User getPublisher() {
return publishingCtx.getPublisher();
}
/**
* Returns the publishing start date.
*
* @return the start date
*/
public Date getPublishFrom() {
return publishingCtx.getPublishFrom();
}
/**
* Returns the publishing end date.
*
* @return the end date
*/
public Date getPublishTo() {
return publishingCtx.getPublishTo();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#isPromoted()
*/
public boolean isPromoted() {
return isPromoted;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#isIndexed()
*/
public boolean isIndexed() {
return isIndexed;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setCoverage(java.lang.String,
* ch.entwine.weblounge.common.language.Language)
*/
public void setCoverage(String coverage, Language language) {
this.coverage.put(coverage, language);
enableLanguage(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getCoverage()
*/
public String getCoverage() {
return coverage.get();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getCoverage(ch.entwine.weblounge.common.language.Language)
*/
public String getCoverage(Language language) {
return coverage.get(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getCoverage(ch.entwine.weblounge.common.language.Language,
* boolean)
*/
public String getCoverage(Language language, boolean force) {
return coverage.get(language, force);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setDescription(java.lang.String,
* ch.entwine.weblounge.common.language.Language)
*/
public void setDescription(String description, Language language) {
this.description.put(description, language);
enableLanguage(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getDescription()
*/
public String getDescription() {
return description.get();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getDescription(ch.entwine.weblounge.common.language.Language)
*/
public String getDescription(Language language) {
return description.get(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getDescription(ch.entwine.weblounge.common.language.Language,
* boolean)
*/
public String getDescription(Language language, boolean force) {
return description.get(language, force);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#setRights(java.lang.String,
* ch.entwine.weblounge.common.language.Language)
*/
public void setRights(String rights, Language language) {
this.rights.put(rights, language);
enableLanguage(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getRights()
*/
public String getRights() {
return rights.get();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getRights(ch.entwine.weblounge.common.language.Language)
*/
public String getRights(Language language) {
return rights.get(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getRights(ch.entwine.weblounge.common.language.Language,
* boolean)
*/
public String getRights(Language language, boolean force) {
return rights.get(language, force);
}
/**
* Sets the page title in the specified language.
*
* @param title
* the page title
* @param language
* the language
*/
public void setTitle(String title, Language language) {
this.title.put(title, language);
enableLanguage(language);
}
/**
* Returns the page title in the active language.
*
* @return the page title
*/
public String getTitle() {
return title.get();
}
/**
* Returns the page title in the specified language or <code>null</code> if
* this language version is not available.
*
* @param language
* the language identifier
* @return the page title
*/
public String getTitle(Language language) {
return title.get(language);
}
/**
* Returns the page title in the required language. If no title can be found
* in that language, then it will be looked up in the default language (unless
* <code>force</code> is set to <code>true</code>). If that doesn't produce a
* result as well, <code>null</code> is returned.
*
* @param language
* the title language
* @param force
* <code>true</code> to force the language
* @return the content
*/
public String getTitle(Language language, boolean force) {
return title.get(language, force);
}
/**
* Returns the keywords that are defined for this page header.
*
* @return the keywords
*/
public String[] getSubjects() {
String[] kw = new String[subjects.size()];
return subjects.toArray(kw);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getSeries()
*/
public String[] getSeries() {
return series.toArray(new String[series.size()]);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Securable#setOwner(ch.entwine.weblounge.common.security.User)
*/
public void setOwner(User owner) {
if (owner == null) {
logger.warn("Someone tried to set the owner of '{}' to null", this);
return;
}
securityCtx.setOwner(owner);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Securable#allow(ch.entwine.weblounge.common.security.Permission,
* ch.entwine.weblounge.common.security.Authority)
*/
public void allow(Permission permission, Authority authority) {
securityCtx.allow(permission, authority);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Securable#deny(ch.entwine.weblounge.common.security.Permission,
* ch.entwine.weblounge.common.security.Authority)
*/
public void deny(Permission permission, Authority authority) {
securityCtx.deny(permission, authority);
}
/**
* Returns <code>true</code> if the user <code>u</code> is allowed to do
* actions that require permission <code>p</code> on this pagelet.
*
* @param p
* the required permission
* @param a
* the authorization used to access to this pagelet
* @return <code>true</code> if the user has the required permission
*/
public boolean check(Permission p, Authority a) {
return securityCtx.check(p, a);
}
/**
* Returns <code>true</code> if the user <code>u</code> is allowed to act on
* the secured object in a way that satisfies the given {@link PermissionSet}
* <code>p</code>.
*
* @param p
* the required set of permissions
* @param a
* the authorization used to access to the secured object
* @return <code>true</code> if the user owns the required permissions
*/
public boolean check(PermissionSet p, Authority a) {
return securityCtx.check(p, a);
}
/**
* Checks whether at least one of the given authorities pass with respect to
* the given permission.
*
* @param permission
* the permission to obtain
* @param authorities
* the objects claiming the permission
* @return <code>true</code> if all authorities pass
*/
public boolean checkOne(Permission permission, Authority[] authorities) {
return securityCtx.checkOne(permission, authorities);
}
/**
* Checks whether all of the given authorities pass with respect to the given
* permission.
*
* @param permission
* the permission to obtain
* @param authorities
* the object claiming the permission
* @return <code>true</code> if all authorities pass
*/
public boolean checkAll(Permission permission, Authority[] authorities) {
return securityCtx.checkAll(permission, authorities);
}
/**
* Returns the pagelets permissions, which are
* <ul>
* <li>READ</li>
* <li>WRITE</li>
* <li>TRANSLATE</li>
* <li>MANAGE</li>
* </ul>
*
* @return the permissions that can be set on the pagelet
* @see ch.entwine.weblounge.api.security.Secured#permissions()
*/
public Permission[] permissions() {
return permissions;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.security.Securable#getOwner()
*/
public User getOwner() {
if (securityCtx == null)
return null;
return securityCtx.getOwner();
}
/**
* Adds the security listener to the pagelets security context.
*
* @param listener
* the security listener
* @see ch.entwine.weblounge.api.security.Secured#addSecurityListener(ch.entwine.weblounge.api.security.SecurityListener)
*/
public void addSecurityListener(SecurityListener listener) {
securityCtx.addSecurityListener(listener);
}
/**
* Removes the security listener from the pagelets security context.
*
* @param listener
* the security listener
* @see ch.entwine.weblounge.api.security.Secured#removeSecurityListener(ch.entwine.weblounge.api.security.SecurityListener)
*/
public void removeSecurityListener(SecurityListener listener) {
securityCtx.removeSecurityListener(listener);
}
/**
* Returns the string representation of this page header.
*
* @return the string representation
*/
public String toString() {
return uri.toString();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Publishable#isPublished()
*/
public boolean isPublished() {
return publishingCtx.isPublished();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Publishable#isPublished(java.util.Date)
*/
public boolean isPublished(Date date) {
return publishingCtx.isPublished(date);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Creatable#setCreated(ch.entwine.weblounge.common.security.User,
* java.util.Date)
*/
public void setCreated(User creator, Date creationDate) {
if (creator == null) {
logger.warn("Someone tried to set the creator of '{}' to null", this);
return;
} else if (creationDate == null) {
logger.warn("Someone tried to set the creation date of '{}' to null", this);
return;
}
creationCtx.setCreated(creator, creationDate);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Creatable#setCreationDate(java.util.Date)
*/
public void setCreationDate(Date date) {
if (date == null) {
logger.warn("Someone tried to set creation date of '{}' to null", this);
return;
}
creationCtx.setCreationDate(date);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getCreationDate()
*/
public Date getCreationDate() {
return creationCtx.getCreationDate();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Creatable#setCreator(ch.entwine.weblounge.common.security.User)
*/
public void setCreator(User user) {
if (user == null) {
logger.warn("Someone tried to set creator of '{}' to null", this);
return;
}
creationCtx.setCreator(user);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getCreator()
*/
public User getCreator() {
return creationCtx.getCreator();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Creatable#isCreatedAfter(java.util.Date)
*/
public boolean isCreatedAfter(Date date) {
return creationCtx.isCreatedAfter(date);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getModificationDate()
*/
public Date getModificationDate() {
return modificationCtx.getModificationDate();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getLastModified()
*/
@Override
public Date getLastModified() {
Date date = getModificationDate();
if (date != null)
return date;
date = getPublishFrom();
if (date != null)
return date;
return getCreationDate();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getModifier()
*/
public User getModifier() {
return modificationCtx.getModifier();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#getLastModifier()
*/
@Override
public User getLastModifier() {
User user = getModifier();
if (user != null)
return user;
user = getPublisher();
if (user != null)
return user;
return getCreator();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Modifiable#setModified(ch.entwine.weblounge.common.security.User,
* java.util.Date)
*/
public void setModified(User user, Date date) {
modificationCtx.setModified(user, date);
}
/**
* Returns the user holding the editing lock for this page.
*
* @return the user holding the editing lock for this page
*/
public User getLockOwner() {
return lockOwner;
}
/**
* Locks this page for editing.
*
* @param user
* the locking user
*/
public void lock(User user) throws IllegalStateException {
if (lockOwner != null && !lockOwner.equals(user) && !SecurityUtils.userHasRole(user, SystemRole.SITEADMIN))
throw new IllegalStateException("The page is already locked by " + lockOwner);
lockOwner = user;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#unlock()
*/
public User unlock() {
User previousLockOwner = lockOwner;
lockOwner = null;
return previousLockOwner;
}
/**
* Returns <code>true</code> if the page is locked.
*
* @return <code>true</code> if this page is locked
*/
public boolean isLocked() {
return lockOwner != null;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#addContent(ch.entwine.weblounge.common.content.ResourceContent)
*/
public void addContent(T content) {
if (content == null)
throw new IllegalArgumentException("Resource content cannot be null");
this.content.put(content.getLanguage(), content);
this.enableLanguage(content.getLanguage());
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getContent(ch.entwine.weblounge.common.language.Language)
*/
public T getContent(Language language) {
return content.get(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#getOriginalContent()
*/
public T getOriginalContent() {
T original = null;
for (T c : content.values()) {
if (original == null || original.isCreatedAfter(c.getCreationDate()))
original = c;
}
return original;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#removeContent(ch.entwine.weblounge.common.language.Language)
*/
public T removeContent(Language language) {
if (language == null)
throw new IllegalArgumentException("Content language must not be null");
boolean useOriginalLanguage = Original.equals(behavior);
disableLanguage(language);
// Fix handling of potential loss of original language. If the original
// language has been deleted, the underlying implementation will switch the
// resource to a random default language.
if (useOriginalLanguage && !Original.equals(behavior)) {
Site site = uri.getSite();
if (supportsLanguage(site.getDefaultLanguage())) {
logger.trace("Switching default language of localizable object {} to site default language {}", this, defaultLanguage);
setOriginalLanguage(site.getDefaultLanguage());
}
}
return content.remove(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#contents()
*/
public Set<T> contents() {
Set<T> contents = new HashSet<T>();
contents.addAll(content.values());
return contents;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#contentLanguages()
*/
public Set<Language> contentLanguages() {
return content.keySet();
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.content.Resource#supportsContentLanguage(ch.entwine.weblounge.common.language.Language)
*/
public boolean supportsContentLanguage(Language language) {
return content.containsKey(language);
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.impl.language.LocalizableObject#switchTo(ch.entwine.weblounge.common.language.Language)
*/
@Override
public Language switchTo(Language language) {
Language l = super.switchTo(language);
title.switchTo(language);
description.switchTo(language);
coverage.switchTo(language);
rights.switchTo(language);
return l;
}
/**
* {@inheritDoc}
*
* @see ch.entwine.weblounge.common.language.Localizable#compareTo(ch.entwine.weblounge.common.language.Localizable,
* ch.entwine.weblounge.common.language.Language)
*/
public int compareTo(Localizable o, Language l) {
if (o instanceof Resource)
return title.get(l).compareTo(((Resource<?>) o).getTitle(l));
return title.compareTo(o, l);
}
/**
* @see java.lang.Object#hashCode()
*/
public int hashCode() {
return uri.hashCode();
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
public boolean equals(Object obj) {
if (obj != null && obj instanceof Resource) {
return uri.equals(((Resource<?>) obj).getURI());
}
return false;
}
/**
* Returns a XML representation of this page header.
*
* @return an XML representation of this page header
*/
public String toXml() {
StringBuffer b = new StringBuffer();
String rootTag = toXmlRootTag();
b.append("<").append(rootTag);
b.append(" id=\"");
b.append(uri.getIdentifier());
if (StringUtils.isNotBlank(uri.getPath())) {
b.append("\" path=\"");
b.append(uri.getPath());
}
b.append("\" version=\"");
b.append(ResourceUtils.getVersionString(uri.getVersion()));
b.append("\">");
// Add header
b.append("<head>");
// Add custom header data
toXmlHead(b);
b.append("<promote>");
b.append(Boolean.toString(isPromoted));
b.append("</promote>");
b.append("<index>");
b.append(Boolean.toString(isIndexed));
b.append("</index>");
// Metadata
b.append("<metadata>");
// Title
for (Language language : title.languages()) {
b.append("<title language=\"");
b.append(language.getIdentifier());
b.append("\"><![CDATA[");
b.append(title.get(language));
b.append("]]></title>");
}
// Description
for (Language language : description.languages()) {
b.append("<description language=\"");
b.append(language.getIdentifier());
b.append("\"><![CDATA[");
b.append(description.get(language));
b.append("]]></description>");
}
// Subject
Collections.sort(subjects);
for (String s : subjects) {
b.append("<subject><![CDATA[");
b.append(s);
b.append("]]></subject>");
}
// Series
Collections.sort(series);
for (String s : series) {
b.append("<series><![CDATA[");
b.append(s);
b.append("]]></series>");
}
// Type
if (type != null) {
b.append("<type><![CDATA[");
b.append(type);
b.append("]]></type>");
}
// Coverage
for (Language language : coverage.languages()) {
b.append("<coverage language=\"");
b.append(language.getIdentifier());
b.append("\"><![CDATA[");
b.append(coverage.get(language));
b.append("]]></coverage>");
}
// Rights
for (Language language : rights.languages()) {
b.append("<rights language=\"");
b.append(language.getIdentifier());
b.append("\"><![CDATA[");
b.append(rights.get(language));
b.append("]]></rights>");
}
b.append("</metadata>");
// Created
b.append(creationCtx.toXml());
// Modified
b.append(modificationCtx.toXml());
// Published
b.append(publishingCtx.toXml());
// Security
b.append(securityCtx.toXml());
// Lock (wrap in UserImpl to prevent weblounge user details)
if (lockOwner != null) {
b.append("<locked>");
b.append(lockOwner.toXml());
b.append("</locked>");
}
b.append("</head>");
// Add custom body
StringBuffer body = new StringBuffer();
body = toXmlBody(body);
if (body.length() > 0) {
b.append("<body>");
b.append(body);
b.append("</body>");
} else {
b.append("<body/>");
}
b.append("</").append(rootTag).append(">");
return b.toString();
}
/**
* Returns the name of the root tag as used in {@link #toXml()}.
*
* @return the root tag
*/
protected String toXmlRootTag() {
return "resource";
}
/**
* Optionally adds additional pieces to the <tt><head></tt> section of
* the resource's xml serialization.
* <p>
* Subclasses that need to store additional content in that section should
* overwrite this method and add that information to the buffer.
*
* @param buffer
* the string buffer
*/
protected void toXmlHead(StringBuffer buffer) {
return;
}
/**
* Optionally adds additional pieces to the <tt><body></tt> section of
* the resource's xml serialization.
* <p>
* Subclasses that need to store additional content in that section should
* overwrite this method and add that information to the buffer. However, make
* sure to call <code>super.toXmlBody()</code> since this implementation adds
* the resource contents to the body.
*
* @param buffer
* the string buffer
*/
protected StringBuffer toXmlBody(StringBuffer buffer) {
for (ResourceContent content : this.content.values()) {
buffer.append(content.toXml());
}
return buffer;
}
}