cancel
Showing results for 
Search instead for 
Did you mean: 

Edit/remove permissions from out-of-the-box actions

onurg
Champ on-the-rise
Champ on-the-rise

Hi all!

I'm trying to add new user roles and one of the roles should be able to move other users' files but not delete them. My new user role includes permission groups "Collaborator" and "AddChildren":

<permissionGroup name='MySiteRole' allowFullControl='false' expose='true'>
<includePermissionGroup permissionGroup='Collaborator' type='cm:cmobject' />
<includePermissionGroup permissionGroup='AddChildren' type='sys:base' />
</permissionGroup>

The code segment above is from "customSitePermissionDefinitions.xml" which I register via beans in "bootstrap-context.xml". All of my new roles are visible in the "share" and they can be assigned to users for sites without any problems.

As far as I know, I need to use share-config-custom.xml in order to make changes to the actions. Per default, the "move" action depends on the "delete" action as you can see here.  I want to edit/remove the "permissions" part from the "move" action. This is the code I added to share-config-custom.xml to change that:

<config evaluator="string-compare" condition="DocLibActions">
    <actions>
      <action id="document-move-to" type="javascript" label="actions.document.move-to">
        <param name="function">onActionMoveTo</param>
        <permissions>
          <permission allow="true">AddChildren</permission>
        </permissions>
        <evaluator>evaluator.doclib.action.editableByCurrentUser</evaluator>
      </action>
    </actions>
  </config>

In this case, I want to make the "move" action dependent on "AddChildren", however, I still can't move the files of other users with my new user role. Am I missing something or do I need another approach to achieve my goal?

Thanks in advance.

Onur

2 ACCEPTED ANSWERS

abhinavmishra14
World-Class Innovator
World-Class Innovator

At the core move operation is combination of delete and create operation, hence move needs 'Delete' permission. You can't remove it.

Simplest approach would be that, you should develop your custom role e.g. CustomCollaborator by extending the permissions of Collaborator which has at least Write permissions.

Update the share config to use: <permission allow="true">Write</permission>, so that action gets available to the custom role even if the user doesn't have delete permission. 

<action id="document-move-to" type="javascript" label="actions.document.move-to">
	<param name="function">onActionMoveTo</param>
	<permissions>
		<permission allow="true">Write</permission>
	</permissions>
	<evaluator>evaluator.doclib.action.editableByCurrentUser</evaluator>
</action>

Write a custom rootscoped object (basically a service available as root scoped object so that it can be used in move-to.post.json.js webscript) that will have logic to exeucute move operation as system user. 

AuthenticationUtil.setRunAsUserSystem();
try {
	FileInfo fileInfo = null;
	if (null == sourceParentNodeRef) {
		fileInfo = fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);

	} else {
		fileInfo = fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
	}

	....
	.....
	........
} catch (Exception excp) {
	throw new ScriptException("Can't move selected node", excp);
} 

And extend 'org\alfresco\slingshot\documentlibrary\action\move-to.post.json.js' repository webscript where you need to add your custom logic to check the role and if role matches your custom role use custom rootscoped object to call the move.

if(null != role && (role == "CustomSiteCollaborator" || role == "CustomCollaborator")) {
	var parentNodeRef = null;
	if (parent != null) {
		parentNodeRef = parent.nodeRef;
	}
	// Move the node via custom move rootscoped object
	result.success = customMove.move(fileNode.nodeRef, parentNodeRef, destNode.nodeRef);
} else {
	// Move the node via OOTB move to service
	result.success = fileNode.move(parent, destNode);
}
~Abhinav
(ACSCE, AWS SAA, Azure Admin)

View answer in original post

abhinavmishra14
World-Class Innovator
World-Class Innovator

@onurg wrote:

image

And it gets stuck here. The dialog window or the text "Files being moved..." doesn't disappear. After refreshing the page I see that the file is moved successfully. Am I still missing any references during my bean registration? Or is something missing in my java class?

CustomMoveObject.java:

import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.scripts.ScriptException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;

public class CustomMoveObject extends BaseProcessorExtension {
    private FileFolderService fileFolderService;

