superdesk/superdesk-client-core

View on GitHub
scripts/apps/users/directives/UserEditDirective.ts

Summary

Maintainability
F
3 days
Test Coverage
import {gettext} from 'core/utils';
import {appConfig} from 'appConfig';
import {applyDefault} from 'core/helpers/typescript-helpers';
import {CC} from 'core/ui/configurable-ui-components';
import {generate} from 'json-merge-patch';
import {noop} from 'lodash';
import {showConfirmationPrompt} from 'core/ui/show-confirmation-prompt';

UserEditDirective.$inject = ['api', 'notify', 'usersService', 'userList', 'session', 'lodash',
    'langmap', '$location', '$route', 'superdesk', 'features', 'asset', 'privileges',
    'desks', 'keyboardManager', 'gettextCatalog', 'metadata', 'modal', '$q'];
export function UserEditDirective(api, notify, usersService, userList, session, _,
    langmap, $location, $route, superdesk, features, asset, privileges, desks, keyboardManager,
    gettextCatalog, metadata, modal, $q) {
    return {
        templateUrl: asset.templateUrl('apps/users/views/edit-form.html'),
        scope: {
            origUser: '=user',
            onsave: '&',
            oncancel: '&',
            onupdate: '&',
        },
        link: function(scope, elem) {
            // origUser is set by parent scope when selecting users from GUI
            // but it also needs to be updated before editing so dirtiness can be computed correctly
            // according to the latest data on the server
            let clearOrigUserWatcher = noop;

            // only initialize after selecting a user for editing/creation
            // having it running when switcing between users can cause fields to be modified
            // or produce an incorrect dirtiness value
            let clearUserWatcher = noop;

            metadata.initialize().then(() => {
                scope.metadata = metadata.values;
            });
            scope.activeNavigation = null;
            scope.privileges = privileges.privileges;
            scope.features = features;
            scope.usernamePattern = appConfig.user?.username_pattern != null ?
                new RegExp(appConfig.user.username_pattern) : usersService.usernamePattern;
            scope.twitterPattern = usersService.twitterPattern;
            scope.phonePattern = usersService.phonePattern;
            scope.signOffPattern = usersService.signOffPattern;
            scope.hideSignOff = appConfig.user != null && appConfig.user.sign_off_mapping;

            // disallow changing an avatar if custom avatars are configured for the instance
            scope.canChangeAvatar = CC.UserAvatar == null;

            scope.dirty = false;
            scope.errorMessage = null;

            // hiding the edit form until user hasn't loaded
            // not doing so causes issues when switching users quickly
            // UserUniqueDirective was computing results based on data of a previously selected user
            scope.loading = true;

            scope.xmppEnabled = appConfig.xmpp_auth;

            resetUser();

            // user avatar component expects immutable data and won't update if object reference hasn't changed
            scope.userImmutable = {};

            scope.isNetworkSubscription = () =>
                ['solo', 'team'].indexOf(appConfig.subscriptionLevel) === -1;

            api('roles').query()
                .then((result) => {
                    scope.roles = _.keyBy(result._items, '_id');
                });
            // get available translation languages
            var noBaseLanguage = true;

            scope.languages = appConfig.profileLanguages.map((lang) => {
                if (lang === gettextCatalog.baseLanguage) {
                    noBaseLanguage = false;
                }

                const langCode = lang.replace('_', '-');

                if (langmap[langCode]) {
                    return {code: lang, nativeName: langmap[langCode].nativeName};
                }

                return {code: lang, nativeName: lang};
            });

            // add baseLanguage if needed
            if (noBaseLanguage) {
                scope.languages.unshift({
                    code: gettextCatalog.baseLanguage,
                    nativeName: langmap[gettextCatalog.baseLanguage].nativeName,
                });
            }

            scope.cancel = function() {
                resetUser();
                if (!scope.origUser.Id) {
                    scope.oncancel();
                }
            };
            scope.focused = function() {
                keyboardManager.unbind('down');
                keyboardManager.unbind('up');
            };

            scope.editPicture = function() {
                superdesk.intent('edit', 'avatar', scope.user).then((avatar) => {
                    scope.user.picture_url = avatar; // prevent replacing Avatar which would get into diff
                });
            };

            scope.goTo = function(id) {
                document.getElementById(id).scrollIntoView({
                    behavior: 'smooth',
                });

                scope.activeNavigation = id;
            };

            scope.checkNavigation = function(id) {
                return scope.activeNavigation === id;
            };

            function validateField(response, field) {
                if (scope.userForm?.[field]) {
                    if (scope.error[field]) {
                        scope.error.message = null;
                    }
                    for (var constraint in response.data._issues[field]) {
                        if (response.data._issues[field][constraint]) {
                            scope.userForm[field].$setValidity(constraint, false);
                            scope.error.message = null;
                        }
                    }
                }
            }

            scope.save = function() {
                new Promise((resolve) => {
                    if (
                        scope.user._id !== session.identity._id // changing language for another user
                        || scope.user.language === scope.origUser.language
                    ) {
                        resolve(false);
                    } else {
                        showConfirmationPrompt({
                            title: 'Do you want to reload the page now?',
                            message: 'The page needs to be reloaded to change the language',
                        }).then((confirmed) => {
                            if (confirmed) {
                                resolve(true);
                            }
                        });
                    }
                })
                    .then((reloadPage) => {
                        scope.error = null;
                        notify.info(gettext('Saving...'));
                        return usersService.save(scope.origUser, generate(scope.origUser, scope.user))
                            .then((response) => {
                                scope.origUser = response;
                                resetUser();
                                notify.pop();
                                notify.success(gettext('user saved.'));
                                scope.onsave({user: scope.origUser});
                                metadata.fetchAuthors(self);
                                if (scope.user._id === session.identity._id) {
                                    session.updateIdentity(scope.origUser);
                                }

                                userList.clearCache();

                                if (reloadPage === true) {
                                    window.location.reload();
                                }
                            }, (response) => {
                                notify.pop();
                                if (response.status === 404) {
                                    if ($location.path() === '/users/') {
                                        $route.reload();
                                    } else {
                                        $location.path('/users/');
                                    }
                                    notify.error(gettext('User was not found. The account might have been deleted.'));
                                } else {
                                    var errorMessage = gettext('There was an error when saving the user account. ');

                                    if (response.data && response.data._issues) {
                                        if (angular.isDefined(response.data._issues['validator exception'])) {
                                            errorMessage = gettext(
                                                'Error: {{error}}',
                                                {error: response.data._issues['validator exception']},
                                            );
                                        }

                                        scope.error = response.data._issues;
                                        scope.error.message = errorMessage;

                                        for (var field in response.data._issues) {
                                            validateField(response, field);
                                        }
                                    }

                                    notify.error(errorMessage);
                                }
                            });
                    });
            };

            scope.toggleStatus = function(active) {
                usersService.toggleStatus(scope.origUser, active).then(() => {
                    resetUser();
                    scope.onupdate({user: scope.origUser});
                });
            };

            function resetUser() {
                clearOrigUserWatcher();
                clearUserWatcher();

                scope.dirty = false;
                scope.loading = true;

                return $q.when()
                    .then(() => {
                        const user = scope.origUser;

                        if (angular.isDefined(user._id)) {
                            return userList.getUser(user._id, true)
                                .then((u) => {
                                    if (u.is_author === undefined) {
                                        u.user.is_author = true;
                                    }

                                    scope.error = null;
                                    scope.origUser = u;
                                    scope.user = Object.assign({}, u);
                                    scope.confirm = {password: null};
                                    scope.show = {password: false};
                                    scope._active = usersService.isActive(u);
                                    scope._pending = usersService.isPending(u);
                                    scope.profile = scope.user._id === session.identity._id;
                                    scope.userDesks = [];
                                    if (angular.isDefined(u) && angular.isDefined(u._links)) {
                                        desks.fetchUserDesks(u).then((response) => {
                                            scope.userDesks = response;
                                        });
                                    }
                                });
                        } else {
                            scope.user = {};

                            return $q.when();
                        }
                    })
                    .then(() => {
                        clearOrigUserWatcher = scope.$watch('origUser', (newVal, oldVal) => {
                            if (newVal !== oldVal) {
                                resetUser();
                            }
                        });

                        let userWatchInitialized = false;

                        clearUserWatcher = scope.$watchCollection('user', (user) => {
                            // avoid incorrect dirty check when user is undefined and not initialized
                            if (userWatchInitialized) {
                                scope.userImmutable = {...user};

                                _.each(user, (value, key) => {
                                    if (scope.origUser[key] !== '' && value === '') {
                                        if (key !== 'phone' || key !== 'byline') {
                                            user[key] = null;
                                        } else {
                                            delete user[key];
                                        }
                                    }
                                });
                                scope.dirty = JSON.stringify(user) !== JSON.stringify(scope.origUser);
                            } else {
                                userWatchInitialized = true;
                                scope.userImmutable = {...scope.user};
                            }
                        });

                        scope.loading = false;
                    });
            }

            scope.$on('user:updated', (event, user) => {
                scope.origUser = user;
                resetUser();
            });

            scope.profileConfig = applyDefault(appConfig.profile, {});
        },
    };
}