/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.webgate.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import de.innovationgate.utils.UserHashMap;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGContent.SessionData;
import de.innovationgate.webgate.api.WGDocument.UniqueNameTester;
import de.innovationgate.webgate.api.WGSessionContext.DocumentContext;
import de.innovationgate.webgate.api.fake.WGFakeArea;
import de.innovationgate.webgate.api.fake.WGFakeContentType;
import de.innovationgate.webgate.api.locking.Lockable;
import de.innovationgate.webgate.api.utils.MasterSessionTask;
/**
* A struct entry that is used to build the site hierarchy, determine the
* content types at the hierarchy nodes and the editing rights in the hierarchy.
*/
public class WGStructEntry extends WGDocument implements Comparable, PageHierarchyNode {
public class SessionData {
private List backendReaders;
public List getBackendReaders() {
return backendReaders;
}
public void setBackendReaders(List backendReaders) {
this.backendReaders = backendReaders;
}
private List backendPageEditors;
public List getBackendPageEditors() {
return backendPageEditors;
}
public void setBackendPageEditors(List backendContentEditors) {
this.backendPageEditors = backendContentEditors;
}
private List backendChildEditors;
public List getBackendChildEditors() {
return backendChildEditors;
}
public void setBackendChildEditors(List backendChildEditors) {
this.backendChildEditors = backendChildEditors;
}
}
/**
* The set of contents that belongs to one struct entry, divided up into different maps
*
*/
public class ContentSet {
private Map<String,WGContent> allContent = new HashMap<String,WGContent>();
private Map<String,WGContent> releasedContent = new HashMap<String,WGContent>();
private Map<String,WGContent> archivedContent = new HashMap<String,WGContent>();
public ContentSet() {
}
public ContentSet(List<WGContent> contents) throws WGAPIException {
Iterator<WGContent> contentListIt = contents.iterator();
WGContent newContent;
while (contentListIt.hasNext()) {
newContent = (WGContent) contentListIt.next();
String cachingKey = newContent.getLanguage().getName() + WGContentKey.TOKEN_DIVIDER + newContent.getVersion();
if (newContent.getStatus().equals(WGContent.STATUS_ARCHIVE)) {
getArchivedContent().put(cachingKey, newContent);
}
else {
getAllContent().put(cachingKey, newContent);
if (newContent.getStatus().equals(WGContent.STATUS_RELEASE)) {
WGContent oldContent = (WGContent) getReleasedContent().put(newContent.getLanguage().getName(), newContent);
if (oldContent != null && oldContent.getVersion() != newContent.getVersion()) {
WGFactory.getLogger().warn("There are multiple released content versions for structentry " + newContent.getStructKey() + " (db:" + newContent.getDatabase().getDbReference() + ") on language " + newContent.getLanguage().getName() + ": Versions " + newContent.getVersion() + " and " + oldContent.getVersion());
}
}
}
}
}
/**
* Returns all ACTIVE content for the struct entry (excluding archived ones), mapped by a string "language.version"
*/
public Map<String,WGContent> getAllContent() {
return allContent;
}
/**
* Returns the archived content for the struct entry, mapped by a string "language.version"
*/
public Map<String,WGContent> getArchivedContent() {
return archivedContent;
}
/**
* Returns the released content for the struct entry, mapped by the language code
*/
public Map<String,WGContent> getReleasedContent() {
return releasedContent;
}
}
class TestForContent implements Runnable {
private String _language;
private String _status;
private boolean _contentExists;
public TestForContent(String language, String status) {
_language = language;
_status = status;
}
public TestForContent() {
_language = null;
_status = null;
}
public boolean doesContentExist() {
Thread thread = new Thread(this);
thread.start();
try {
thread.join();
}
catch (InterruptedException e) {
}
return _contentExists;
}
public void run() {
try {
getDatabase().openSession();
WGContentList allContent = getAllContent(_status == null || _status.equals(WGContent.STATUS_ARCHIVE));
_contentExists = false;
if( allContent != null && !allContent.isEmpty() ){
if( _language==null && _status==null){
_contentExists = true;
}
else{
Iterator itContent = allContent.iterator();
while( itContent.hasNext() ){
WGContent content = (WGContent)itContent.next();
if( _status == null ){
if( content.getLanguage().getName().equalsIgnoreCase(_language) ){
_contentExists = true;
break;
}
}
else{
if( _language == null ){
if( content.getStatus().equals(_status) ){
_contentExists = true;
break;
}
}
else{
if( content.getStatus().equals(_status) && content.getLanguage().getName().equalsIgnoreCase(_language) ){
_contentExists = true;
break;
}
}
}
}
}
}
} catch (Throwable t) {
WGFactory.getLogger().error(
"Error testing for content in master session",
t);
} finally {
WGFactory.getInstance().closeSessions();
}
}
}
public static final String META_KEY = "KEY";
public static final MetaInfo METAINFO_KEY = new MetaInfo(META_KEY, Object.class, null);
public static final String META_TITLE = "TITLE";
public static final String META_UNIQUENAME = "UNIQUENAME";
public static final MetaInfo METAINFO_TITLE = new MetaInfo(META_TITLE, String.class, "");
static {
METAINFO_TITLE.setInputConverter(TITLE_CONVERTER);
METAINFO_TITLE.setOutputConverter(TITLE_CONVERTER);
}
public static final String META_AREA = "AREA";
public static final MetaInfo METAINFO_AREA = new MetaInfo(META_AREA, String.class, null);
public static final String META_POSITION = "POSITION";
public static final MetaInfo METAINFO_POSITION = new MetaInfo(META_POSITION, Number.class, new Integer(0));
public static final String META_CONTENTTYPE = "CONTENTTYPE";
public static final MetaInfo METAINFO_CONTENTTYPE = new MetaInfo(META_CONTENTTYPE, Object.class, null);
public static final String META_READERS = "READERS";
public static final MetaInfo METAINFO_READERS = new MetaInfo(META_READERS, String.class, Collections.EMPTY_LIST);
static {
METAINFO_READERS.setMultiple(true);
METAINFO_READERS.setLuceneIndexType(MetaInfo.LUCENE_INDEXTYPE_FULLTEXT);
METAINFO_READERS.setInputConverter(new MetaConverter() {
public Object convert(WGDocument doc, MetaInfo metaInfo, Object value) throws WGAPIException {
List readers = (List) value;
// Test if the current user is contained in the list. If not, he is added to avoid self disclosure
if (!WGDatabase.anyoneAllowed(readers) && !doc.getDatabase().isMemberOfUserList(readers)) {
readers.add(doc.getDatabase().getSessionContext().getUser());
}
return readers;
}
});
};
public static final String META_PAGE_EDITORS = "PAGEEDITORS";
public static final MetaInfo METAINFO_PAGE_EDITORS = new MetaInfo(META_PAGE_EDITORS, String.class, Collections.EMPTY_LIST);
static { METAINFO_PAGE_EDITORS.setMultiple(true); };
public static final String META_CHILD_EDITORS = "CHILDEDITORS";
public static final MetaInfo METAINFO_CHILD_EDITORS = new MetaInfo(META_CHILD_EDITORS, String.class, Collections.EMPTY_LIST);
static { METAINFO_CHILD_EDITORS.setMultiple(true); };
public static final String META_WORKFLOW_NAME = "OVERRIDE_WORKFLOW";
public static final MetaInfo METAINFO_WORKFLOW_NAME = new MetaInfo(META_WORKFLOW_NAME, String.class, null);
public static final MetaInfo METAINFO_UNIQUENAME = new MetaInfo(META_UNIQUENAME, String.class, null);
static {
METAINFO_UNIQUENAME.setMinCsVersion(WGDatabase.CSVERSION_WGA5);
METAINFO_UNIQUENAME.setLowerCase(true);
METAINFO_UNIQUENAME.addSynonym("PAGENAME");
METAINFO_UNIQUENAME.addSynonym("PAGEUNIQUENAME");
METAINFO_UNIQUENAME.addSynonym("PAGEDOCNAME");
}
public static final String META_PUBLISHED = "PUBLISHED";
public static final MetaInfo METAINFO_PUBLISHED = new MetaInfo(META_PUBLISHED, Map.class, Collections.emptyMap());
static {
METAINFO_PUBLISHED.setMinCsVersion(WGDatabase.CSVERSION_WGA5);
}
/**
* No content has yet been fetched for this struct entry.
*/
public static final int FETCHSTATE_NONE = 0;
/**
* Only active contents (non-archived) have been fetched for this struct
* entry.
*/
public static final int FETCHSTATE_ACTIVE_ONLY = 1;
/**
* All contents - including archived - have been fetched for this struct
* entry.
*/
public static final int FETCHSTATE_ARCHIVED = 2;
private Object retrievalKey = null;
private WGDocumentKey area = null;
protected WGDocumentKey parentEntry = null;
private WGDocumentListCache childEntries = null;
/*
private Map fetchedContentForUsers = Collections
.synchronizedMap(new HashMap());
*/
//B00003852
private UserHashMap contentCache = null;
private Integer contentCount = null;
private static final String FETCH_STATE_KEY = "FETCH_STATE";
private static final Object FETCH_CONTENT_CACHE = "CONTENT_CACHE";
private static final Object FETCH_RELEASED_CONTENT_CACHE = "RELEASED_CONTENT_CACHE_";
/**
* Determines if this entry is a root entry
* @throws WGAPIException
*/
public boolean isRoot() throws WGAPIException {
if (this.getParentEntry() == null) {
return true;
} else {
return false;
}
}
/**
* Sets allowed editors for child entries and their content (if they inherit
* their editing rights)
*
* @param editors
* @throws WGAPIException
*/
public boolean setChildEditors(List editors) throws WGAPIException {
return setMetaData(META_CHILD_EDITORS, editors);
}
/**
* Sets names that are allowed to edit this struct and it's contents.
*
* @param editors
* @throws WGAPIException
*/
public void setPageEditors(List editors) throws WGAPIException {
setMetaData(META_PAGE_EDITORS, editors);
}
/**
* Old version of {@link #setPageEditors(List)}. Avoid in new developments.
* @param editors
* @throws WGAPIException
* @deprecated
*/
public boolean setStructEditors(List editors) throws WGAPIException {
setPageEditors(editors);
return true;
}
/**
* Determines if this entry has child entries
* @throws WGAPIException
*/
public boolean hasChildren() throws WGAPIException {
if (this.getChildEntries().size() > 0) {
return true;
} else {
return false;
}
}
/**
* Returns a list of names that are allowed to edit this struct document and
* it's contents.
* @throws WGAPIException
*/
public List getPageEditors() throws WGAPIException {
return (List) this.getMetaData(WGStructEntry.META_PAGE_EDITORS);
}
/**
* Old version for {@link #getPageEditors()}. Avoid in new developments.
* @throws WGAPIException
* @deprecated
*/
public List getStructEditors() throws WGAPIException {
return getPageEditors();
}
/**
* Returns the editors allowed to edit child entries and content of this
* struct entry
* @throws WGAPIException
*/
public List getChildEditors() throws WGAPIException {
return (List) this.getMetaData(WGStructEntry.META_CHILD_EDITORS);
}
/**
* Returns the parent entry, null if this is a root entry.
* @throws WGAPIException
*/
public WGStructEntry getParentEntry() throws WGAPIException {
// Double checked cache (un-synchronized and synchronized)
WGDocument theEntry = null;
if (this.parentEntry != null && isCachingEnabled()) {
theEntry = this.parentEntry.getDocument(getDatabase());
}
if (!isCachingEnabled() || theEntry == null) {
WGOperationKey op = db.obtainOperationKey(
WGOperationKey.OP_STRUCT_PARENT, String
.valueOf(getStructKey()));
synchronized (op) {
try {
op.setUsed(true);
if (this.parentEntry != null && isCachingEnabled()) {
theEntry = this.parentEntry.getDocument(getDatabase());
}
if (!isCachingEnabled() || theEntry == null) {
theEntry = this.db.getParentEntry(this);
if (theEntry == null) {
theEntry = this;
}
if (isCachingEnabled() && getDatabase().getSessionContext().isCacheWritingEnabled()) {
this.parentEntry = theEntry.getDocumentKeyObj();
}
}
}
finally {
op.setUsed(false);
}
}
}
if (!WGUtils.nullSafeEquals(theEntry, this)) {
return (WGStructEntry) theEntry;
} else {
return null;
}
}
/**
* Returns the area of this struct entry
* @throws WGAPIException
*/
public WGArea getArea() throws WGAPIException {
WGArea theArea = null;
if (this.area != null) {
theArea = (WGArea) this.area.getDocument(getDatabase());
}
if (!isCachingEnabled() || theArea == null) {
if (this.isRoot()) {
theArea = this.db.getArea((String) this.getMetaData(META_AREA));
}
else {
theArea = this.getRootEntry().getArea();
}
if (isCachingEnabled() && getDatabase().getSessionContext().isCacheWritingEnabled()) {
this.area = theArea.getDocumentKeyObj();
}
}
if (theArea != null) {
return theArea;
} else {
theArea = (WGArea) db
.getOrCreateDesignDocumentObject(new WGFakeArea(db, String
.valueOf(this.getMetaData(META_AREA))));
theArea.setDummy(true);
return theArea;
}
}
/**
* Constructor. Should not be used outside the WGAPI
*
* @param db
* @param doc
* @throws WGAPIException
*/
public WGStructEntry(WGDatabase db, WGDocumentCore doc) throws WGAPIException {
super(db, doc);
gatherRetrievalKeys();
// B00003852
this.contentCache = db.getUserHashMapGroup().newUserHashMap(getDocumentKey() + "///fetchState");
if (db.getUserCacheLatency() != 0) {
this.contentCache.setMapLatency(db.getUserCacheLatency() * 1000 * 60);
}
}
private void gatherRetrievalKeys() throws WGAPIException {
this.retrievalKey = this.getMetaData(META_KEY);
}
/**
* Gets the sibling entries (including the current struct entry)
* @throws WGAPIException
*/
public WGStructEntryList getSiblingEntries() throws WGAPIException {
if (this.isRoot()) {
return this.getArea().getRootEntries();
} else {
WGStructEntry entry = this.getParentEntry();
if (entry != null) {
return entry.getChildEntries();
} else {
WGFactory.getLogger().error(
"Could not get parent entry for struct '"
+ getStructKey() + "'");
return null;
}
}
}
/**
* Returns the root entry of this entry
* @throws WGAPIException
*/
public WGStructEntry getRootEntry() throws WGAPIException {
WGStructEntry entry = this;
while (!entry.isRoot()) {
entry = entry.getParentEntry();
}
return entry;
}
/**
* Returns the position number.
* @throws WGAPIException
*/
public Number getPosition() throws WGAPIException {
return (Number) this.getMetaData(META_POSITION);
}
/**
* Returns a path of position numbers for all ancestors of the current struct entry
* @return A list of position numbers for the struct entry and all it's ancestors, beginning with the current struct entry and proceeding up the hierarchy
* @throws WGAPIException
*/
public List getPositionPath() throws WGAPIException {
List path = new ArrayList();
WGStructEntry entry = this;
while (entry != null) {
path.add(entry.getPosition());
entry = entry.getParentEntry();
}
Collections.reverse(path);
return path;
}
/**
* Sets the position number.
*
* @param pos
* @throws WGAPIException
*/
public boolean setPosition(int pos) throws WGAPIException {
return setMetaData(META_POSITION, new Integer(pos));
}
/**
* Returns the child entries of this struct entry
* @throws WGAPIException
*/
public WGStructEntryList getChildEntries() throws WGAPIException {
List theEntries = getDatabase().fetchDocumentListCache(this.childEntries, WGDocument.TYPE_STRUCTENTRY);
if (!isCachingEnabled() || theEntries == null) {
WGOperationKey op = db.obtainOperationKey(
WGOperationKey.OP_STRUCT_CHILDREN, String
.valueOf(getStructKey()));
synchronized (op) {
try {
op.setUsed(true);
theEntries = getDatabase().fetchDocumentListCache(this.childEntries, WGDocument.TYPE_STRUCTENTRY);
if (!isCachingEnabled() || theEntries == null) {
theEntries = this.db.getChildEntries(this);
if (isCachingEnabled() && getDatabase().getSessionContext().isCacheWritingEnabled()) {
this.childEntries = WGDocumentListCache.buildFromDocuments(theEntries);
}
}
}
finally {
op.setUsed(false);
}
}
}
return WGStructEntryList.create(theEntries);
}
/**
* Returns all content documents under this struct entry
*
* @param includeArchived
* Specify true to also include archived documents in result.
* @throws WGAPIException
*/
public WGContentList getAllContent(boolean includeArchived) throws WGAPIException {
ContentSet contentSet = getContentSet(includeArchived);
if (includeArchived) {
List allContent = new ArrayList();
allContent.addAll(contentSet.getAllContent().values());
allContent.addAll(contentSet.getArchivedContent().values());
return WGContentList.create(allContent);
} else {
return WGContentList.create(contentSet.getAllContent().values());
}
}
/**
* Returns all active (non-archived) content under this struct entry.
* @throws WGAPIException
*/
public WGContentList getAllContent() throws WGAPIException {
return getAllContent(false);
}
private boolean alreadyFetched(boolean includeArchived) {
int status = getContentFetchState();
if (status == FETCHSTATE_NONE) {
return false;
}
if (includeArchived && status == FETCHSTATE_ACTIVE_ONLY) {
return false;
}
return true;
}
/**
* Returns a specific content of the given language and content version
*
* @param language
* @param version
* @throws WGAPIException
*/
public WGContent getContent(String language, int version) throws WGAPIException {
ContentSet contentSet = getContentSet(true);
String cachingKey = language + WGContentKey.TOKEN_DIVIDER + version;
// Try non-archived
WGContent content = (WGContent) contentSet.getAllContent().get(cachingKey);
if (content != null) {
return content;
}
// Try archived
return (WGContent) contentSet.getArchivedContent().get(cachingKey);
}
/**
* Returns a content of the given language and status, if any exists.
* If multiple contents exist with these parameters a random content of
* that group is returned.
*
* @param language The language of the content
* @param status The workflow status of the content
* @return A content with that parameters. null if none exists.
* @throws WGAPIException
*/
public WGContent getContent(String language, String status) throws WGAPIException {
WGStructEntry.ContentSet contentSet = getContentSet(status.equals(WGContent.STATUS_ARCHIVE));
Iterator contents;
if (status.equals(WGContent.STATUS_ARCHIVE)) {
contents = contentSet.getArchivedContent().values().iterator();
}
else {
contents = contentSet.getAllContent().values().iterator();
}
WGContent content;
while (contents.hasNext()) {
content = (WGContent) contents.next();
if (content.getLanguage().getName().equals(language) && content.getStatus().equals(status)) {
return content;
}
}
return null;
}
/**
* Returns the title of the struct entry. We prohibit this on struct entries whose contents may not be read by the current user.
* @throws WGAPIException
*/
public String getTitle() throws WGAPIException {
if (!mayReadContent()) {
return null;
}
return this.getMetaData(META_TITLE).toString();
}
/**
* Sets the struct title.
*
* @param title
* @throws WGAPIException
*/
public boolean setTitle(String title) throws WGAPIException {
return this.setMetaData(META_TITLE, title);
}
/**
* Returns the workflow name of the struct entry.
* @throws WGAPIException
*/
public String getWorkflowName() throws WGAPIException {
String wfName = "";
WGStructEntry pEntry = this;
while( pEntry != null ){
wfName = (String)pEntry.getMetaData(META_WORKFLOW_NAME);
if( wfName != null && !wfName.equals("") ){
break;
}
pEntry = pEntry.getParentEntry();
}
if( wfName == null || wfName.equals("") ){
wfName = this.getContentType().getWorkflow();
}
return wfName;
}
/**
* Sets the workflow name.
* @param wfname The workflow name to set
* @throws WGAPIException
*/
public boolean setWorkflowName(String wfname) throws WGAPIException {
return this.setMetaData(META_WORKFLOW_NAME, wfname);
}
/**
* Returns the content type for this struct entry
* @throws WGAPIException
*/
public WGContentType getContentType() throws WGAPIException {
WGContentType theContentType = null;
String doctypeName = (String) this.getMetaData(META_CONTENTTYPE);
if (doctypeName != null) {
theContentType = (WGContentType) this.db.getContentType(doctypeName);
}
if (theContentType != null) {
return theContentType;
}
else {
return null;
}
}
/**
* Change the content type for this struct entry and all its contents.
* This is only possible while no contents are in a approval workflow.
* @param contentType The content type to set
* @throws WGAPIException
*/
public void changeContentType(WGContentType contentType) throws WGAPIException {
// master task to check for unreleased content
MasterSessionTask checkUnreleasedContent = new MasterSessionTask(getDatabase()) {
protected void exec(WGDatabase db) throws Throwable {
Iterator contents = getAllContent().iterator();
while (contents.hasNext()) {
WGContent content = (WGContent) contents.next();
if (!content.getStatus().equals(WGContent.STATUS_RELEASE)) {
throw new WGIllegalStateException("ContentType cannot be changed bc. there is unreleased content in draft or review state.");
}
}
}
};
// check for unreleased content
try {
checkUnreleasedContent.runWithExceptions();
} catch (WGAPIException e) {
throw e;
} catch (Throwable e) {
throw new WGAPIException(e.getMessage(), e);
}
// Check if we may modify this content type
performSaveCheck();
// check if contenttype can be used by current user at this position
// null is also allowed if user is at least designer
WGDocument parent = null;
if (isRoot()) {
parent = getArea();
} else {
parent = getParentEntry();
}
if (contentType != null && !contentType.mayCreateChildEntry(parent)) {
throw new WGAuthorisationException("User is not allowed to use this content type at this position");
}
if (getDatabase().hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES) && contentType == null && getDatabase().getSessionContext().isDesigner() == false) {
throw new WGAuthorisationException("You are not authorized to create a struct entry without content type");
}
if (contentType != null) {
setMetaData(WGStructEntry.META_CONTENTTYPE, (getDatabase().hasFeature(WGDatabase.FEATURE_USE_OBJECTS_AS_REFERENCES) ? contentType.getCore() : (Object) contentType.getName()));
}
// save changes
save();
// Trigger content saved events for all contents so they are reindexed
for (WGContent content : getAllContent(true)) {
WGContentEvent event = new WGContentEvent(WGContentEvent.TYPE_HASBEENSAVED, content.getDocumentKey(), content.getStructEntry().getContentType().getName(), content.getDatabase());
event.setContent(content);
getDatabase().fireContentEvent(event);
}
}
/**
* Returns the inner layout for this struct entry. This may be determined by
* it's content type or by internal struct fields.
*
* @param mediaKey
* The media key of the desired layout
* @return The TML module for the layout, if it exists for this media key,
* null otherwise
* @throws WGAPIException
*/
public WGTMLModule getInnerLayout(String mediaKey) throws WGAPIException {
WGContentType contentType = getContentType();
if (contentType != null) {
return contentType.getInnerLayout(mediaKey);
}
else {
return null;
}
}
/**
* Returns the outer layout for this struct entry. This may be determined by
* it's content type or by internal struct fields.
*
* @param mediaKey
* The media key of the desired layout
* @return The TML module for the layout, if it exists for this media key,
* null otherwise
* @throws WGAPIException
*/
public WGTMLModule getOuterLayout(String mediaKey) throws WGAPIException {
WGContentType contentType = getContentType();
if (contentType != null) {
return contentType.getOuterLayout(mediaKey);
}
else {
return null;
}
}
/**
* Compares two struct entries. Position is taken into account for
* higher/lower but does not result in equality when positions are equal.
* @throws WGAPIException
*/
public int compareTo(Object obj) throws ClassCastException {
// Only compares with other objects of this type
if (obj.getClass() != this.getClass()) {
throw new ClassCastException(obj.getClass().getName()
+ " can only be compared to itself");
}
try {
// Look if one is deleted - Then only the hashCode can compare them
WGStructEntry otherEntry = (WGStructEntry) obj;
if (this.isDeleted() || otherEntry.isDeleted()) {
return (this.hashCode() == this.hashCode() ? 0 : -1);
}
// Try to compare via position
Double thisPos = new Double(this.getPosition().doubleValue());
Double otherPos = new Double(otherEntry.getPosition().doubleValue());
int posCompare = thisPos.compareTo(otherPos);
if (posCompare != 0)
return posCompare;
// Try to compare via title
String thisTitle = WGUtils.getValueOrDefault(this.getTitle(), "");
String otherTitle = WGUtils.getValueOrDefault(otherEntry.getTitle(), "");
int titleCompare = getDatabase().getDefaultLanguageCollator().compare(thisTitle, otherTitle);
if (titleCompare != 0)
return titleCompare;
// If everything else fails, compare Struct key
return this.getStructKey().toString().compareTo(
otherEntry.getStructKey().toString());
} catch (WGAPIException e) {
throw new RuntimeException("Unable to compare bc. of WGAPIException", e);
}
}
/*
* (non-Javadoc)
*
* @see de.innovationgate.webgate.api.WGDocument#dropCache()
*/
public void dropCache() throws WGAPIException {
super.dropCache();
this.contentCount = null;
dropRelations();
}
protected void dropRelations() {
this.area = null;
this.parentEntry = null;
this.childEntries = null;
if (this.contentCache != null) {
this.contentCache.clear();
}
}
/**
* Returns the child level of this entry. 1 is root.
* @throws WGAPIException
*/
public Integer getLevel() throws WGAPIException {
WGStructEntry entry = this;
int level = 0;
while (entry != null) {
level++;
entry = entry.getParentEntry();
}
return new Integer(level);
}
/**
* Returns the struct key.
*/
public Object getStructKey() {
return this.retrievalKey;
}
/**
* Returns the released content of the given language
*
* @param strLanguage
* @throws WGAPIException
*/
public WGContent getReleasedContent(String strLanguage) throws WGAPIException {
// Test for read access
if (!mayReadContent()) {
return null;
}
if (strLanguage == null) {
strLanguage = getDatabase().getDefaultLanguage();
}
// If the content set is already fetched we might just take the content from it
if (getContentFetchState() >= FETCHSTATE_ACTIVE_ONLY) {
ContentSet contentSet = getContentSet(false);
return (WGContent) contentSet.getReleasedContent().get(strLanguage.toLowerCase());
}
// Else look in released docs cache. If not available there use db.getReleasedContent() to retrieve it
else {
try {
String cacheKey = FETCH_RELEASED_CONTENT_CACHE + strLanguage;
WGContent content = (WGContent) getDatabase().fetchDocumentFromCache(this.contentCache, cacheKey);
if (content == null) {
WGOperationKey op = getDatabase().obtainOperationKey(WGOperationKey.OP_STRUCT_CONTENTS, String.valueOf(getStructKey()));
synchronized (op) {
try {
op.setUsed(true);
content = (WGContent) getDatabase().fetchDocumentFromCache(this.contentCache, cacheKey);
if (content == null) {
content = getDatabase().getReleasedContent(this, strLanguage);
if (isCachingEnabled() && getDatabase().getSessionContext().isCacheWritingEnabled()) {
if (content != null) {
contentCache.put(cacheKey, content.getDocumentKeyObj());
}
else {
getDatabase().mapNonExistentDocIndicator(contentCache, cacheKey, WGDocument.TYPE_CONTENT);
}
}
}
}
finally {
op.setUsed(false);
}
}
}
return content;
}
catch (WGDocumentDoesNotExistException e) {
return null;
}
}
}
/**
* Tests if this entry has an released content for the given language.
* This method is capable of regarding contents that the current user is not allowed to see.
* @param strLanguage The language to test
* @return true, if there is a released content, false otherwise
*/
public boolean hasReleasedContent(String strLanguage) {
TestForContent test = new TestForContent(strLanguage, WGContent.STATUS_RELEASE);
return test.doesContentExist();
}
/**
* Tests if struct entry has content in given language code / status, even if the user doesn't have the authorisation to read it.
* If language code parameter is null, proves on any language code.
* If status parameter is null, proves on any status.
* @throws WGAPIException
*/
public boolean hasContent(String strLanguage, String strStatus) throws WGAPIException {
// If only pure existence of any content is tested
// we redirect to the optimized WGDatabase.hasContent() method
if (strLanguage == null && strStatus == null) {
return (getDatabase().hasContents(this));
}
TestForContent test = new TestForContent(strLanguage, strStatus);
return test.doesContentExist();
}
/**
* Returns the number of content documents in any status or language, that are located at this struct entry.
* This includes content documents that are invisible to the current user because of reader settings, so this
* method may be used to determine the existence of such documents while they cannot be retrieved directly.
* @throws WGAPIException
*/
public int getContentCount() throws WGAPIException {
if (contentCount == null) {
// Technique 1: Try native content count functionalty (in-session) from backend
try {
contentCount = new Integer(getDatabase().getCore().getContentCount(this));
return contentCount.intValue();
}
catch (WGNotSupportedException e) {
}
catch (WGAPIException e) {
WGFactory.getLogger().error("Exception retrieving content count from " + getDocumentKey(), e);
}
// Techique 2: Retrieve number of contents from master session
MasterSessionTask task = new MasterSessionTask(getDatabase()) {
protected void exec(WGDatabase db) throws Throwable {
setResult(new Integer(WGStructEntry.this.getAllContent(true).size()));
}
};
if (task.run() == true) {
contentCount = ((Integer) task.getResult());;
}
else {
throw new WGAPIException("Exception retrieving content count", task.getThrowable());
}
}
return contentCount.intValue();
}
/**
* Returns all released content documents under this struct entry, keyed by
* their language code.
* @throws WGAPIException
*/
public Map getAllReleasedContent() throws WGAPIException {
return new HashMap(getContentSet(false).getReleasedContent());
}
/**
* @throws WGAPIException
* @see WGDocument#retrieveCore()
*/
protected WGDocumentCore retrieveCore() throws WGAPIException {
return this.db.getCore().getStructEntryByKey(this.retrievalKey);
}
/**
* Returns the next sibling by siblings index.
* @throws WGAPIException
*/
public WGStructEntry getNextSibling() throws WGAPIException {
WGStructEntryList siblings = this.getSiblingEntries();
int idx = siblings.getIndexOfEntry(this);
return siblings.getByIndex(++idx);
}
/**
* Returns the previous sibling by siblings index
* @throws WGAPIException
*/
public WGStructEntry getPreviousSibling() throws WGAPIException {
WGStructEntryList siblings = this.getSiblingEntries();
int idx = siblings.getIndexOfEntry(this);
return siblings.getByIndex(--idx);
}
/**
* Tests if the current user may edit this struct entry and it's contents.
*
* @return null, if the user may edit. If not, the WGStructEntry or WGArea
* object ist returned that denies editing access
* @throws WGAPIException
*/
public WGDocument mayEditEntryAndContent() throws WGAPIException {
// returns null if user may edit Entry
// Otherwise returns WGStructEntry, which prohibits user from editing
// this Entry.
// Test for read access. Without read access no write access (not even for managers)
WGDocument prohibitingDoc = getReadProtectionCause();
if (prohibitingDoc != null) {
return prohibitingDoc;
}
// test if user has Admin-Role, if so grant access in anyway.
if (this.getDatabase().getSessionContext().getAccessLevel() == WGDatabase.ACCESSLEVEL_MANAGER) {
return null;
}
// Retrieve the list of editors. If we have retrieved backend data we prefer that one
// bc. the current data may have been modified
List editors = getEffectivePageEditors();
// Case 1: Anyone allowed
if (WGDatabase.anyoneAllowed(editors, false)) {
WGFactory.getLogger().debug(
"mayEditEntryAndContent - Case 1: Anyone allowed!");
return null;
}
// Case 2: regular struct-document - no editor is assigned, so lookup
// editors from parent-Struct
if (editors.isEmpty()
|| (editors.size() == 1 && (editors.get(0) == null || editors
.get(0).toString().equals("")))) {
WGFactory.getLogger().debug(
"mayEditEntryAndContent: Recursion to the root!");
if (this.isRoot()) {
editors = this.getArea().getEditors();
// Is user stated as Editor in the Area-Doc?
if (editors.size() >= 1
&& !editors.get(0).toString().equals("")
&& !this.getDatabase().isMemberOfUserList(editors)) {
WGFactory
.getLogger()
.debug(
"WGArea: mayEditEntryAndContent - Case 4: Not member of user list!");
return this.getArea();
} else {
WGFactory
.getLogger()
.debug(
"mayEditEntryAndContent - Case 1: Area-Document reached, user access granted if nobody specified!");
return null;
}
} else {
return this.getParentEntry().mayEditChildren();
}
}
// Case 3: nobody is allowed to edit children of this struct.
else if (editors.size() == 1
&& editors.get(0).toString()
.equals(WGStructEntry.NOONE_ALLOWED)) {
WGFactory.getLogger().debug(
"mayEditEntryAndContent - Case 3: No user allowed!");
return this;
}
// Case 4: current struct-document prohibits user to edit children, so
// return current struct-doc
else if (editors.size() >= 1
&& !this.getDatabase().isMemberOfUserList(editors)) {
WGFactory
.getLogger()
.debug(
"mayEditEntryAndContent - Case 4: Not member of user list!");
return this;
}
// Case 5: everything is cool, user may create children :-)
else {
WGFactory.getLogger().debug(
"mayEditEntryAndContent - Case 5: User access granted!");
return null;
}
}
/**
* Tests if the current user may modify each document of the current pages subtree.
* This is intended to be tested on operations that involve each document of a page subtree including the current one, like moving it.
* The method exits normally if the user may do this operation or with an {@link WGAuthorisationException} if he does not, holding detail information about the cause.
*/
public void performSubtreeModificationCheck() throws WGAPIException, WGAuthorisationException {
for (WGStructEntry child : getChildEntries()) {
WGDocument causeDoc = child.mayEditEntryAndContent();
if (causeDoc != null) {
if (causeDoc instanceof WGStructEntry) {
WGStructEntry causeStruct = (WGStructEntry) causeDoc;
throw new WGAuthorisationException("Page " + causeStruct.getDocumentKey() + " prevents you from modifying the given page subtree");
}
else if (causeDoc instanceof WGArea) {
WGArea causeArea = (WGArea) causeDoc;
throw new WGAuthorisationException("Area " + causeArea.getName() + " prevents you from modifying the given page subtree");
}
else {
throw new WGAuthorisationException("Document " + causeDoc.getDocumentKey() + " prevents you from modifying the given page subtree");
}
}
if (child.getContentCount() != child.getAllContent(true).size()) {
throw new WGAuthorisationException("A read protected document, invisible to you, prevents you from modifying the given page subtree");
}
child.performSubtreeModificationCheck();
}
}
/**
* Tests if the current user may read contents on this page and below, and if so returns the document whose settings prohibit it.
* @return null, if the user may read. If not, the WGStructEntry or WGArea
* object ist returned that denies reading access
* @throws WGAPIException
*/
public WGDocument getReadProtectionCause() throws WGAPIException {
if (!getDatabase().isSessionOpen()) {
throw new WGClosedSessionException();
}
// Are hierarchical reader fields enabled ?
if (!getDatabase().isPageReadersEnabled()) {
return null;
}
// No read protection on master session
if (getDatabase().getSessionContext().isMasterSession()) {
return null;
}
// Parent check
if (isRoot()) {
if (!getArea().mayReadContent()) {
return getArea();
}
}
else {
WGDocument prohibitingDoc = getParentEntry().getReadProtectionCause();
if (prohibitingDoc != null) {
return prohibitingDoc;
}
}
// Local check
List readers = getEffectiveReaders();
if (!WGDatabase.anyoneAllowed(readers, true) && !getDatabase().isMemberOfUserList(readers)) {
return this;
}
return null;
}
/**
* Tests if the current user may read contents on this page and below
* @return true if the user may, false if not
* @throws WGAPIException
*/
public boolean mayReadContent() throws WGAPIException {
WGDocument cause = getReadProtectionCause();
return (cause == null);
}
/**
* Tests if the current user may edit child entries and their contents.
*
* @return null, if the user may edit. If not, the WGStructEntry or WGArea
* object ist returned that denies editing access
* @throws WGAPIException
*/
public WGDocument mayEditChildren() throws WGAPIException {
// returns null if user may edit Children
// Otherwise returns WGStructEntry, which prohibits user from editing
// this Entry.
// test if user has Admin-Role, if so grant access in anyway.
if (this.getDatabase().getSessionContext().getAccessLevel() == WGDatabase.ACCESSLEVEL_MANAGER) {
return null;
}
// Test for read access. Without read access no write acces
WGDocument prohibitingDoc = getReadProtectionCause();
if (prohibitingDoc != null) {
return prohibitingDoc;
}
// fetch information about Struct-Editors
List editors = getEffectiveChildEditors();
// Case 1: Anyone allowed
if (WGDatabase.anyoneAllowed(editors, false)) {
WGFactory.getLogger().debug(
"mayEditChildren - Case 1: Anyone allowed!");
return null;
}
// Case 2: regular struct-document - no editor is assigned, so lookup
// editors from parent-Struct
if (editors.isEmpty()
|| (editors.size() == 1 && editors.get(0).toString().equals(""))) {
WGFactory.getLogger().debug(
"mayEditChildren: Recursion to the root!");
if (this.isRoot()) {
if (!getArea().mayEditAreaChildren()) {
return getArea();
}
else {
return null;
}
}
else {
return this.getParentEntry().mayEditChildren();
}
}
// Case 3: nobody is allowed to edit children of this struct.
else if (editors.size() == 1
&& editors.get(0).toString()
.equals(WGStructEntry.NOONE_ALLOWED)) {
WGFactory.getLogger().debug(
"mayEditChildren - Case 3: No user allowed!");
return this;
}
// Case 4: current struct-document prohibits user to edit children, so
// return current struct-doc
else if (editors.size() >= 1
&& !this.getDatabase().isMemberOfUserList(editors)) {
WGFactory.getLogger().debug(
"mayEditChildren - Case 4: Not member of user list!");
return this;
}
// Case 5: everything is cool, user may create children :-)
else {
WGFactory.getLogger().debug(
"mayEditChildren - Case 5: User access granted!");
return null;
}
}
/**
* Create a child struct entry
*
* @param contentType
* Content type for new entry
* @param title
* Title of new entry
* @return The newly created entry
* @throws WGAPIException
*/
public WGStructEntry createChildEntry(WGContentType contentType,
String title) throws WGAPIException {
return this.getDatabase().createStructEntry(this, contentType, title);
}
/**
* Create a content document under this struct entry.
*
* @param language
* The language for the content
* @param title
* The title of the content
* @return The newly created content
* @throws WGAPIException
*/
public WGContent createContent(WGLanguage language, String title)
throws WGAPIException {
return this.getDatabase().createContent(this, language, title);
}
/**
* Create a content document under this struct entry.
*
* @param language
* The language for the content
* @param title
* The title of the content
* @return The newly created content
* @throws WGAPIException
*/
public WGContent createContent(String language, String title)
throws WGAPIException {
return this.getDatabase().createContent(this, getDatabase().getLanguage(language), title);
}
/**
* Creates a new content for this struct entry, using the default language of the database.
* @param title The title of the new content
* @return The newly created content
* @throws WGAPIException
*/
public WGContent createContent(String title) throws WGAPIException {
WGLanguage lang = getDatabase().getLanguage(getDatabase().getDefaultLanguage());
if (lang != null) {
return createContent(lang, title);
}
else {
throw new WGCreationException("No default language configured for this database");
}
}
/*
* (Kein Javadoc)
*
* @see de.innovationgate.webgate.api.WGDocument#createClone(de.innovationgate.webgate.api.WGDatabase)
*/
public WGDocument createClone(WGDatabase db, WGDocument ref)
throws WGAPIException {
if (!(ref instanceof WGStructEntry || ref instanceof WGArea)) {
return null;
}
WGContentType contentType = db.getContentType((String) getMetaData(META_CONTENTTYPE));
if (contentType == null) {
throw new WGCreationException("The content type '" + getMetaData(META_CONTENTTYPE) + "' is not available in target database");
}
WGStructEntry newEntry = db.createStructEntry(getStructKey(), ref, contentType,
getTitle());
pushData(newEntry);
if (newEntry.save(getLastModified())) {
return newEntry;
} else {
return null;
}
}
/*
* (non-Javadoc)
*
* @see de.innovationgate.webgate.api.WGDocument#pushData(de.innovationgate.webgate.api.WGDocument)
*/
public void pushData(WGDocument doc) throws WGAPIException {
super.pushData(doc);
WGStructEntry entry = (WGStructEntry) doc;
entry.setPosition(getPosition().intValue());
entry.setMetaData(META_WORKFLOW_NAME, getMetaData(META_WORKFLOW_NAME));
entry.setMetaData(META_CONTENTTYPE, getMetaData(META_CONTENTTYPE));
entry.setReaders(new ArrayList(getReaders()));
entry.setChildEditors(new ArrayList(getChildEditors()));
entry.setPageEditors(new ArrayList(getPageEditors()));
entry.setTitle(getTitle());
if (getDatabase().getContentStoreVersion() >= WGDatabase.CSVERSION_WGA5 && doc.getDatabase().getContentStoreVersion() >= WGDatabase.CSVERSION_WGA5) {
entry.setUniqueName(getUniqueName());
entry.setPublished(new HashMap(getPublished()));
}
boolean wasRepositioned = isDifferentParent(entry);
// We must test the target parents for existence. They may not yet exist (see #00000640)
if (wasRepositioned) {
if (this.getParentEntry() == null) {
WGArea targetArea = entry.getDatabase().getArea(getArea().getName());
if (targetArea != null) {
entry.getDatabase().moveStructEntry(entry, targetArea);
}
}
else {
WGStructEntry targetEntry = entry.getDatabase().getStructEntryByKey(getParentEntry().getStructKey());
if (targetEntry != null) {
entry.getDatabase().moveStructEntry(entry, targetEntry);
}
}
}
}
/**
* Tests if this struct entry has a different parent than the given clone.
* Used in synchronisation to test if the struct was repositioned since last sync.
* @param clone The struct entry clone
* @return true if it was repositioned, false otherwise
* @throws WGAPIException
*/
public boolean isDifferentParent(WGStructEntry clone) throws WGAPIException {
// Look for reposition - We compare document keys bc. the tested documents may be from different databases, yet still represent the same document
boolean wasRepositioned = false;
if (this.isRoot() && clone.isRoot()) {
wasRepositioned = (this.getArea().getDocumentKey().equals(clone.getArea().getDocumentKey()) ? false
: true);
} else if (!this.isRoot() && !clone.isRoot()) {
wasRepositioned = (this.getParentEntry().getDocumentKey().equals(
clone.getParentEntry().getDocumentKey()) ? false : true);
} else if (this.isRoot() != clone.isRoot()) {
wasRepositioned = true;
}
return wasRepositioned;
}
protected boolean remove(boolean performRemoveCheck) throws WGAPIException {
if (db.isSessionOpen() == false) {
throw new WGClosedSessionException();
}
if (performRemoveCheck) {
performRemoveCheck();
}
// Deleting child contents and struct entries
if (!db.hasFeature(WGDatabase.FEATURE_AUTOCASCADES_DELETIONS)
&& db.getSessionContext().isCascadeDeletions()) {
// Delete all content
Iterator doomed = getAllContent(true).iterator();
while (doomed.hasNext()) {
WGContent doomedContent = (WGContent) doomed.next();
if (!doomedContent.isDeleted()) {
if (doomedContent.remove() == false) {
throw new WGBackendException("Unable to delete content " + doomedContent.getContentKey().toString());
}
}
}
// There are still contents on this struct entry (that the current user is not allowed to read) so we cancel
// Should not happen any more since we test this previously
/*
if (db.hasContents(this)) {
throw new WGAuthorisationException("Unable to delete struct entry " + getStructKey() + " because it contains contents that are not under your access");
}
*/
// Delete all children
doomed = getChildEntries().iterator();
while (doomed.hasNext()) {
WGStructEntry doomedEntry = (WGStructEntry) doomed.next();
if (!doomedEntry.isDeleted()) {
if (doomedEntry.remove(false) == false) {
throw new WGBackendException("Unable to delete struct entry " + doomedEntry.getStructKey());
}
}
}
}
return innerRemove();
}
public boolean remove() throws WGAPIException {
return remove(true);
}
/* (non-Javadoc)
* @see de.innovationgate.webgate.api.WGDocument#performRemovalCheck()
*/
public void performRemoveCheck() throws WGAuthorisationException, WGAPIException {
super.performRemoveCheck();
// Test general right to delete struct entries
if (db.getSessionContext().getAccessLevel() < WGDatabase.ACCESSLEVEL_EDITOR) {
throw new WGAuthorisationException("You are not authorized to delete entries in this database!");
}
// Test rights to delete this entry and content
// Test rights to edit this entry and content
if (getContentType() != null && !getContentType().mayCreateContent()) {
throw new WGAuthorisationException("You are not allowed to delete documents of content type '" + getContentType().getName() + "'");
}
WGDocument cause = mayEditEntryAndContent();
if (cause != null) {
if (cause instanceof WGStructEntry) {
WGStructEntry entry = (WGStructEntry) cause;
throw new WGAuthorisationException(
"You cannot delete struct entry " + getStructKey() + " because the settings of struct entry " + entry.getStructKey() + " deny this");
} else {
WGArea area = (WGArea) cause;
throw new WGAuthorisationException(
"You cannot delete struct entry " + getStructKey() + " because the settings of area '" + area.getName() + " deny this");
}
}
// Test rights to delete children, if there are any
if (hasChildren()) {
cause = mayEditChildren();
if (cause != null) {
if (cause instanceof WGStructEntry) {
WGStructEntry entry = (WGStructEntry) cause;
throw new WGAuthorisationException(
"You cannot delete the children of struct entry " + getStructKey() + " because the settings of child entry " + entry.getStructKey() + " deny this");
} else {
WGArea area = (WGArea) cause;
throw new WGAuthorisationException(
"You cannot delete the children of struct entry " + getStructKey() + " children, because the settings of area '" + area.getName() + " denies this");
}
}
}
// Test if there are read protected contents on this struct entry that we cannot see
if (getContentCount() != getAllContent(true).size()) {
throw new WGAuthorisationException("You cannot delete struct entry " + getStructKey() + " because it contains read protected contents invisible to you");
}
// Perform removal check on all child contents
Iterator contents = getAllContent(true).iterator();
while (contents.hasNext()) {
((WGContent) contents.next()).performRemoveCheck();
}
// Perform removal check on all child entries
Iterator entries = getChildEntries().iterator();
while (entries.hasNext()) {
((WGStructEntry) entries.next()).performRemoveCheck();
}
}
/*
* (Kein Javadoc)
*
* @see de.innovationgate.webgate.api.WGDocument#save()
*/
public boolean save(Date lastModified) throws WGAPIException {
boolean result = super.save(lastModified);
if (result == true) {
gatherRetrievalKeys();
}
return result;
}
public void performSaveCheck() throws WGAPIException, WGAuthorisationException {
super.performSaveCheck();
// Test rights to edit this entry and content
if (getContentType() != null && !getContentType().mayCreateContent()) {
throw new WGAuthorisationException("You are not allowed to edit documents of content type '" + getContentType().getName() + "'");
}
WGDocument cause = mayEditEntryAndContent();
if (cause != null) {
if (cause instanceof WGStructEntry) {
WGStructEntry entry = (WGStructEntry) cause;
throw new WGAuthorisationException(
"You cannot edit this struct entry, because the settings of struct entry " + entry.getStructKey() + " deny this");
} else {
throw new WGAuthorisationException(
"You cannot edit struct entries in this area");
}
}
// Check if Unique Name exists
if (db.hasFeature(WGDatabase.FEATURE_FULLCONTENTFEATURES) &&
getDatabase().getSessionContext().isTestUniqueNames()) {
boolean isUniqueNameOK = false;
// B00004D4E
if (this.getUniqueName() != null) {
UniqueNameTester tester = null;
try {
tester = new UniqueNameTester(this.getUniqueName(), null, getStructKey());
Thread proveThread = new Thread(tester);
proveThread.start();
proveThread.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
isUniqueNameOK = tester.isUniqueNameOK;
}
else {
isUniqueNameOK = true;
}
if (!isUniqueNameOK) {
throw new WGDuplicateKeyException("Unique name '" + this.getUniqueName() + "' already exists.");
}
}
}
/**
* Returns the fetch state for content of this structentry as constant
* FETCHSTATE_... WGA clients may determine by this state how expensive a
* desired fetch operation might be. E.g. retrieving content including
* archived versions may be expensive if it has not been fetched yet
* Otherwise, everything that is already fetched is cheap to retrieve.
*/
public int getContentFetchState() {
/*
Integer fetchState = (Integer) this.fetchedContentForUsers.get(content
.getCurrentUser());
if (fetchState != null) {
return fetchState.intValue();
} else {
return FETCHSTATE_NONE;
}*/
//B00003852
Integer fetchState = (Integer) this.contentCache.get(FETCH_STATE_KEY);
if (fetchState != null) {
return fetchState.intValue();
} else {
return FETCHSTATE_NONE;
}
}
/**
* Returns a path consisting of the structkeys from root entry to this
* entry, divided by slashes. This information can be used as an easy
* reference for the position of this entry in the area's hierarchy
* @throws WGAPIException
*/
public String getStructKeyPath() throws WGAPIException {
List path = new ArrayList();
WGStructEntry entry = this;
while (entry != null) {
path.add(String.valueOf(entry.getStructKey()));
entry = entry.getParentEntry();
}
Collections.reverse(path);
return WGUtils.serializeCollection(path, "/");
}
/**
* Returns a path consisting of the entry titles from root entry to this
* entry, divided by slashes This information can be used as an easy
* reference for the position of this entry in the area's hierarchy
* @throws WGAPIException
*/
public String getTitlePath() throws WGAPIException {
List path = new ArrayList();
WGStructEntry entry = this;
while (entry != null) {
path.add(String.valueOf(entry.getTitle()));
entry = entry.getParentEntry();
}
Collections.reverse(path);
return WGUtils.serializeCollection(path, "/");
}
/**
* Determines if this struct entry is a descendant of the given entry, i.e.
* is a part of it's child hierarchy.
*
* @param entry
* The entry that may be a descendant
* @return yes if it is a descendant of the given entry, false if not
* @throws WGAPIException
*/
public boolean isDescendantOf(WGStructEntry entry) throws WGAPIException {
WGStructEntry parent = getParentEntry();
while (parent != null) {
if (parent.equals(entry)) {
return true;
}
parent = parent.getParentEntry();
}
return false;
}
protected boolean knowsParentEntry() {
return (this.parentEntry != null);
}
/*
* (non-Javadoc)
* @see de.innovationgate.webgate.api.locking.Lockable#getParentLockable()
*/
public Lockable getParentLockable() throws WGAPIException {
WGStructEntry parentEntry = getParentEntry();
if (parentEntry != null) {
return parentEntry;
}
else {
return getArea();
}
}
/**
* Returns the set of contents for the current WGStructEntry
* @param includeArchived Set true to also retrieve archived contents
* @return The content set
* @throws WGAPIException
*/
public ContentSet getContentSet(boolean includeArchived) throws WGAPIException {
// Test for read access
if (!mayReadContent()) {
return new ContentSet();
}
// This set is returned. It is filled either from cache or from backend database
ContentSet contentSet = null;
// We retrieve the cached contents. We can bypass this potentially expensive operation if we know the needed set has not yet been fetched
// or caching is disabled
List<WGContent> cachedContents = null;
if (alreadyFetched(includeArchived) && isCachingEnabled()) {
cachedContents = getDatabase().fetchDocumentListCache((WGDocumentListCache) this.contentCache.get(FETCH_CONTENT_CACHE), WGDocument.TYPE_CONTENT);
}
// Double checked locking
if (cachedContents == null) {
int operationType = (includeArchived ? WGOperationKey.OP_STRUCT_CONTENTS_INCLUDING_ARCHIVED : WGOperationKey.OP_STRUCT_CONTENTS);
WGOperationKey op = db.obtainOperationKey(operationType, String.valueOf(getStructKey()));
synchronized (op) {
try {
op.setUsed(true);
if (alreadyFetched(includeArchived) && isCachingEnabled()) {
cachedContents = getDatabase().fetchDocumentListCache((WGDocumentListCache) this.contentCache.get(FETCH_CONTENT_CACHE), WGDocument.TYPE_CONTENT);
}
if (cachedContents == null) {
WGContentList contentList = this.db.getAllContent(this, includeArchived);
contentSet = new ContentSet(contentList);
// If cache writing enabled we will write the contentSet to the cache
if (isCachingEnabled() && getDatabase().getSessionContext().isCacheWritingEnabled()) {
// B00003852
contentCache.clearUser();
List<WGContent> contents = new ArrayList();
contents.addAll(contentSet.getAllContent().values());
if (includeArchived) {
contents.addAll(contentSet.getArchivedContent().values());
}
this.contentCache.put(FETCH_CONTENT_CACHE, WGDocumentListCache.buildFromDocuments(contents));
this.contentCache.put(FETCH_STATE_KEY, new Integer(includeArchived ? FETCHSTATE_ARCHIVED : FETCHSTATE_ACTIVE_ONLY));
}
}
}
finally {
op.setUsed(false);
}
}
}
// Fill buffers from cache
// Might be ineffective to build the set on each request from a list. Should we cache a more organized form?
if (contentSet == null && cachedContents != null) {
contentSet = new ContentSet(cachedContents);
}
return contentSet;
}
/**
* Returns the index of the current struct entry in the list of it's siblings
* @throws WGAPIException
*/
public int getSiblingIndex() throws WGAPIException {
return getSiblingEntries().getIndexOfEntry(this);
}
public Class getChildNodeType() {
return WGStructEntry.class;
}
public List getChildNodes() throws WGAPIException {
return new ArrayList(getChildEntries());
}
public String getNodeKey() throws WGAPIException {
return getDocumentKey();
}
public String getNodeTitle(String language) throws WGAPIException {
WGContent con = getReleasedContent(language);
if (con != null) {
return con.getTitle();
}
else {
return getTitle();
}
}
public PageHierarchyNode getParentNode() throws WGAPIException {
if (isRoot()) {
return getArea();
}
else {
return getParentEntry();
}
}
/**
* Returns the archived contents for one language, as list in the order of their version numbers.
* @param language The language to retrieve archived contents for
* @return List of archived contents of the given language, ordered by version ascending
* @throws WGAPIException
*/
public List<WGContent> getArchivedContent(String language) throws WGAPIException {
ContentSet set = getContentSet(true);
List<WGContent> archivedContent = new ArrayList<WGContent>();
Iterator<WGContent> contents = set.getArchivedContent().values().iterator();
WGContent content;
while (contents.hasNext()) {
content = (WGContent) contents.next();
if (content.getLanguage().getName().equals(language)) {
archivedContent.add(content);
}
}
Collections.sort(archivedContent, new Comparator<WGContent>() {
public int compare(WGContent o1, WGContent o2) {
try {
int v1 = o1.getVersion();
int v2 = o2.getVersion();
return v2 - v1;
}
catch (WGAPIException e) {
throw new RuntimeException("WGAPI Exception comparing content versions", e);
}
}
});
return archivedContent;
}
protected void updateBackendCaches(WGDocumentCore core) {
super.updateBackendCaches(core);
try {
de.innovationgate.webgate.api.WGStructEntry.SessionData sessionData = getSessionData();
sessionData.setBackendReaders((List) core.getMetaData(WGStructEntry.META_READERS));
sessionData.setBackendPageEditors((List) core.getMetaData(WGStructEntry.META_PAGE_EDITORS));
sessionData.setBackendChildEditors((List) core.getMetaData(WGStructEntry.META_CHILD_EDITORS));
}
catch (WGAPIException e) {
WGFactory.getLogger().error("Error updating struct entry backend cache", e);
}
}
private SessionData getSessionData() {
DocumentContext con = getDocumentSessionContext();
SessionData data = (SessionData) con.getCustomData();
if (data == null) {
data = new SessionData();
con.setCustomData(data);
}
return data;
}
/**
* Returns the unique name of this struct entry, null if there is none
* @throws WGAPIException
*/
public String getUniqueName() throws WGAPIException {
return (String) getMetaData(META_UNIQUENAME);
}
/**
* Sets the unique name of this struct entry
*/
public void setUniqueName(String name) throws WGAPIException {
setMetaData(META_UNIQUENAME, name);
}
/**
* Returns the dates when this page was first published in its available languages
* The returned map is keyed by language codes. The corresponding value is the date/time when the page was first published in this language.
* @throws WGAPIException
*/
public Map<String,Date> getPublished() throws WGAPIException {
return (Map<String,Date>) getMetaData(META_PUBLISHED);
}
protected void setPublished(Map<String,Date> published) throws WGAPIException {
setMetaData(META_PUBLISHED, published);
}
/**
* Implements the visitor pattern on the page hierarchy branch of this struct entry. The visitor visits this and all struct entries below and all of their (non-archived) contents.
* @param visitor
* @throws WGAPIException
*/
public void visit(WGPageVisitor visitor) throws WGAPIException {
visitor.visit(this);
List<WGContent> contents = getAllContent();
for (WGContent content : contents) {
visitor.visit(content);
}
List<WGStructEntry> children = getChildEntries().asList();
for (WGStructEntry child : children) {
child.visit(visitor);
}
}
/**
* Checks if the current user may create a new child struct entry of the given content type. If so the method exits normally. Otherwise an rexception is thrown.
* @param ct The content type to use.
* @throws WGAPIException @throws WGAPIException If the user may not create the document. The exception informs about the reason.
*/
public void performChildCreationCheck(WGContentType ct) throws WGAPIException {
getDatabase().performStructCreationCheck(null, this, ct);
}
/**
* Checks if the current user may create a new child page with the given data. If so the method exits normally. Otherwise an exception is thrown.
* @param ct The content type to use for the struct entry
* @param lang The language to use for the content
* @throws WGAPIException If the user may not create the document. The exception informs about the reason.
*/
public void performChildPageCreationCheck(WGContentType ct, WGLanguage lang) throws WGAPIException {
performChildCreationCheck(ct);
getDatabase().performContentCreationCheck(null, lang);
}
/**
* Creates a new child page, including struct entry and content.
* Both documents are already saved when the method exists.
* @param contentType The content type of the page
* @param title The title of the page that will be used for both struct entry
* @param language The language of the content to create. Leave null to use databases default language.
* @return The content of the created page
* @throws WGAPIException
*/
public WGContent createChildPage(WGContentType contentType, String title, String language) throws WGAPIException {
if (language == null) {
language = getDatabase().getDefaultLanguage();
}
WGLanguage lang = getDatabase().getLanguage(language);
performChildPageCreationCheck(contentType, lang);
WGStructEntry entry = createChildEntry(contentType, title);
entry.save();
WGContent content = entry.createContent(lang, title);
content.save();
return content;
}
@Override
public int getType() {
return WGDocument.TYPE_STRUCTENTRY;
}
/**
* Returns a list of allowed readers of contents on this struct entry. This method only returns the readers set on this entry, not those that are effective in hierarchy.
* This is only supported on content stores of version 5.
* @return List
* @throws WGAPIException
*/
public List getReaders() throws WGAPIException {
List readers = (List) this.getMetaData(WGArea.META_READERS);
if (readers != null) {
return readers;
}
else {
return new ArrayList();
}
}
/**
* Sets the allowed readers for this struct entry. This is only supported on content stores of version 5.
* @param list
* @throws WGAPIException
*/
public boolean setReaders(List list) throws WGAPIException {
return setMetaData(META_READERS, list);
}
/**
* Returns the list of currently effective readers. These may differ from the currently set readers in a yet unsaved document.
* @throws WGAPIException
*/
public List getEffectiveReaders() throws WGAPIException {
List readers = getSessionData().getBackendReaders();
if (readers == null) {
readers = getReaders();
}
return readers;
}
/**
* Returns the list of currently effective page editors.. These may differ from the currently set editors in a yet unsaved document.
* @throws WGAPIException
*/
public List getEffectivePageEditors() throws WGAPIException {
List editors = getSessionData().getBackendPageEditors();
if (editors == null) {
editors = getPageEditors();
}
return editors;
}
/**
* Returns the list of currently effective child editors.. These may differ from the currently set editors in a yet unsaved document.
* @throws WGAPIException
*/
public List getEffectiveChildEditors() throws WGAPIException {
List editors = getSessionData().getBackendChildEditors();
if (editors == null) {
editors = getChildEditors();
}
return editors;
}
}