Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 28, 2018 @ 12:36
    Simon Dingley
    0

    Disable Save Button in Members Area for Users that are not in a given UserGroup

    I have a requirement to provide read-only access to Member data for users other than membership administrators. This has been achieved and details can be found in the thread Intercepting a Core Directive in Angular thanks to some help from Dave Woestenborghs.

    There is, however, one current piece of the puzzle yet to solve and that is removing the ability to "Save" a member for users not in a given user group.

    I know I can probably cancel the save event but I'm the sort of person that would prefer prevention rather than cure and so I'd rather not present users with a feature that is of no use or concern to them.

    Should I be looking at an angular interceptor again to achieve this? It still seems a little hacky.

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Nov 28, 2018 @ 13:58
    Dave Woestenborghs
    0

    Hi Simon,

    I never tried it with members but I think it works similar as I got it working for content

     protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
        {       
            EditorModelEventManager.SendingContentModel += this.EditorModelEventManager_SendingContentModel;
        }
    
    private void EditorModelEventManager_SendingContentModel(System.Web.Http.Filters.HttpActionExecutedContext sender, EditorModelEventArgs<Umbraco.Web.Models.ContentEditing.ContentItemDisplay> e)
        {
            var contentItemDisplay = e.Model;
            var context = e.UmbracoContext;
    
           if (contentItemDisplay.ContentTypeAlias == "MyDocType")
            {
                            contentItemDisplay.AllowedActions = contentItemDisplay.AllowedActions.ToList().Where(x => x != "A" && x != "U");
            }
        }
    

    This removes the 'Save' and 'Save & Publish' button from a doctype with a specific content type. In the examples from the other thread you can figure out on how to check on user group.

    An overview of all the action letters can be found here : https://our.umbraco.com/documentation/Extending/Section-Trees/tree-actions

    Dave

  • Julien Kulker 75 posts 427 karma points c-trib
    Nov 28, 2018 @ 14:19
    Julien Kulker
    0

    Content is not the same as a Member. So you have to return a MemberDisplay

     EditorModelEventManager.SendingMemberModel += this.EditorModelEventManager_SendingMemberModel;
    
     private void EditorModelEventManager_SendingMemberModel(System.Web.Http.Filters.HttpActionExecutedContext sender, EditorModelEventArgs<Umbraco.Web.Models.ContentEditing.MemberDisplay> e)
        {
        }
    

    But then i can't find really a option to disable it maybe you have some ideas @dave

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 28, 2018 @ 14:22
    Simon Dingley
    0

    Thanks Dave but as Julien pointed out, unfortunately, the MemberDisplay class does not have an AllowedActions property like ContentItemDisplay

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Nov 28, 2018 @ 14:31
    Dave Woestenborghs
    1

    Ouch..should have checked that...

    Just had this example lying around. Don't see it can be done using c# code.

    So the only option is to use a interceptor.

    Maybe it's possible without actually replacing the entire view. Here nathan woulfe has an example on how to change the html using a interceptor without swapping a view

    https://github.com/nathanwoulfe/NestingContently/blob/master/src/NestingContently/backoffice/interceptor.js

    Dave

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 28, 2018 @ 15:46
    Simon Dingley
    0

    Interesting, I will take a look into that. Thanks

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 29, 2018 @ 09:20
    Simon Dingley
    0

    Morning David, I've made good progress on this now and have it working without the check on userGroup so thanks for your help. My angular skills are far from an expert and I am struggling to get access to userService so I can perform the userGroup check. What is the correct way to gain access to the service here?

    If I do the following I get a circular dependency error:

    angular.module('umbraco').factory('memberSaveButtonInterceptor', ['$q', 'userService', memberSaveButtonInterceptor]);
    angular.module('umbraco').config(['$httpProvider', addInterceptors]);
    

    Circular dependency: authResource <- userService <- memberSaveButtonInterceptor <- $http <- umbRequestHelper <- mediaHelper

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Nov 29, 2018 @ 09:23
    Dave Woestenborghs
    0

    Hi Simon,

    Can you post your full code ?

    Dave

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 29, 2018 @ 09:29
    Simon Dingley
    0

    Sure, here you go.

    (() => {
    
        // This could be done using content events, but that then means the injected directive would need to be compiled
        // while this is a bit flaky, it ensures the directive is in the DOM when the digest cycle runs so should be more efficient
        // passing the prefix in to the directive allows construction of correct class names
        // Ref: https://github.com/nathanwoulfe/NestingContently/blob/master/src/NestingContently/backoffice/interceptor.js
        function memberSaveButtonInterceptor($q, userService) {
            return {
                response: resp => {
                    if (resp.config.url.toLowerCase().indexOf('views/member/edit.html') !== -1) {
                        const pattern = /<umb-button[\s\S]*? type="submit"[\s\S]*? label="Save"[\s\S]*? \S+=["']?(?:.(?!["']?\s+(?:\S+)=|[>"']))+.["'][\s\S]*?>[\s\S]*?<\/umb-button>/gmi;
                        if (resp.data.match(pattern)) {
                            // Remove the save button in it's entirety
                            resp.data = resp.data.replace(pattern, '');
    
                            //userService.getCurrentUser().then(function (result) {
                            //  return result.userGroups;
                            //}).then(function (userGroups) {
                            //  console.log(userGroups);
                            //});
                        }
                    }
                    return resp || $q.when(resp);
                }
            };
        }
    
        // add the interceptor, if it doesn't exist already.
        function addInterceptors($httpProvider) {
            if ($httpProvider.interceptors.indexOf('memberSaveButtonInterceptor') === -1) {
                $httpProvider.interceptors.push('memberSaveButtonInterceptor');
            }
        }
    
        angular.module('umbraco').factory('memberSaveButtonInterceptor', ['$q', 'userService', memberSaveButtonInterceptor]);
        angular.module('umbraco').config(['$httpProvider', addInterceptors]);
    
    })();
    
  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Nov 29, 2018 @ 10:04
    Dave Woestenborghs
    1

    Hi Simon,

    I just looked at my interceptor code in Nexu.

    I don't use a factory. But I use the notifications service by adding when pushing the interceptor

    https://github.com/dawoe/umbraco-nexu/blob/develop/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js#L5

    Maybe that's the issue.

    But I also using another pattern i see to get my own service.

    https://github.com/dawoe/umbraco-nexu/blob/develop/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js#L45

    Dave

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 29, 2018 @ 20:33
    Simon Dingley
    0

    Thanks Dave, I've made some further progress. I think I now understand why I was getting the circular dependency errors, see the link in my comments below. This is where I am at now:

    angular.module('umbraco.services').config([
        '$httpProvider',
        function($httpProvider) {
    
            $httpProvider.interceptors.push([
                '$q', '$injector', function($q, $injector) {
                    return {
                        response: resp => {
                            if (resp.config.url.toLowerCase().indexOf('views/member/edit.html') !== -1) {
                                // Use the injector to get the userService and avoid circular dependencies
                                // See https://www.codelord.net/2016/11/10/circular-dependencies-in-angular-and-the-injector-service/
                                var userService = $injector.get("userService");
                                userService.getCurrentUser().then(function(result) {
                                    return result.userGroups;
                                }).then(function(userGroups) {
                                    if (userGroups.indexOf("membershipAdministrator") > -1) {
                                        const pattern = /<umb-button[\s\S]*? type="submit"[\s\S]*? label="Save"[\s\S]*? \S+=["']?(?:.(?!["']?\s+(?:\S+)=|[>"']))+.["'][\s\S]*?>[\s\S]*?<\/umb-button>/gmi;
                                        if (resp.data.match(pattern)) {
                                            // Remove the save button in it's entirety
                                            resp.data = resp.data.replace(pattern, '');
                                        }
                                    }
                                });
                            }
                            return resp || $q.when(resp);
                        }
                    };
                }
            ]);
    
        }
    ]);
    

    I am now fighting a new issue but too tired to make any further meaningful progress tonight. The new errors I think might relate to the callbacks:

    enter image description here

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 30, 2018 @ 10:08
    Simon Dingley
    0

    The error in my last message is a red herring and doesn't appear to be linked to this work. The only thing I am currently struggling to resolve now is that the resp is returned before the promise is resolved and therefore the button is still appearing in the page. I need a way to stop the function returning until the promise is resolved.

  • Dave Woestenborghs 3504 posts 12133 karma points MVP 8x admin c-trib
    Nov 30, 2018 @ 10:12
    Dave Woestenborghs
    1

    You will probably need to 'defer' your promise untill the call to userService is resolved.

    https://github.com/dawoe/umbraco-nexu/blob/develop/Source/Our.Umbraco.Nexu.Core/Client/interceptor.js#L48

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 30, 2018 @ 10:37
    Simon Dingley
    100

    Perfect, thanks Dave! Here is a working version now.

    $httpProvider.interceptors.push([
        '$q', '$injector', function ($q, $injector) {
            return {
                response: resp => {
    
                    if (resp.config.url.toLowerCase().indexOf('views/member/edit.html') !== -1) {
                        // Use the injector to get the userService and avoid circular dependencies
                        // See https://www.codelord.net/2016/11/10/circular-dependencies-in-angular-and-the-injector-service/
                        var userService = $injector.get("userService");
    
                        // create deferred request so we don't return the response before our promises have resolved
                        var deferred = $q.defer();
    
                        userService.getCurrentUser().then(function (result) {
                            return result.userGroups;
                        }).then(function (userGroups) {
                            if (userGroups.indexOf("membershipAdministrator") === -1) {
                                const pattern = /<umb-button[\s\S]*? type="submit"[\s\S]*? label="Save"[\s\S]*? \S+=["']?(?:.(?!["']?\s+(?:\S+)=|[>"']))+.["'][\s\S]*?>[\s\S]*?<\/umb-button>/gmi;
                                if (resp.data.match(pattern)) {
                                    // Remove the save button in it's entirety
                                    resp.data = resp.data.replace(pattern, '');
                                }
                            }
                            // Resolve the promise
                            deferred.resolve(resp);
                        });
    
                        // return deferred promise
                        return deferred.promise;
                    }
    
                    return resp || $q.when(resp);
                }
            };
        }
    ]);
    

    One final thing left for me to do and that is to remove the actions menu but I think I should be able to use the same technique for that.

    Thanks again for all of your help!

    Cheers, Simon

  • Simon Dingley 1470 posts 3427 karma points c-trib
    Nov 30, 2018 @ 13:47
    Simon Dingley
    0

    For completeness (for now) I am removing the menu as follows:

    $httpProvider.interceptors.push([
        '$q', '$injector', function ($q, $injector) {
            return {
                response: resp => {
    
                    if (resp.config.url.toLowerCase().indexOf('views/components/editor/umb-editor-menu.html') !== -1) {
                        // Use the injector to get the userService and avoid circular dependencies
                        // See https://www.codelord.net/2016/11/10/circular-dependencies-in-angular-and-the-injector-service/
                        var userService = $injector.get("userService");
    
                        // create deferred request so we don't return the response before our promises have resolved
                        var deferred = $q.defer();
    
                        userService.getCurrentUser().then(function (result) {
                            return result.userGroups;
                        }).then(function (userGroups) {
                            if (userGroups.indexOf("membershipAdministrator") === -1) {
                                if (resp.data.match(pattern)) {
                                    // Remove the save button in it's entirety
                                    resp.data = resp.data = '';
                                }
                            }
                            // Resolve the promise
                            deferred.resolve(resp);
                        });
    
                        // return deferred promise
                        return deferred.promise;
                    }
    
                    return resp || $q.when(resp);
                }
            };
        }
    ]);
    

    I'm not sure I like either approach to the problem, it feels a bit hacky. There should probably be more granular control over user permissions in the Members section so will consider creating an issue for it on the tracker.

Please Sign in or register to post replies

Write your reply to:

Draft