    public void move(NodeRef sourceNodeRef, NodeRef sourceParentNodeRef, NodeRef targetParentNodeRef) {
        AuthenticationUtil.setRunAsUserSystem();
        try {
            if (null == sourceParentNodeRef) {
                fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);

            } else {
                fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
            }
        } catch (Exception e) {
            throw new ScriptException("Can't move selected node", e);
        }
    }

    public void setFileFolderService(FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

}

It looks like you are not returning boolean as expected by the webscript in order to complete the whole process. This could be the reason it is getting stuck. If you review the move-to.json.js code, you would see this block:

if (result.success) {
  ................................
}

Your custom move is returning void. Try returning boolean from custom move script and see if it works.

Move method should be something like:

public boolean move(NodeRef sourceNodeRef, NodeRef sourceParentNodeRef, NodeRef targetParentNodeRef) {
    AuthenticationUtil.setRunAsUserSystem();
    boolean hasMoved = false;
    try {
        FileInfo fileInfo = null;
        if (null == sourceParentNodeRef) {
            fileInfo = fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);
        } else {
            fileInfo = fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
        }
        hasMoved = nodeService.exists(fileInfo.getNodeRef());
    } catch (Exception e) {
        throw new ScriptException("Can't move selected node", e);
    }
    return hasMoved;
}

With help of NodeService check whether node exists after move and then return boolean. 

~Abhinav
(ACSCE, AWS SAA, Azure Admin)

View answer in original post

16 REPLIES 16

onurg
Champ on-the-rise
Champ on-the-rise

Here is how it looks like right now:

move-to.post.json.js

<import resource="classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js">

/**
 * Move multiple files action
 * @method POST
 */

/**
 * Entrypoint required by action.lib.js
 *
 * @method runAction
 * @param p_params {object} Object literal containing files array
 * @return {object|null} object representation of action results
 */
