/**
* Copyright (C) 2010-2017 Structr GmbH
*
* This file is part of Structr <http://structr.org>.
*
* Structr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* Structr 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Structr. If not, see <http://www.gnu.org/licenses/>.
*/
package org.structr.web.entity;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.structr.api.config.Settings;
import org.structr.common.PropertyView;
import org.structr.common.SecurityContext;
import org.structr.common.ValidationHelper;
import org.structr.common.View;
import org.structr.common.error.ErrorBuffer;
import org.structr.common.error.FrameworkException;
import org.structr.common.error.UniqueToken;
import org.structr.core.app.StructrApp;
import org.structr.core.entity.LinkedTreeNode;
import org.structr.core.graph.ModificationQueue;
import static org.structr.core.graph.NodeInterface.name;
import org.structr.core.property.BooleanProperty;
import org.structr.core.property.CollectionIdProperty;
import org.structr.core.property.EndNode;
import org.structr.core.property.EndNodes;
import org.structr.core.property.EntityIdProperty;
import org.structr.core.property.Property;
import org.structr.core.property.StartNode;
import org.structr.web.common.FileHelper;
import org.structr.web.entity.relation.FileChildren;
import org.structr.web.entity.relation.FileSiblings;
import org.structr.web.entity.relation.FolderChildren;
import org.structr.web.property.PathProperty;
/**
* Base class for filesystem objects in structr.
*
*
*/
public class AbstractFile extends LinkedTreeNode<FileChildren, FileSiblings, AbstractFile> {
private static final Logger logger = LoggerFactory.getLogger(AbstractFile.class.getName());
public static final Property<Folder> parent = new StartNode<>("parent", FolderChildren.class);
public static final Property<List<AbstractFile>> children = new EndNodes<>("children", FileChildren.class);
public static final Property<AbstractFile> previousSibling = new StartNode<>("previousSibling", FileSiblings.class);
public static final Property<AbstractFile> nextSibling = new EndNode<>("nextSibling", FileSiblings.class);
public static final Property<List<String>> childrenIds = new CollectionIdProperty("childrenIds", children);
public static final Property<String> nextSiblingId = new EntityIdProperty("nextSiblingId", nextSibling);
public static final Property<String> path = new PathProperty("path").indexed().readOnly();
public static final Property<String> parentId = new EntityIdProperty("parentId", parent);
public static final Property<Boolean> hasParent = new BooleanProperty("hasParent").indexed();
public static final Property<Boolean> includeInFrontendExport = new BooleanProperty("includeInFrontendExport").cmis().indexed();
public static final View defaultView = new View(AbstractFile.class, PropertyView.Public, path);
public static final View uiView = new View(AbstractFile.class, PropertyView.Ui, path);
@Override
public boolean onCreation(final SecurityContext securityContext, final ErrorBuffer errorBuffer) throws FrameworkException {
boolean valid = true;
if (Settings.UniquePaths.getValue()) {
valid = validateAndRenameFileOnce(securityContext, errorBuffer);
}
return valid && super.onCreation(securityContext, errorBuffer);
}
@Override
public boolean onModification(final SecurityContext securityContext, final ErrorBuffer errorBuffer, final ModificationQueue modificationQueue) throws FrameworkException {
boolean valid = true;
if (Settings.UniquePaths.getValue()) {
valid = validatePath(securityContext, errorBuffer);
}
return valid && super.onModification(securityContext, errorBuffer, modificationQueue);
}
public boolean validatePath(final SecurityContext securityContext, final ErrorBuffer errorBuffer) throws FrameworkException {
final String filePath = getProperty(path);
if (filePath != null) {
final List<AbstractFile> files = StructrApp.getInstance().nodeQuery(AbstractFile.class).and(path, filePath).getAsList();
for (final AbstractFile file : files) {
if (!file.getUuid().equals(getUuid())) {
if (errorBuffer != null) {
final UniqueToken token = new UniqueToken(AbstractFile.class.getSimpleName(), path, file.getUuid());
token.setValue(filePath);
errorBuffer.add(token);
}
return false;
}
}
}
return true;
}
public boolean validateAndRenameFileOnce(final SecurityContext securityContext, final ErrorBuffer errorBuffer) throws FrameworkException {
boolean valid = validatePath(securityContext, null);
if (!valid) {
final String originalPath = getProperty(AbstractFile.path);
final String newName = getProperty(AbstractFile.name).concat("_").concat(FileHelper.getDateString());
setProperty(AbstractFile.name, newName);
valid = validatePath(securityContext, errorBuffer);
if (valid) {
logger.warn("File {} already exists, renaming to {}", new Object[] { originalPath, newName });
} else {
logger.warn("File {} already existed. Tried renaming to {} and failed. Aborting.", new Object[] { originalPath, newName });
}
}
return valid;
}
@Override
public boolean isValid(ErrorBuffer errorBuffer) {
boolean valid = super.isValid(errorBuffer);
valid &= nonEmpty(AbstractFile.name, errorBuffer);
valid &= ValidationHelper.isValidStringMatchingRegex(this, name, "[^\\/\\x00]+", errorBuffer);
return valid;
}
@Override
public Class<FileChildren> getChildLinkType() {
return FileChildren.class;
}
@Override
public Class<FileSiblings> getSiblingLinkType() {
return FileSiblings.class;
}
}