mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
MNT-17703: Allow Bulk Import versioning without appending a version number to docs
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@136971 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,36 +1,45 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.bulkimport;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
public class BulkImportParameters
|
||||
{
|
||||
// MNT-17703: Provide configurable behaviour for when the target file already exists in the repository.
|
||||
public enum ExistingFileMode
|
||||
{
|
||||
// If the file already exists...
|
||||
SKIP, // skip the import from the source.
|
||||
REPLACE, // replace the file, loosing any previous version history.
|
||||
ADD_VERSION // create a new version of the file during import, preserving previous history.
|
||||
};
|
||||
|
||||
private ExistingFileMode existingFileMode = ExistingFileMode.SKIP;
|
||||
private NodeRef target;
|
||||
private boolean replaceExisting = false;
|
||||
private Integer batchSize;
|
||||
private Integer numThreads;
|
||||
private Integer loggingInterval;
|
||||
@@ -60,14 +69,6 @@ public class BulkImportParameters
|
||||
{
|
||||
this.target = target;
|
||||
}
|
||||
public boolean isReplaceExisting()
|
||||
{
|
||||
return replaceExisting;
|
||||
}
|
||||
public void setReplaceExisting(boolean replaceExisting)
|
||||
{
|
||||
this.replaceExisting = replaceExisting;
|
||||
}
|
||||
public Integer getBatchSize()
|
||||
{
|
||||
return batchSize;
|
||||
@@ -84,5 +85,40 @@ public class BulkImportParameters
|
||||
{
|
||||
this.numThreads = numThreads;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getExistingFileMode} (MNT-17703)
|
||||
* @return
|
||||
*/
|
||||
public boolean isReplaceExisting()
|
||||
{
|
||||
return existingFileMode == ExistingFileMode.REPLACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setExistingFileMode} (MNT-17703)
|
||||
* @param replaceExisting
|
||||
*/
|
||||
@Deprecated()
|
||||
public void setReplaceExisting(boolean replaceExisting)
|
||||
{
|
||||
if (replaceExisting)
|
||||
{
|
||||
setExistingFileMode(ExistingFileMode.REPLACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
setExistingFileMode(ExistingFileMode.SKIP);
|
||||
}
|
||||
}
|
||||
|
||||
public ExistingFileMode getExistingFileMode()
|
||||
{
|
||||
return existingFileMode;
|
||||
}
|
||||
|
||||
public void setExistingFileMode(ExistingFileMode existingFileMode)
|
||||
{
|
||||
this.existingFileMode = existingFileMode;
|
||||
}
|
||||
}
|
||||
|
@@ -120,6 +120,10 @@ public final class ImportableItem
|
||||
|
||||
public Set<VersionedContentAndMetadata> getVersionEntries()
|
||||
{
|
||||
if (versionEntries == null)
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return(Collections.unmodifiableSet(versionEntries));
|
||||
}
|
||||
|
||||
|
@@ -23,20 +23,20 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.bulkimport;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* Imports an importable item in the filesystem into the repository by creating a node to represent it.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
public interface NodeImporter
|
||||
{
|
||||
public NodeRef importImportableItem(ImportableItem importableItem, boolean replaceExisting);
|
||||
public File getSourceFolder();
|
||||
}
|
||||
package org.alfresco.repo.bulkimport;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* Imports an importable item in the filesystem into the repository by creating a node to represent it.
|
||||
*
|
||||
* @since 4.0
|
||||
*
|
||||
*/
|
||||
public interface NodeImporter
|
||||
{
|
||||
public NodeRef importImportableItem(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode);
|
||||
public File getSourceFolder();
|
||||
}
|
||||
|
@@ -1,20 +1,20 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
@@ -33,6 +33,7 @@ import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.bulkimport.BulkFilesystemImporter;
|
||||
import org.alfresco.repo.bulkimport.BulkImportParameters;
|
||||
import org.alfresco.repo.bulkimport.DirectoryAnalyser;
|
||||
import org.alfresco.repo.bulkimport.ImportableItem;
|
||||
import org.alfresco.repo.bulkimport.MetadataLoader;
|
||||
@@ -99,7 +100,7 @@ public abstract class AbstractNodeImporter implements NodeImporter
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
protected abstract NodeRef importImportableItemImpl(ImportableItem importableItem, boolean replaceExisting);
|
||||
protected abstract NodeRef importImportableItemImpl(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode);
|
||||
protected abstract void importContentAndMetadata(NodeRef nodeRef, ImportableItem.ContentAndMetadata contentAndMetadata, MetadataLoader.Metadata metadata);
|
||||
|
||||
/*
|
||||
@@ -181,11 +182,19 @@ public abstract class AbstractNodeImporter implements NodeImporter
|
||||
return(result);
|
||||
}
|
||||
|
||||
protected final int importImportableItemFile(NodeRef nodeRef, ImportableItem importableItem, MetadataLoader.Metadata metadata, NodeState nodeState)
|
||||
protected final int importImportableItemFile(NodeRef nodeRef, ImportableItem importableItem, MetadataLoader.Metadata metadata, NodeState nodeState, BulkImportParameters.ExistingFileMode existingFileMode)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (importableItem.hasVersionEntries())
|
||||
if (nodeState == NodeState.REPLACED && existingFileMode == BulkImportParameters.ExistingFileMode.ADD_VERSION)
|
||||
{
|
||||
// It is being replaced, and ADD_VERSION is the selected method of dealing with overwrites.
|
||||
Map<QName, Serializable> versionProperties = new HashMap<>();
|
||||
versionProperties.put(ContentModel.PROP_VERSION_TYPE, VersionType.MAJOR);
|
||||
versionService.ensureVersioningEnabled(nodeRef, versionProperties);
|
||||
result = importContentVersions(nodeRef, importableItem, nodeState);
|
||||
}
|
||||
else if (importableItem.hasVersionEntries())
|
||||
{
|
||||
result = importContentVersions(nodeRef, importableItem, nodeState);
|
||||
}
|
||||
@@ -242,9 +251,18 @@ public abstract class AbstractNodeImporter implements NodeImporter
|
||||
return(result);
|
||||
}
|
||||
|
||||
protected final Triple<NodeRef, Boolean, NodeState> createOrFindNode(NodeRef target, ImportableItem importableItem,
|
||||
boolean replaceExisting, MetadataLoader.Metadata metadata)
|
||||
// TODO: this is a confusing method that does too many things. It doesn't just "create or find"
|
||||
// but also decides whether an existing node (i.e. the 'find' in 'create or find') WILL BE
|
||||
// skipped or replaced further on in the calling code.
|
||||
// TODO: refactor?
|
||||
protected final Triple<NodeRef, Boolean, NodeState> createOrFindNode(
|
||||
NodeRef target, ImportableItem importableItem,
|
||||
BulkImportParameters.ExistingFileMode existingFileMode, MetadataLoader.Metadata metadata)
|
||||
{
|
||||
// ADD_VERSION isn't strictly a replacement option, but we need to deal with it as such, at least as an
|
||||
// interim measure while the new ExistingFileMode options are introduced (MNT-17703)
|
||||
boolean replaceExisting = (existingFileMode == BulkImportParameters.ExistingFileMode.REPLACE ||
|
||||
existingFileMode == BulkImportParameters.ExistingFileMode.ADD_VERSION);
|
||||
Triple<NodeRef, Boolean, NodeState> result = null;
|
||||
boolean isDirectory = false;
|
||||
NodeState nodeState = replaceExisting ? NodeState.REPLACED : NodeState.SKIPPED;
|
||||
@@ -424,14 +442,14 @@ public abstract class AbstractNodeImporter implements NodeImporter
|
||||
return(result);
|
||||
}
|
||||
|
||||
public NodeRef importImportableItem(ImportableItem importableItem, boolean replaceExisting)
|
||||
public NodeRef importImportableItem(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode)
|
||||
{
|
||||
if(logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Importing " + String.valueOf(importableItem));
|
||||
}
|
||||
|
||||
NodeRef nodeRef = importImportableItemImpl(importableItem, replaceExisting);
|
||||
NodeRef nodeRef = importImportableItemImpl(importableItem, existingFileMode);
|
||||
|
||||
// allow parent to be garbage collected
|
||||
//importableItem.setParent(null);
|
||||
|
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.bulkimport.impl;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
@@ -1,28 +1,28 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.bulkimport.impl;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
@@ -126,7 +126,7 @@ public abstract class MultiThreadedBulkFilesystemImporter extends AbstractBulkFi
|
||||
{
|
||||
behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
|
||||
|
||||
NodeRef nodeRef = nodeImporter.importImportableItem(importableItem, bulkImportParameters.isReplaceExisting());
|
||||
NodeRef nodeRef = nodeImporter.importImportableItem(importableItem, bulkImportParameters.getExistingFileMode());
|
||||
filesystemTracker.itemImported(nodeRef, importableItem);
|
||||
}
|
||||
finally
|
||||
|
@@ -1,20 +1,20 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
@@ -30,6 +30,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.bulkimport.BulkImportParameters;
|
||||
import org.alfresco.repo.bulkimport.ImportableItem;
|
||||
import org.alfresco.repo.bulkimport.MetadataLoader;
|
||||
import org.alfresco.repo.bulkimport.NodeImporter;
|
||||
@@ -109,7 +110,7 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory
|
||||
importImportableItemMetadata(nodeRef, contentAndMetadata.getContentFile(), metadata);
|
||||
}
|
||||
|
||||
protected NodeRef importImportableItemImpl(ImportableItem importableItem, boolean replaceExisting)
|
||||
protected NodeRef importImportableItemImpl(ImportableItem importableItem, BulkImportParameters.ExistingFileMode existingFileMode)
|
||||
{
|
||||
NodeRef target = importableItem.getParent().getNodeRef();
|
||||
if(target == null)
|
||||
@@ -120,7 +121,16 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory
|
||||
NodeRef result = null;
|
||||
MetadataLoader.Metadata metadata = loadMetadata(importableItem.getHeadRevision());
|
||||
|
||||
Triple<NodeRef, Boolean, NodeState> node = createOrFindNode(target, importableItem, replaceExisting, metadata);
|
||||
// TODO: we'll get NodeState.REPLACED back from this method (i.e. the node WILL be replaced)
|
||||
// even if we're using ExistingFileMode.ADD_VERSION - we need to do this currently, otherwise
|
||||
// the file would be SKIPPED and various other checks that are only computed if replace is being used,
|
||||
// wouldn't happen.
|
||||
// TODO: sort this out.
|
||||
Triple<NodeRef, Boolean, NodeState> node = createOrFindNode(
|
||||
target,
|
||||
importableItem,
|
||||
existingFileMode,
|
||||
metadata);
|
||||
boolean isDirectory = node.getSecond() == null ? false : node.getSecond(); // Watch out for NPEs during unboxing!
|
||||
NodeState nodeState = node.getThird();
|
||||
|
||||
@@ -139,7 +149,7 @@ public class StreamingNodeImporterFactory extends AbstractNodeImporterFactory
|
||||
}
|
||||
else
|
||||
{
|
||||
numVersionProperties = importImportableItemFile(result, importableItem, metadata, nodeState);
|
||||
numVersionProperties = importImportableItemFile(result, importableItem, metadata, nodeState, existingFileMode);
|
||||
}
|
||||
|
||||
importStatus.incrementNodesWritten(importableItem, isDirectory, nodeState, metadata.getProperties().size() + 4, numVersionProperties);
|
||||
|
Reference in New Issue
Block a user