Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.dotmarketing.portlets.folders.exception.InvalidFolderNameException;
import com.dotmarketing.portlets.folders.model.Folder;
import com.dotmarketing.portlets.folders.struts.FolderForm;
import com.dotmarketing.portlets.htmlpageasset.business.HTMLPageAssetAPI;
import com.dotmarketing.portlets.links.model.Link;
import com.dotmarketing.portlets.structure.model.Structure;
import com.dotmarketing.util.Config;
Expand Down Expand Up @@ -62,9 +63,12 @@ public class EditFolderAction extends DotPortletAction {

private FolderAPI folderAPI = APILocator.getFolderAPI();
private HostAPI hostAPI = APILocator.getHostAPI();
private HTMLPageAssetAPI htmlPageAssetAPI = APILocator.getHTMLPageAssetAPI();

private static final int MAX_FOLDER_PATH_LENGTH = 255;
private static final int MAX_FOLDER_NAME_LENGTH = 255;
private static final String DELETE_NOT_EMPTY_FOLDER_PROPERTY = "no.delete.notempty.folder";
private static final String DELETE_NOT_EMPTY_FOLDER_MESSAGE_KEY = "message.folder.delete.not.empty";

/**
*
Expand Down Expand Up @@ -438,26 +442,103 @@ public void _deleteFolder(ActionRequest req, ActionResponse res,
// gets the session object for the messages
HttpSession session = httpReq.getSession();

String selectedFolder = ((String) session
.getAttribute(com.dotmarketing.util.WebKeys.FOLDER_SELECTED) != null) ? (String) session
.getAttribute(com.dotmarketing.util.WebKeys.FOLDER_SELECTED)
: "";




session.removeAttribute(com.dotmarketing.util.WebKeys.FOLDER_SELECTED);

if (isDeleteNotEmptyFolderProtectionEnabled() && isNotEmpty(f)) {
SessionMessages.add(req, "message", DELETE_NOT_EMPTY_FOLDER_MESSAGE_KEY);
return;
}

session.removeAttribute(com.dotmarketing.util.WebKeys.FOLDER_SELECTED);
User user = _getUser(req);
folderAPI.delete(f, user,false);


// For messages to be displayed on messages page
SessionMessages.add(req, "message", "message.folder.delete");

}

/**
* Determines whether the protection that blocks the deletion of non-empty
* folders is enabled through configuration.
*
* @return {@code true} if the protection is enabled, otherwise {@code false}.
*/
private boolean isDeleteNotEmptyFolderProtectionEnabled() {
return Config.getBooleanProperty(DELETE_NOT_EMPTY_FOLDER_PROPERTY, false);
Comment thread
riccardoruocco marked this conversation as resolved.
Outdated
}

/**
* Recursively checks whether the specified folder, or one of its descendants,
* contains content that makes the branch non-empty.
*
* @param folder
* the folder to inspect.
* @return {@code true} if at least one relevant asset is found.
* @throws DotDataException
* if a data access error occurs.
* @throws DotStateException
* if a folder is in an invalid state.
* @throws DotSecurityException
* if access to the APIs is not authorized.
*/
private boolean isNotEmpty(final Folder folder)
throws DotDataException, DotStateException, DotSecurityException {
return isNotEmpty(folder, APILocator.getUserAPI().getSystemUser());
}

/**
* Performs the recursive check while reusing the same system user for all
* queries executed against the folder tree.
*
* @param folder
* the current folder to inspect.
* @param user
* the user used to execute the internal checks.
* @return {@code true} as soon as content is found in the current branch.
* @throws DotDataException
* if a data access error occurs.
* @throws DotStateException
* if a folder is in an invalid state.
* @throws DotSecurityException
* if access to the APIs is not authorized.
*/
private boolean isNotEmpty(final Folder folder, final User user)
throws DotDataException, DotStateException, DotSecurityException {
if (containsRelevantAssets(folder, user)) {
return true;
}

for (final Folder childFolder : folderAPI.findSubFolders(folder, user, false)) {
if (isNotEmpty(childFolder, user)) {
return true;
}
}

return false;
}

/**
* Determines whether the current folder directly contains at least one
* contentlet, link, or HTML page.
*
* @param folder
* the folder to inspect.
* @param user
* the user used to query the internal APIs.
* @return {@code true} if the folder contains direct relevant assets.
* @throws DotDataException
* if a data access error occurs.
* @throws DotSecurityException
* if access to the APIs is not authorized.
*/
private boolean containsRelevantAssets(final Folder folder, final User user)
throws DotDataException, DotSecurityException {
return !folderAPI.getContent(folder, user, false).isEmpty()
|| !folderAPI.getLinks(folder, user, false).isEmpty()
|| !htmlPageAssetAPI.getHTMLPages(folder, false, false, user, false).isEmpty()
|| !htmlPageAssetAPI.getHTMLPages(folder, true, false, user, false).isEmpty();
}

/**
*
* @param folder
Expand Down
4 changes: 4 additions & 0 deletions dotCMS/src/main/resources/dotmarketing-config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ DEFAULT_FILE_TO_DEFAULT_LANGUAGE = true
## it is similar to DEFAULT_CONTENT_TO_DEFAULT_LANGUAGE but only applies to Persona
DEFAULT_PERSONA_TO_DEFAULT_LANGUAGE = true

## Prevents folder deletion when the folder or one of its descendants is not empty.
## Environment variable: DOT_NO_DELETE_NOTEMPTY_FOLDER
no.delete.notempty.folder=false

PER_PAGE = 40

## in minutes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2550,6 +2550,7 @@ message.folder.admin.doesnotallow=The Site cannot have folder with the name &quo
message.folder.alreadyexists=The folder already exists; please choose a different folder name.
message.folder.defaultfiletype.required=A default File Asset type is required
message.folder.delete=Folder deleted
message.folder.delete.not.empty=This folder cannot be deleted because it is not empty.
message.folder.hostname.required=A Site name is required.
message.folder.ischildfolder=The destination folder is a sub-folder of the source folder; please select another destination folder.
message.folder.ishostfolder=The destination folder is a Site; please select another destination folder.
Expand Down
Loading