function runAction(p_params) {
    var results = [],
    destNode = p_params.destNode,
    files = p_params.files,
    parent = null,
    file,
    fileNode,
    result,
    nodeRef,
    fromSite;

    // Must have array of files
    if (!files || files.length == 0) {
        status.setCode(status.STATUS_BAD_REQUEST, "No files.");
        return;
    }

    for (file in files) {
        nodeRef = files[file];
        result = {
            nodeRef: nodeRef,
            action: "moveFile",
            success: false
        }

        try {
            fileNode = search.findNode(nodeRef);
            if (fileNode == null) {
                result.id = file;
                result.nodeRef = nodeRef;
                result.success = false;
            } else {
                if (p_params.parent && p_params.parent != null) {
                    parent = search.findNode(p_params.parent);
                }
                result.id = fileNode.name;
                result.type = fileNode.isContainer ? "folder" : "document";

                // Retain the name of the site the node is currently in. Null if it's not in a site.
                fromSite = fileNode.siteShortName;

                var site = siteService.getSite(fromSite);
		var role = site.getMembersRole(person.properties.userName);
if (null != role && role == "CustomSiteCollaborator") { var parentNodeRef = null; if (parent != null) { parentNodeRef = parent.nodeRef; } // Move the node via custom move rootscoped object result.success = customMove.move(fileNode.nodeRef, parentNodeRef, destNode.nodeRef); } else { // Move the node via OOTB move to service result.success = fileNode.move(parent, destNode); } // move the node // result.success = fileNode.move(parent, destNode); if (result.success) { // If this was an inter-site move, we'll need to clean up the permissions on the node if ((fromSite) && (String(fromSite) !== String(fileNode.siteShortName))) { siteService.cleanSitePermissions(fileNode); } } } } catch (e) { result.id = file; result.nodeRef = nodeRef; result.success = false; //MNT-7514 Uninformational error message on move when file name conflicts result.fileExist = false; error = e.toString(); if (error.indexOf("FileExistsException") != -1) { result.fileExist = true; } } results.push(result); } return results; } /* Bootstrap action script */ main();

You can also check this post for the other changes I made.

abhinavmishra14
World-Class Innovator
World-Class Innovator

Keep some logging to make sure your logic works. Make sure to check from site and out of site moves. These are simple basic checks and debugging steps.

If you still see any errors, try removing custom permission check (or custom permission check + custom permission definition) and use any OOTB role such as 'SiteCollaborator' to make sure the extended move-to.post.json webscript is working fine. 

Enable following log config in log4j.properties:

log4j.logger.org.alfresco.repo.jscript.ScriptLogger=DEBUG

Updated code:

<import resource = "classpath:/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/action/action.lib.js" >

/**
 * Move multiple files action
 * @method POST
 */

/**
 * Entrypoint required by action.lib.js
 *
 * @method runAction
 * @param p_params {object} Object literal containing files array
 * @return {object|null} object representation of action results
 */
function runAction(p_params) {
    var results = [],
    destNode = p_params.destNode,
    files = p_params.files,
    parent = null,
    file,
    fileNode,
    result,
    nodeRef,
    fromSite;

    // Must have array of files
    if (!files || files.length == 0) {
        status.setCode(status.STATUS_BAD_REQUEST, "No files.");
        return;
    }

    for (file in files) {
        nodeRef = files[file];
        result = {
            nodeRef: nodeRef,
            action: "moveFile",
            success: false
        }

        try {
            fileNode = search.findNode(nodeRef);
            if (fileNode == null) {
                result.id = file;
                result.nodeRef = nodeRef;
                result.success = false;
            } else {
                if (p_params.parent && p_params.parent != null) {
                    parent = search.findNode(p_params.parent);
                }
                result.id = fileNode.name;
                result.type = fileNode.isContainer ? "folder" : "document";

                // Retain the name of the site the node is currently in. Null if it's not in a site.
                fromSite = fileNode.siteShortName;

                if (fromSite != null) { //If node is in a Site
                    var site = siteService.getSite(fromSite);
                    if (site != null) {
                        var role = site.getMembersRole(person.properties.userName)

                            if (logger.isLoggingEnabled()) {
                                logger.log("UserRole: " + role);
                            }

                            if (null != role && (role == "CustomSiteCollaborator")) {
                                var parentNodeRef = null;
                                if (parent != null) {
                                    parentNodeRef = parent.nodeRef;
                                }

                                if (logger.isLoggingEnabled()) {
                                    logger.log("Starting custom move for: " + role);
                                }
                                // custom move
                                result.success = customMove.move(fileNode.nodeRef, parentNodeRef, destNode.nodeRef);

                            } else {
                                // Move the node via OOTB move to service
                                if (logger.isLoggingEnabled()) {
                                    logger.log("Starting OOTB move from within a Site..");
                                }
                                result.success = fileNode.move(parent, destNode);
                            }
                    }
                } else { //If node is out of a Site
                    // Move the node via OOTB move to service
                    if (logger.isLoggingEnabled()) {
                        logger.log("Starting OOTB move..");
                    }
                    result.success = fileNode.move(parent, destNode);
                }

                // move the node
                // result.success = fileNode.move(parent, destNode);

                if (result.success) {
					if (logger.isLoggingEnabled()) {
                        logger.log("Cleaning up permissions for inter site move...");
                    }
                    // If this was an inter-site move, we'll need to clean up the permissions on the node
                    if ((fromSite) && (String(fromSite) !== String(fileNode.siteShortName))) {
                        siteService.cleanSitePermissions(fileNode);
                    }
                }
            }
        } catch (e) {
	    logger.log(e);
			
            result.id = file;
            result.nodeRef = nodeRef;
            result.success = false;

            //MNT-7514 Uninformational error message on move when file name conflicts
            result.fileExist = false;

            error = e.toString();
            if (error.indexOf("FileExistsException") != -1) {
                result.fileExist = true;
            }
        }

	if (logger.isLoggingEnabled()) {
            logger.log("Returning the result.");
        }
        results.push(result);
    }
    return results;
}

/* Bootstrap action script */
main();
~Abhinav
(ACSCE, AWS SAA, Azure Admin)

Thanks again for the explanation. Logger helped me a lot and I tested the script by removing custom permissions check and permission definitions. I think the modified move-to.post.json.js is functioning correctly but there is something wrong with my customMove method or with the bean registration.

After checking my XML and java files, I realized that I didn't have a bean reference for 'FileFolderService' in my custom move bean so I added it as below:

service-context.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="at.tuwien.dsdm.alfresco.acs.repo.customMoveObject"
          class="at.tuwien.dsdm.alfresco.acs.repo.CustomMoveObject"
          parent="baseJavaScriptExtension">
        <property name="extensionName" value="customMove" />
        <property name="fileFolderService">
            <ref bean="FileFolderService" />
        </property>
    </bean>

</beans>

After this change, I didn't get any errors regarding moving a file as a user with my new role. However, when I try to move any file I get the screen below:

image

And it gets stuck here. The dialog window or the text "Files being moved..." doesn't disappear. After refreshing the page I see that the file is moved successfully. Am I still missing any references during my bean registration? Or is something missing in my java class?

CustomMoveObject.java:

import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.scripts.ScriptException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;

public class CustomMoveObject extends BaseProcessorExtension {
    private FileFolderService fileFolderService;

    public void move(NodeRef sourceNodeRef, NodeRef sourceParentNodeRef, NodeRef targetParentNodeRef) {
        AuthenticationUtil.setRunAsUserSystem();
        try {
            if (null == sourceParentNodeRef) {
                fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);

            } else {
                fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
            }
        } catch (Exception e) {
            throw new ScriptException("Can't move selected node", e);
        }
    }

    public void setFileFolderService(FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

}

Onur

abhinavmishra14
World-Class Innovator
World-Class Innovator

@onurg wrote:

image

And it gets stuck here. The dialog window or the text "Files being moved..." doesn't disappear. After refreshing the page I see that the file is moved successfully. Am I still missing any references during my bean registration? Or is something missing in my java class?

CustomMoveObject.java:

import org.alfresco.repo.processor.BaseProcessorExtension;
import org.alfresco.scripts.ScriptException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;

public class CustomMoveObject extends BaseProcessorExtension {
    private FileFolderService fileFolderService;

    public void move(NodeRef sourceNodeRef, NodeRef sourceParentNodeRef, NodeRef targetParentNodeRef) {
        AuthenticationUtil.setRunAsUserSystem();
        try {
            if (null == sourceParentNodeRef) {
                fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);

            } else {
                fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
            }
        } catch (Exception e) {
            throw new ScriptException("Can't move selected node", e);
        }
    }

    public void setFileFolderService(FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

}

It looks like you are not returning boolean as expected by the webscript in order to complete the whole process. This could be the reason it is getting stuck. If you review the move-to.json.js code, you would see this block:

if (result.success) {
  ................................
}

Your custom move is returning void. Try returning boolean from custom move script and see if it works.

Move method should be something like:

public boolean move(NodeRef sourceNodeRef, NodeRef sourceParentNodeRef, NodeRef targetParentNodeRef) {
    AuthenticationUtil.setRunAsUserSystem();
    boolean hasMoved = false;
    try {
        FileInfo fileInfo = null;
        if (null == sourceParentNodeRef) {
            fileInfo = fileFolderService.move(sourceNodeRef, targetParentNodeRef, null);
        } else {
            fileInfo = fileFolderService.moveFrom(sourceNodeRef, sourceParentNodeRef, targetParentNodeRef, null);
        }
        hasMoved = nodeService.exists(fileInfo.getNodeRef());
    } catch (Exception e) {
        throw new ScriptException("Can't move selected node", e);
    }
    return hasMoved;
}

With help of NodeService check whether node exists after move and then return boolean. 

~Abhinav
(ACSCE, AWS SAA, Azure Admin)

*facepalm*

That was it. Thanks a lot for all the help. I will update my first post tomorrow with the correct implementations in case someone else needs something similar in the future. Really appreciated.

Onur

Edit: I didn't know you can accept more than one post as a solution. Thanks for marking the other needed post as a solution as well @abhinavmishra14!

abhinavmishra14
World-Class Innovator
World-Class Innovator

Glad it worked.. @onurg 

~Abhinav
(ACSCE, AWS SAA, Azure Admin)

yes @onurg you can accept it as a combined solution.

~Abhinav
(ACSCE, AWS SAA, Azure Admin)