Versuch der Behebung der Probleme mit Sprachvarianten und Assoziationen
git-svn-id: https://svn.libreccm.org/ccm/trunk@973 8810af33-2d31-482b-a856-94f89814c4dfmaster
parent
c3e8aa14b3
commit
adc6fa293a
|
|
@ -203,10 +203,12 @@ import java.util.Set;
|
|||
public class ContentItem extends VersionedACSObject implements CustomCopy {
|
||||
|
||||
private static final Logger s_log = Logger.getLogger(ContentItem.class);
|
||||
private static final Logger s_logDenorm = Logger.getLogger(ContentItem.class.getName() + ".Denorm");
|
||||
private static final Logger s_logDenorm =
|
||||
Logger.getLogger(ContentItem.class.getName()
|
||||
+ ".Denorm");
|
||||
private static final String MODEL = "com.arsdigita.cms";
|
||||
private static final String QUERY_PENDING_ITEMS =
|
||||
MODEL + ".getPendingSortedByLifecycle";
|
||||
MODEL + ".getPendingSortedByLifecycle";
|
||||
public static final String BASE_DATA_OBJECT_TYPE = MODEL + ".ContentItem";
|
||||
/**
|
||||
* A state marking the draft or master item corresponding to a
|
||||
|
|
@ -234,7 +236,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
public static final String VERSIONS = "slaveVersions";
|
||||
public static final String CONTENT_SECTION = "section";
|
||||
private static final String PUBLISH_LISTENER_CLASS =
|
||||
PublishLifecycleListener.class.getName();
|
||||
PublishLifecycleListener.class.getName();
|
||||
private VersionCache m_pending;
|
||||
private VersionCache m_live;
|
||||
private boolean m_wasNew;
|
||||
|
|
@ -302,19 +304,20 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
}
|
||||
}
|
||||
private static DomainObjectObserver s_parentObs =
|
||||
new AbstractDomainObjectObserver() {
|
||||
new AbstractDomainObjectObserver() {
|
||||
|
||||
public void set(DomainObject dobj, String name,
|
||||
public void set(DomainObject dobj, String name,
|
||||
Object old, Object newVal) {
|
||||
if (PARENT.equals(name)) {
|
||||
ContentItem ci = (ContentItem) dobj;
|
||||
if (PARENT.equals(name)) {
|
||||
ContentItem ci = (ContentItem) dobj;
|
||||
|
||||
if (newVal != null) {
|
||||
PermissionService.setContext(ci.getOID(), ((DataObject) newVal).getOID());
|
||||
}
|
||||
}
|
||||
if (newVal != null) {
|
||||
PermissionService.setContext(ci.getOID(),
|
||||
((DataObject) newVal).getOID());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the base class (<code>DomainObject</code>)
|
||||
|
|
@ -341,7 +344,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
if (isNew()) {
|
||||
s_log.debug(this + " is being newly created; "
|
||||
+ "marking it as a draft version");
|
||||
+ "marking it as a draft version");
|
||||
|
||||
m_wasNew = true;
|
||||
|
||||
|
|
@ -351,7 +354,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
try {
|
||||
final ContentType type =
|
||||
ContentType.findByAssociatedObjectType(getSpecificObjectType());
|
||||
ContentType.findByAssociatedObjectType(
|
||||
getSpecificObjectType());
|
||||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Set content type for " + this + " to " + type);
|
||||
|
|
@ -478,11 +482,11 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
setContentSection(((ContentItem) parent).getContentSection());
|
||||
} else {
|
||||
s_log.debug("The item's parent is not a folder; I am "
|
||||
+ "not setting the default content section");
|
||||
+ "not setting the default content section");
|
||||
}
|
||||
} else {
|
||||
s_log.debug("The item's version is null or it is not draft; "
|
||||
+ "doing nothing");
|
||||
+ "doing nothing");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -522,7 +526,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* Get the parent object.
|
||||
*/
|
||||
public ACSObject getParent() {
|
||||
return (ACSObject) DomainObjectFactory.newInstance((DataObject) get(PARENT));
|
||||
return (ACSObject) DomainObjectFactory.newInstance((DataObject) get(
|
||||
PARENT));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -542,7 +547,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
*/
|
||||
public final ItemCollection getChildren() {
|
||||
final DataAssociationCursor cursor =
|
||||
((DataAssociation) super.get(CHILDREN)).cursor();
|
||||
((DataAssociation) super.get(CHILDREN)).
|
||||
cursor();
|
||||
|
||||
return new ItemCollection(cursor);
|
||||
}
|
||||
|
|
@ -598,7 +604,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* @return The content section to which this item belongs
|
||||
*/
|
||||
public ContentSection getContentSection() {
|
||||
return (ContentSection) DomainObjectFactory.newInstance((DataObject) get(CONTENT_SECTION));
|
||||
return (ContentSection) DomainObjectFactory.newInstance((DataObject) get(
|
||||
CONTENT_SECTION));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -694,7 +701,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* @return the items on the path to the root folder.
|
||||
*/
|
||||
public ItemCollection getPathInfo(boolean includeSelf) {
|
||||
DataCollection collection = SessionManager.getSession().retrieve(BASE_DATA_OBJECT_TYPE);
|
||||
DataCollection collection = SessionManager.getSession().retrieve(
|
||||
BASE_DATA_OBJECT_TYPE);
|
||||
|
||||
String ids = (String) get(ANCESTORS);
|
||||
if (ids == null) {
|
||||
|
|
@ -858,7 +866,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
DataQuery versions = getSession().retrieveQuery(QUERY_PENDING_ITEMS);
|
||||
versions.setParameter("itemID", getDraftVersion().getID());
|
||||
|
||||
return new ItemCollection(new DataQueryDataCollectionAdapter(versions, "item"));
|
||||
return new ItemCollection(new DataQueryDataCollectionAdapter(versions,
|
||||
"item"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -934,13 +943,15 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
s_log.debug("m_live miss");
|
||||
|
||||
final DataAssociationCursor versions =
|
||||
((DataAssociation) get(VERSIONS)).cursor();
|
||||
((DataAssociation) get(VERSIONS)).cursor();
|
||||
|
||||
versions.addEqualsFilter(VERSION, LIVE);
|
||||
|
||||
try {
|
||||
if (versions.next()) {
|
||||
ContentItem item = (ContentItem) DomainObjectFactory.newInstance(versions.getDataObject());
|
||||
ContentItem item =
|
||||
(ContentItem) DomainObjectFactory.newInstance(versions.
|
||||
getDataObject());
|
||||
return m_live.set(item);
|
||||
}
|
||||
return m_live.set(null);
|
||||
|
|
@ -1068,18 +1079,18 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
public void setLive(final ContentItem version) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Setting item " + this + " live with version "
|
||||
+ version);
|
||||
+ version);
|
||||
}
|
||||
|
||||
if (Assert.isEnabled()) {
|
||||
Assert.isTrue(version == null || LIVE.equals(version.getVersion()),
|
||||
"Item version " + version + " must be null or "
|
||||
+ "the live version");
|
||||
"Item version " + version + " must be null or "
|
||||
+ "the live version");
|
||||
}
|
||||
|
||||
if (isLive()) {
|
||||
s_log.debug("The item is already live; getting the current "
|
||||
+ "live version");
|
||||
+ "live version");
|
||||
|
||||
final ContentItem oldVersion = getLiveVersion();
|
||||
|
||||
|
|
@ -1096,7 +1107,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
// XXX We don't need to use a custom query here
|
||||
// anymore.
|
||||
final DataQuery items =
|
||||
SessionManager.getSession().retrieveQuery("com.arsdigita.cms.getLiveItemsWithSameParent");
|
||||
SessionManager.getSession().retrieveQuery(
|
||||
"com.arsdigita.cms.getLiveItemsWithSameParent");
|
||||
items.addNotEqualsFilter("id", oldVersion.getID());
|
||||
items.setParameter("itemId", oldVersion.getID());
|
||||
|
||||
|
|
@ -1109,7 +1121,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug(oldVersion + " is the last child of "
|
||||
+ parent);
|
||||
+ parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1131,14 +1143,14 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
if (parent instanceof ContentBundle || parent instanceof Folder) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Parent of " + oldVersion + " is " + parent
|
||||
+ "; unpublishing the parent");
|
||||
+ "; unpublishing the parent");
|
||||
}
|
||||
|
||||
((ContentItem) parent).setLive(null);
|
||||
}
|
||||
|
||||
s_log.debug("Setting the live version association to null and "
|
||||
+ "saving");
|
||||
+ "saving");
|
||||
|
||||
setLiveVersion(null);
|
||||
|
||||
|
|
@ -1147,7 +1159,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
if (version != null) {
|
||||
s_log.debug("The new version is not null; setting the live "
|
||||
+ "version association");
|
||||
+ "version association");
|
||||
|
||||
setLiveVersion(version);
|
||||
|
||||
|
|
@ -1174,15 +1186,15 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* @return the new pending version
|
||||
*/
|
||||
public ContentItem publish(final LifecycleDefinition cycleDef,
|
||||
final Date startDate) {
|
||||
final Date startDate) {
|
||||
|
||||
applyTag("Published");
|
||||
Versions.suspendVersioning();
|
||||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Publishing item " + this + " with lifecycle "
|
||||
+ "definition " + cycleDef + " and start date "
|
||||
+ startDate);
|
||||
+ "definition " + cycleDef + " and start date "
|
||||
+ startDate);
|
||||
}
|
||||
/* amended Chris Gilbert
|
||||
*
|
||||
|
|
@ -1196,7 +1208,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
if (listener == null) {
|
||||
listener = getPublishListenerClassName();
|
||||
}
|
||||
final Lifecycle cycle = cycleDef.createFullLifecycle(startDate, listener);
|
||||
final Lifecycle cycle =
|
||||
cycleDef.createFullLifecycle(startDate, listener);
|
||||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Instantiated lifecycle " + cycle);
|
||||
|
|
@ -1212,15 +1225,16 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
if (Assert.isEnabled()) {
|
||||
Assert.exists(pending, ContentItem.class);
|
||||
Assert.isTrue(PENDING.equals(pending.getVersion())
|
||||
|| LIVE.equals(pending.getVersion()),
|
||||
"The new pending item must be pending or live; "
|
||||
+ "instead it is " + pending.getVersion());
|
||||
|| LIVE.equals(pending.getVersion()),
|
||||
"The new pending item must be pending or live; "
|
||||
+ "instead it is " + pending.getVersion());
|
||||
}
|
||||
return pending;
|
||||
}
|
||||
|
||||
public String getPublishListenerClassName() {
|
||||
String className = ContentSection.getConfig().getPublishLifecycleListenerClass();
|
||||
String className = ContentSection.getConfig().
|
||||
getPublishLifecycleListenerClass();
|
||||
if (className != null && !"".equals(className)) {
|
||||
return className;
|
||||
} else {
|
||||
|
|
@ -1243,8 +1257,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
if (isLive()) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("The item is currently live; removing the "
|
||||
+ "lifecycle of the public version, "
|
||||
+ getPublicVersion());
|
||||
+ "lifecycle of the public version, "
|
||||
+ getPublicVersion());
|
||||
}
|
||||
|
||||
removeLifecycle(getPublicVersion());
|
||||
|
|
@ -1319,7 +1333,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
if (lifecycle == null) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("The item has no lifecycle; checking if the "
|
||||
+ "public version has a lifecycle");
|
||||
+ "public version has a lifecycle");
|
||||
}
|
||||
|
||||
final ContentItem pub = getPublicVersion();
|
||||
|
|
@ -1333,7 +1347,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("The public version has a lifecycle; "
|
||||
+ "returning " + cyclelife);
|
||||
+ "returning " + cyclelife);
|
||||
}
|
||||
|
||||
return cyclelife;
|
||||
|
|
@ -1382,7 +1396,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Removing lifecycle instance from item "
|
||||
+ itemToRemove);
|
||||
+ itemToRemove);
|
||||
}
|
||||
|
||||
LifecycleService.removeLifecycle(itemToRemove);
|
||||
|
|
@ -1434,11 +1448,12 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
}
|
||||
|
||||
|
||||
Category root = Category.getRootForObject(getContentSection(), useContext);
|
||||
Category root = Category.getRootForObject(getContentSection(),
|
||||
useContext);
|
||||
if (null == root) {
|
||||
s_log.warn("No root category for "
|
||||
+ getContentSection().getOID().toString()
|
||||
+ " with context " + useContext);
|
||||
+ getContentSection().getOID().toString()
|
||||
+ " with context " + useContext);
|
||||
return Collections.EMPTY_LIST.iterator();
|
||||
}
|
||||
|
||||
|
|
@ -1534,6 +1549,10 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
return copy(null, false);
|
||||
}
|
||||
|
||||
public ContentItem copy(String lang) {
|
||||
return copy(null, false, lang);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copy this item, creating a clone.
|
||||
* Reassign composite associations from the copy to point
|
||||
|
|
@ -1548,7 +1567,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* @return the new copy of the item
|
||||
* @see #copyServicesFrom(ContentItem)
|
||||
*/
|
||||
final public ContentItem copy(final ContentItem newParent, final boolean copyServices) {
|
||||
final public ContentItem copy(final ContentItem newParent,
|
||||
final boolean copyServices) {
|
||||
ContentItem newItem = makeCopy();
|
||||
if (newParent != null) {
|
||||
newItem.setParent(newParent);
|
||||
|
|
@ -1559,6 +1579,21 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
return newItem;
|
||||
}
|
||||
|
||||
final public ContentItem copy(final ContentItem newParent,
|
||||
final boolean copyServices,
|
||||
final String lang) {
|
||||
ContentItem newItem = makeCopy(lang);
|
||||
if (newParent != null) {
|
||||
newItem.setParent(newParent);
|
||||
}
|
||||
if (copyServices) {
|
||||
newItem.copyServicesFrom(this);
|
||||
}
|
||||
return newItem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Performs the actual mechanics of copying a content item.
|
||||
* Non-final so that subtypes can extend copying behavior.
|
||||
|
|
@ -1579,6 +1614,29 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
return newItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link ACSObject#makeCopy} which allows to pass the (further)
|
||||
* language of the copy.
|
||||
*
|
||||
* @param language
|
||||
* @return
|
||||
*/
|
||||
protected ContentItem makeCopy(String language) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Copy taking place", new Throwable("trace"));
|
||||
}
|
||||
|
||||
LanguageAwareObjectCopier copier = new LanguageAwareObjectCopier(
|
||||
language);
|
||||
|
||||
final ContentItem newItem = copier.copyItem(this);
|
||||
// Doesn't seem like I should have to do this, but what the hell
|
||||
newItem.setContentSection(getContentSection());
|
||||
newItem.save();
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transfer services, such as categories,
|
||||
* from the passed-in item to this item. This method should be
|
||||
|
|
@ -1653,7 +1711,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
setLive(pending);
|
||||
ContentBundle draftBundle = getBundle();
|
||||
ContentBundle liveBundle = pending.getBundle();
|
||||
if (draftBundle != null && liveBundle != null && !liveBundle.isLiveVersion()) {
|
||||
if (draftBundle != null && liveBundle != null && !liveBundle.
|
||||
isLiveVersion()) {
|
||||
draftBundle.promotePendingVersion(liveBundle);
|
||||
}
|
||||
|
||||
|
|
@ -1669,7 +1728,7 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
*/
|
||||
protected void setVersionRecursively(final String version) {
|
||||
s_log.debug("Recursively updating the version attribute of the "
|
||||
+ "item");
|
||||
+ "item");
|
||||
|
||||
new VersionUpdater(version).updateItemVersion(this);
|
||||
}
|
||||
|
|
@ -1702,9 +1761,10 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* @deprecated use {@link #copyProperty(CustomCopy, Property, ItemCopier)} instead
|
||||
*/
|
||||
protected final boolean copyProperty(final ContentItem source,
|
||||
final String attribute,
|
||||
final ItemCopier copier) {
|
||||
throw new UnsupportedOperationException("use copyProperty(CustomCopy, Property, ItemCopier) for copying");
|
||||
final String attribute,
|
||||
final ItemCopier copier) {
|
||||
throw new UnsupportedOperationException(
|
||||
"use copyProperty(CustomCopy, Property, ItemCopier) for copying");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1793,8 +1853,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
* to copy the property.
|
||||
*/
|
||||
public boolean copyProperty(final CustomCopy source,
|
||||
final Property property,
|
||||
final ItemCopier copier) {
|
||||
final Property property,
|
||||
final ItemCopier copier) {
|
||||
String attribute = property.getName();
|
||||
if (CHILDREN.equals(attribute)) {
|
||||
return true;
|
||||
|
|
@ -1822,26 +1882,29 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
// If live Bundle already exists, recategorize.
|
||||
if (PARENT.equals(attribute)) {
|
||||
ACSObject parent = ((ContentItem) source).getParent();
|
||||
if (parent != null && copier.getCopyType() == ItemCopier.VERSION_COPY) {
|
||||
if (parent != null && copier.getCopyType()
|
||||
== ItemCopier.VERSION_COPY) {
|
||||
if (parent instanceof ContentBundle) {
|
||||
|
||||
ContentBundle bundle = (ContentBundle) parent;
|
||||
ContentBundle liveBundle = (ContentBundle) bundle.getPublicVersion();
|
||||
ContentBundle liveBundle = (ContentBundle) bundle.
|
||||
getPublicVersion();
|
||||
if (liveBundle == null) {
|
||||
liveBundle = (ContentBundle) bundle.createPendingVersion(null);
|
||||
liveBundle =
|
||||
(ContentBundle) bundle.createPendingVersion(null);
|
||||
} else {
|
||||
Set liveCatSet = new HashSet();
|
||||
Set draftCatSet = new HashSet();
|
||||
|
||||
CategoryCollection liveCategories =
|
||||
liveBundle.getCategoryCollection();
|
||||
liveBundle.getCategoryCollection();
|
||||
while (liveCategories.next()) {
|
||||
liveCatSet.add(liveCategories.getCategory());
|
||||
}
|
||||
liveCategories.close();
|
||||
|
||||
CategoryCollection draftCategories =
|
||||
bundle.getCategoryCollection();
|
||||
bundle.getCategoryCollection();
|
||||
while (draftCategories.next()) {
|
||||
draftCatSet.add(draftCategories.getCategory());
|
||||
}
|
||||
|
|
@ -1854,7 +1917,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
|
||||
Iterator removeIter = catsToRemove.iterator();
|
||||
while (removeIter.hasNext()) {
|
||||
liveBundle.removeCategory((Category) removeIter.next());
|
||||
liveBundle.removeCategory(
|
||||
(Category) removeIter.next());
|
||||
}
|
||||
Iterator addIter = catsToAdd.iterator();
|
||||
while (addIter.hasNext()) {
|
||||
|
|
@ -1932,7 +1996,8 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
// slp.getLocale()
|
||||
|
||||
try {
|
||||
locale = Locale.fromJavaLocale(new java.util.Locale(getLanguage(), ""));
|
||||
locale = Locale.fromJavaLocale(new java.util.Locale(getLanguage(),
|
||||
""));
|
||||
} catch (GlobalizationException e) {
|
||||
s_log.warn("GlobalizationException thrown in getLocale()", e);
|
||||
throw new UncheckedWrapperException(e.getMessage());
|
||||
|
|
@ -2024,20 +2089,26 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
super.beforeDelete();
|
||||
|
||||
// remove Link associations to this
|
||||
DataCollection dc = SessionManager.getSession().retrieve(Link.BASE_DATA_OBJECT_TYPE);
|
||||
DataCollection dc = SessionManager.getSession().retrieve(
|
||||
Link.BASE_DATA_OBJECT_TYPE);
|
||||
dc.addEqualsFilter(Link.TARGET_ITEM + "." + ACSObject.ID,
|
||||
getID());
|
||||
getID());
|
||||
while (dc.next()) {
|
||||
Link link = (Link) DomainObjectFactory.newInstance(dc.getDataObject());
|
||||
Link link = (Link) DomainObjectFactory.newInstance(
|
||||
dc.getDataObject());
|
||||
link.setTargetItem(null);
|
||||
}
|
||||
|
||||
// remove ContentGroup associations to this
|
||||
dc = SessionManager.getSession().retrieve(ContentGroupAssociation.BASE_DATA_OBJECT_TYPE);
|
||||
dc.addEqualsFilter(ContentGroupAssociation.CONTENT_ITEM + "." + ACSObject.ID,
|
||||
getID());
|
||||
dc = SessionManager.getSession().retrieve(
|
||||
ContentGroupAssociation.BASE_DATA_OBJECT_TYPE);
|
||||
dc.addEqualsFilter(ContentGroupAssociation.CONTENT_ITEM + "."
|
||||
+ ACSObject.ID,
|
||||
getID());
|
||||
while (dc.next()) {
|
||||
ContentGroupAssociation groupAssoc = (ContentGroupAssociation) DomainObjectFactory.newInstance(dc.getDataObject());
|
||||
ContentGroupAssociation groupAssoc =
|
||||
(ContentGroupAssociation) DomainObjectFactory.
|
||||
newInstance(dc.getDataObject());
|
||||
groupAssoc.setContentItem(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -2110,5 +2181,4 @@ public class ContentItem extends VersionedACSObject implements CustomCopy {
|
|||
public List getExtraXMLGenerators() {
|
||||
return extraXMLGenerators;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,9 @@ import java.util.Iterator;
|
|||
class DomainCopier extends DomainService {
|
||||
|
||||
private static Logger s_log = Logger.getLogger(DomainCopier.class);
|
||||
|
||||
// A map of OID => DomainObject
|
||||
private final HashMap m_copied;
|
||||
private final TraversedSet m_traversed;
|
||||
protected final TraversedSet m_traversed;
|
||||
final Tracer m_trace;
|
||||
|
||||
/**
|
||||
|
|
@ -69,7 +68,7 @@ class DomainCopier extends DomainService {
|
|||
*
|
||||
* @param source the <code>DomainObject</code> from which to copy
|
||||
*/
|
||||
public DomainObject copy(final DomainObject source) {
|
||||
public DomainObject copy(final DomainObject source) {
|
||||
m_trace.enter("copy", source);
|
||||
|
||||
final OID sourceOID = source.getOID();
|
||||
|
|
@ -90,15 +89,15 @@ class DomainCopier extends DomainService {
|
|||
|
||||
if (source instanceof ACSObject) {
|
||||
final String type =
|
||||
((ACSObject) source).getSpecificObjectType();
|
||||
((ACSObject) source).getSpecificObjectType();
|
||||
|
||||
target = (DomainObject)Classes.newInstance(
|
||||
clacc,
|
||||
new Class[] { String.class },
|
||||
new Object[] { type });
|
||||
target = (DomainObject) Classes.newInstance(
|
||||
clacc,
|
||||
new Class[]{String.class},
|
||||
new Object[]{type});
|
||||
} else {
|
||||
Assert.fail("Cannot copy " + source + "; it is not an " +
|
||||
"ACSObject");
|
||||
Assert.fail("Cannot copy " + source + "; it is not an "
|
||||
+ "ACSObject");
|
||||
// XXX Contrary to this assertion, it is reasonable to
|
||||
// copy domain objects, and this code should support
|
||||
// it.
|
||||
|
|
@ -129,57 +128,57 @@ class DomainCopier extends DomainService {
|
|||
}
|
||||
|
||||
protected void copyData(final DomainObject source, DomainObject target) {
|
||||
final ObjectType type = source.getObjectType();
|
||||
final ObjectType type = source.getObjectType();
|
||||
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Using object type " + type.getName());
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("Using object type " + type.getName());
|
||||
}
|
||||
|
||||
// XXX This is what I would like to do:
|
||||
//
|
||||
//final Iterator iter = type.getProperties();
|
||||
//
|
||||
//while (iter.hasNext()) {
|
||||
// copyProperty(source, target, (Property) iter.next());
|
||||
//}
|
||||
|
||||
// But the beforeSave-on-flush behavior makes it so I have
|
||||
// to do this instead:
|
||||
|
||||
Iterator iter = type.getProperties();
|
||||
final ArrayList attributes = new ArrayList();
|
||||
final ArrayList roles = new ArrayList();
|
||||
final ArrayList collections = new ArrayList();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
final Property prop = (Property) iter.next();
|
||||
|
||||
if (prop.isAttribute()) {
|
||||
attributes.add(prop);
|
||||
} else if (prop.isCollection()) {
|
||||
collections.add(prop);
|
||||
} else {
|
||||
roles.add(prop);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX This is what I would like to do:
|
||||
//
|
||||
//final Iterator iter = type.getProperties();
|
||||
//
|
||||
//while (iter.hasNext()) {
|
||||
// copyProperty(source, target, (Property) iter.next());
|
||||
//}
|
||||
iter = attributes.iterator();
|
||||
|
||||
// But the beforeSave-on-flush behavior makes it so I have
|
||||
// to do this instead:
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
|
||||
Iterator iter = type.getProperties();
|
||||
final ArrayList attributes = new ArrayList();
|
||||
final ArrayList roles = new ArrayList();
|
||||
final ArrayList collections = new ArrayList();
|
||||
iter = roles.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
final Property prop = (Property) iter.next();
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
|
||||
if (prop.isAttribute()) {
|
||||
attributes.add(prop);
|
||||
} else if (prop.isCollection()) {
|
||||
collections.add(prop);
|
||||
} else {
|
||||
roles.add(prop);
|
||||
}
|
||||
}
|
||||
iter = collections.iterator();
|
||||
|
||||
iter = attributes.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
|
||||
iter = roles.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
|
||||
iter = collections.iterator();
|
||||
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
while (iter.hasNext()) {
|
||||
copyProperty(source, target, (Property) iter.next());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -211,8 +210,8 @@ class DomainCopier extends DomainService {
|
|||
}
|
||||
|
||||
if (prop.isKeyProperty()) {
|
||||
s_log.debug("The property is one of the key properties; " +
|
||||
"skipping it");
|
||||
s_log.debug("The property is one of the key properties; "
|
||||
+ "skipping it");
|
||||
} else {
|
||||
s_log.debug("Copying is enabled; proceeding");
|
||||
|
||||
|
|
@ -281,8 +280,8 @@ class DomainCopier extends DomainService {
|
|||
final String name = prop.getName();
|
||||
|
||||
if (m_traversed.contains(source, prop)) {
|
||||
s_log.debug("The role belongs to a link that has " +
|
||||
"already been traversed; skipping it");
|
||||
s_log.debug("The role belongs to a link that has "
|
||||
+ "already been traversed; skipping it");
|
||||
} else {
|
||||
// This marks the forward link traversed. Further down,
|
||||
// in this method and in copyCollection, we mark the
|
||||
|
|
@ -363,6 +362,7 @@ class DomainCopier extends DomainService {
|
|||
m_traversed.add(selem, reverse);
|
||||
|
||||
final DomainObject telem = copy(source, target, selem, prop);
|
||||
|
||||
DataObject tgtLink = null;
|
||||
|
||||
// removing this assert since copy will return null in the
|
||||
|
|
@ -410,8 +410,7 @@ class DomainCopier extends DomainService {
|
|||
|
||||
if (prop.isComponent()) {
|
||||
if (s_log.isDebugEnabled()) {
|
||||
s_log.debug("The property is a component; " +
|
||||
"copying by value");
|
||||
s_log.debug("The property is a component; " + "copying by value");
|
||||
}
|
||||
|
||||
final DomainObject copy = copy(object);
|
||||
|
|
@ -420,8 +419,8 @@ class DomainCopier extends DomainService {
|
|||
|
||||
return copy;
|
||||
} else {
|
||||
s_log.debug("The property is not a component; " +
|
||||
"copying by reference");
|
||||
s_log.debug("The property is not a component; "
|
||||
+ "copying by reference");
|
||||
|
||||
m_trace.exit("copy", object);
|
||||
|
||||
|
|
@ -442,10 +441,8 @@ class DomainCopier extends DomainService {
|
|||
return (DomainObject) m_copied.get(oid);
|
||||
}
|
||||
|
||||
|
||||
// Utility methods and classes
|
||||
|
||||
private DomainObject domain(final DataObject data) {
|
||||
protected DomainObject domain(final DataObject data) {
|
||||
Assert.exists(data, DataObject.class);
|
||||
|
||||
final DomainObject domain = DomainObjectFactory.newInstance(data);
|
||||
|
|
@ -455,7 +452,8 @@ class DomainCopier extends DomainService {
|
|||
return domain;
|
||||
}
|
||||
|
||||
private static class TraversedSet extends HashSet {
|
||||
protected static class TraversedSet extends HashSet {
|
||||
|
||||
void add(final DomainObject object, final Property prop) {
|
||||
Assert.exists(object, DomainObject.class);
|
||||
|
||||
|
|
@ -472,7 +470,9 @@ class DomainCopier extends DomainService {
|
|||
return contains(object.getOID() + "." + prop.getName());
|
||||
}
|
||||
}
|
||||
private final class WrapperDomainObject extends DomainObject{
|
||||
|
||||
protected final class WrapperDomainObject extends DomainObject {
|
||||
|
||||
public WrapperDomainObject(DataObject dobj) {
|
||||
super(dobj);
|
||||
}
|
||||
|
|
@ -480,6 +480,5 @@ class DomainCopier extends DomainService {
|
|||
public WrapperDomainObject(OID oid) {
|
||||
super(oid);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
package com.arsdigita.cms;
|
||||
|
||||
import com.arsdigita.domain.DomainObject;
|
||||
import com.arsdigita.persistence.DataAssociation;
|
||||
import com.arsdigita.persistence.DataAssociationCursor;
|
||||
import com.arsdigita.persistence.DataObject;
|
||||
import com.arsdigita.persistence.metadata.Property;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jens Pelzetter
|
||||
* @version $Id$
|
||||
*/
|
||||
public class LanguageAwareObjectCopier extends ObjectCopier {
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(LanguageAwareObjectCopier.class);
|
||||
private String language;
|
||||
|
||||
public LanguageAwareObjectCopier(String language) {
|
||||
super();
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void copyCollection(final DomainObject source,
|
||||
final DomainObject target,
|
||||
final Property prop) {
|
||||
if (source instanceof ContentPage) {
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Copying collection " + prop);
|
||||
}
|
||||
|
||||
final String name = prop.getName();
|
||||
|
||||
final DataAssociation sass = (DataAssociation) get(source, name);
|
||||
final DataAssociationCursor scursor = sass.cursor();
|
||||
final Property reverse = prop.getAssociatedProperty();
|
||||
|
||||
while (scursor.next()) {
|
||||
final DomainObject selem = domain(scursor.getDataObject());
|
||||
|
||||
m_traversed.add(selem, reverse);
|
||||
|
||||
DomainObject telem = copy(source, target, selem, prop);
|
||||
if (telem instanceof ContentPage) {
|
||||
ContentPage tpage = (ContentPage) telem;
|
||||
telem = tpage.getContentBundle().getInstance(language);
|
||||
}
|
||||
|
||||
DataObject tgtLink = null;
|
||||
|
||||
// removing this assert since copy will return null in the
|
||||
// case of deferred association creation in VersionCopier
|
||||
//Assert.exists(telem, DomainObject.class);
|
||||
|
||||
if (telem != null) {
|
||||
tgtLink = add(target, name, telem);
|
||||
}
|
||||
if (tgtLink != null) {
|
||||
// Copy link attributes as well
|
||||
copyData(new WrapperDomainObject(scursor.getLink()),
|
||||
new WrapperDomainObject(tgtLink));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
//Use old behaviour
|
||||
super.copyCollection(source, target, prop);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -156,7 +156,7 @@ public class ItemLanguages extends LayoutPanel {
|
|||
|
||||
ContentType type = item.getContentType();
|
||||
|
||||
item = (ContentPage) item.copy();
|
||||
item = (ContentPage) item.copy(lang);
|
||||
item.setLanguage(lang);
|
||||
item.setName(name);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,9 +77,31 @@ public class SciDepartmentOrganizationForm
|
|||
getSelectedObject(state);
|
||||
|
||||
if (this.getSaveCancelSection().getSaveButton().isSelected(state)) {
|
||||
department.setOrganization((SciOrganization) data.get(ITEM_SEARCH));
|
||||
SciOrganization orga = (SciOrganization) data.get(ITEM_SEARCH);
|
||||
|
||||
init(fse);
|
||||
orga = (SciOrganization) orga.getContentBundle().getInstance(department.getLanguage());
|
||||
|
||||
department.setOrganization(orga);
|
||||
//department.setOrganization((SciOrganization) data.get(ITEM_SEARCH));
|
||||
}
|
||||
|
||||
init(fse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(FormSectionEvent fse) throws FormProcessException {
|
||||
final PageState state = fse.getPageState();
|
||||
final FormData data = fse.getFormData();
|
||||
|
||||
SciDepartment department = (SciDepartment) getItemSelectionModel().
|
||||
getSelectedObject(state);
|
||||
|
||||
SciOrganization orga = (SciOrganization) data.get(ITEM_SEARCH);
|
||||
|
||||
if (!(orga.getContentBundle().hasInstance(department.getLanguage()))) {
|
||||
data.addError(
|
||||
SciOrganizationGlobalizationUtil.globalize(
|
||||
"sciorganization.ui.department.organization.add.no_suitable_language_variant"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ package com.arsdigita.cms.contenttypes.ui;
|
|||
import com.arsdigita.bebop.Component;
|
||||
import com.arsdigita.bebop.ControlLink;
|
||||
import com.arsdigita.bebop.Label;
|
||||
import com.arsdigita.bebop.Link;
|
||||
import com.arsdigita.bebop.PageState;
|
||||
import com.arsdigita.bebop.Table;
|
||||
import com.arsdigita.bebop.event.TableActionEvent;
|
||||
|
|
@ -31,12 +32,19 @@ import com.arsdigita.bebop.table.TableColumn;
|
|||
import com.arsdigita.bebop.table.TableColumnModel;
|
||||
import com.arsdigita.bebop.table.TableModel;
|
||||
import com.arsdigita.bebop.table.TableModelBuilder;
|
||||
import com.arsdigita.cms.CMS;
|
||||
import com.arsdigita.cms.ContentSection;
|
||||
import com.arsdigita.cms.ItemSelectionModel;
|
||||
import com.arsdigita.cms.SecurityManager;
|
||||
import com.arsdigita.cms.contenttypes.SciDepartment;
|
||||
import com.arsdigita.cms.contenttypes.SciOrganization;
|
||||
import com.arsdigita.cms.dispatcher.ItemResolver;
|
||||
import com.arsdigita.cms.dispatcher.Utilities;
|
||||
import com.arsdigita.dispatcher.ObjectNotFoundException;
|
||||
import com.arsdigita.domain.DomainObjectFactory;
|
||||
import com.arsdigita.util.LockableImpl;
|
||||
import java.math.BigDecimal;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Sheet for showing the superior organization of a SciDepartment.
|
||||
|
|
@ -49,6 +57,9 @@ public class SciDepartmentOrganizationSheet
|
|||
extends Table
|
||||
implements TableActionListener {
|
||||
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(
|
||||
SciDepartmentOrganizationSheet.class);
|
||||
private final String TABLE_COL_EDIT = "table_col_edit";
|
||||
private final String TABLE_COL_DEL = "table_col_del";
|
||||
private ItemSelectionModel m_itemModel;
|
||||
|
|
@ -169,8 +180,57 @@ public class SciDepartmentOrganizationSheet
|
|||
Object key,
|
||||
int row,
|
||||
int column) {
|
||||
Label label = new Label(value.toString());
|
||||
return label;
|
||||
SecurityManager securityManager =
|
||||
Utilities.getSecurityManager(state);
|
||||
SciDepartment department = (SciDepartment) m_itemModel.
|
||||
getSelectedObject(state);
|
||||
|
||||
boolean canEdit = securityManager.canAccess(
|
||||
state.getRequest(),
|
||||
SecurityManager.EDIT_ITEM,
|
||||
department);
|
||||
|
||||
if (canEdit) {
|
||||
SciOrganization organization;
|
||||
try {
|
||||
organization = new SciOrganization((BigDecimal) key);
|
||||
} catch (ObjectNotFoundException ex) {
|
||||
logger.warn(String.format("No object with key '%s' found.",
|
||||
key),
|
||||
ex);
|
||||
return new Label(value.toString());
|
||||
}
|
||||
|
||||
ContentSection section = CMS.getContext().getContentSection();
|
||||
ItemResolver resolver = section.getItemResolver();
|
||||
Link link = new Link(String.format("%s (%s)",
|
||||
value.toString(),
|
||||
organization.getLanguage()),
|
||||
resolver.generateItemURL(state,
|
||||
organization,
|
||||
section,
|
||||
organization.
|
||||
getVersion()));
|
||||
|
||||
return link;
|
||||
} else {
|
||||
SciOrganization organization;
|
||||
try {
|
||||
organization = new SciOrganization((BigDecimal) key);
|
||||
} catch (ObjectNotFoundException ex) {
|
||||
logger.warn(String.format("No object with key '%s' found.",
|
||||
key),
|
||||
ex);
|
||||
return new Label(value.toString());
|
||||
}
|
||||
|
||||
Label label = new Label(
|
||||
String.format("%s (%s)",
|
||||
value.toString(),
|
||||
organization.getLanguage()));
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,7 +276,8 @@ public class SciDepartmentOrganizationSheet
|
|||
public void cellSelected(TableActionEvent event) {
|
||||
PageState state = event.getPageState();
|
||||
|
||||
SciDepartment department = (SciDepartment) m_itemModel.getSelectedObject(
|
||||
SciDepartment department =
|
||||
(SciDepartment) m_itemModel.getSelectedObject(
|
||||
state);
|
||||
|
||||
TableColumn column = getColumnModel().get(event.getColumn().intValue());
|
||||
|
|
|
|||
|
|
@ -78,10 +78,33 @@ public class SciOrganizationDepartmentAddForm
|
|||
getSelectedObject(state);
|
||||
|
||||
if (!(this.getSaveCancelSection().getCancelButton().
|
||||
isSelected(state))) {
|
||||
orga.addDepartment((SciDepartment) data.get(ITEM_SEARCH));
|
||||
isSelected(state))) {
|
||||
SciDepartment department = (SciDepartment) data.get(ITEM_SEARCH);
|
||||
|
||||
department = (SciDepartment) department.getContentBundle().
|
||||
getInstance(orga.getLanguage());
|
||||
|
||||
orga.addDepartment(department);
|
||||
//orga.addDepartment((SciDepartment) data.get(ITEM_SEARCH));
|
||||
}
|
||||
|
||||
init(fse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(FormSectionEvent fse) throws FormProcessException {
|
||||
final PageState state = fse.getPageState();
|
||||
final FormData data = fse.getFormData();
|
||||
|
||||
SciOrganization orga = (SciOrganization) getItemSelectionModel().
|
||||
getSelectedObject(state);
|
||||
|
||||
SciDepartment department = (SciDepartment) data.get(ITEM_SEARCH);
|
||||
|
||||
if (!(department.getContentBundle().hasInstance(orga.getLanguage()))) {
|
||||
data.addError(
|
||||
SciOrganizationGlobalizationUtil.globalize(
|
||||
"sciorganization.ui.organization.department.add.no_suitable_language_variant"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,12 +221,13 @@ public class SciOrganizationDepartmentsTable
|
|||
key),
|
||||
ex);
|
||||
return new Label(value.toString());
|
||||
|
||||
}
|
||||
|
||||
ContentSection section = CMS.getContext().getContentSection();
|
||||
ItemResolver resolver = section.getItemResolver();
|
||||
Link link = new Link(value.toString(),
|
||||
Link link = new Link(String.format("%s (%s)",
|
||||
value.toString(),
|
||||
department.getLanguage()),
|
||||
resolver.generateItemURL(state,
|
||||
department,
|
||||
section,
|
||||
|
|
@ -236,7 +237,19 @@ public class SciOrganizationDepartmentsTable
|
|||
return link;
|
||||
|
||||
} else {
|
||||
Label label = new Label(value.toString());
|
||||
SciDepartment department;
|
||||
try {
|
||||
department = new SciDepartment((BigDecimal) key);
|
||||
} catch (ObjectNotFoundException ex) {
|
||||
s_log.warn(String.format("No object with key '%s' found.",
|
||||
key),
|
||||
ex);
|
||||
return new Label(value.toString());
|
||||
}
|
||||
|
||||
Label label = new Label(String.format("%s (%s)",
|
||||
value.toString(),
|
||||
department.getLanguage()));
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,3 +178,5 @@ scimember.ui.project.confirm_remove=Do you really want to remove this associatio
|
|||
scimember.ui.project.select_project=Project
|
||||
scimember.ui.projects.edit_assoc=Edit association
|
||||
sciorganization.ui.project.description=Description of project
|
||||
sciorganization.ui.organization.department.add.no_suitable_language_variant=The selected item has no language variant for the language of the current item. Please add an suitable language variant to the item to add before adding it.
|
||||
sciorganization.ui.department.organization.add.no_suitable_language_variant=The selected item has no language variant for the language of the current item. Please add an suitable language variant to the item to add before adding it.
|
||||
|
|
|
|||
|
|
@ -178,3 +178,5 @@ scimember.ui.project.confirm_remove=Wollen Sie diese Verkn\u00fcpfung wirklich e
|
|||
scimember.ui.project.select_project=Projekt
|
||||
scimember.ui.projects.edit_assoc=Verkn\u00fcpfung bearbeiten
|
||||
sciorganization.ui.project.description=Projektbeschreibung
|
||||
sciorganization.ui.organization.department.add.no_suitable_language_variant=Das ausgew\u00e4hlte ContentItem besitzt keine Sprachvariante f\u00fcr die Sprache dieses Items und kann daher nicht hinzugef\u00fcgt werden.
|
||||
sciorganization.ui.department.organization.add.no_suitable_language_variant=Das ausgew\u00e4hlte ContentItem besitzt keine Sprachvariante f\u00fcr die Sprache dieses Items und kann daher nicht hinzugef\u00fcgt werden.
|
||||
|
|
|
|||
Loading…
Reference in New Issue