From bdaa3ab3d581a4e79ba4b5bc0f17d2880f9ccac5 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Mon, 24 Oct 2022 11:17:36 -0400 Subject: [PATCH] Add MemberManage dashboard --- .../installed/installedPackages.config | 134 ++++ .../dashboard/memberListView.controller.js | 720 ++++++++++++++++++ .../backoffice/dashboard/memberListView.html | 187 +++++ .../dialogs/member/edit.controller.js | 149 ++++ .../backoffice/dialogs/member/edit.html | 45 ++ .../dialogs/member/export.controller.js | 116 +++ .../backoffice/dialogs/member/export.html | 90 +++ .../dialogs/member/filter.controller.js | 167 ++++ .../backoffice/dialogs/member/filter.html | 95 +++ .../MemberManager/css/memberListView.css | 61 ++ WebCms/App_Plugins/MemberManager/lang/cs.xml | 42 + WebCms/App_Plugins/MemberManager/lang/da.xml | 42 + WebCms/App_Plugins/MemberManager/lang/de.xml | 42 + .../App_Plugins/MemberManager/lang/en-gb.xml | 43 ++ .../App_Plugins/MemberManager/lang/en-us.xml | 43 ++ WebCms/App_Plugins/MemberManager/lang/es.xml | 42 + WebCms/App_Plugins/MemberManager/lang/fr.xml | 42 + WebCms/App_Plugins/MemberManager/lang/he.xml | 42 + WebCms/App_Plugins/MemberManager/lang/it.xml | 42 + WebCms/App_Plugins/MemberManager/lang/ja.xml | 42 + WebCms/App_Plugins/MemberManager/lang/ko.xml | 42 + WebCms/App_Plugins/MemberManager/lang/nb.xml | 42 + WebCms/App_Plugins/MemberManager/lang/nl.xml | 42 + WebCms/App_Plugins/MemberManager/lang/pl.xml | 42 + WebCms/App_Plugins/MemberManager/lang/pt.xml | 42 + WebCms/App_Plugins/MemberManager/lang/ru.xml | 42 + WebCms/App_Plugins/MemberManager/lang/sv.xml | 42 + WebCms/App_Plugins/MemberManager/lang/tr.xml | 42 + .../App_Plugins/MemberManager/lang/zh-tw.xml | 42 + WebCms/App_Plugins/MemberManager/lang/zh.xml | 42 + .../MemberManager/package.manifest | 12 + .../resources/member.resource.js | 204 +++++ WebCms/Config/ClientDependency.config | 2 +- WebCms/Config/Dashboard.config | 23 +- 34 files changed, 2834 insertions(+), 13 deletions(-) create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.html create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.html create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.html create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js create mode 100644 WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.html create mode 100644 WebCms/App_Plugins/MemberManager/css/memberListView.css create mode 100644 WebCms/App_Plugins/MemberManager/lang/cs.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/da.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/de.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/en-gb.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/en-us.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/es.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/fr.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/he.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/it.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/ja.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/ko.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/nb.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/nl.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/pl.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/pt.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/ru.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/sv.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/tr.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/zh-tw.xml create mode 100644 WebCms/App_Plugins/MemberManager/lang/zh.xml create mode 100644 WebCms/App_Plugins/MemberManager/package.manifest create mode 100644 WebCms/App_Plugins/MemberManager/resources/member.resource.js diff --git a/WebCms/App_Data/packages/installed/installedPackages.config b/WebCms/App_Data/packages/installed/installedPackages.config index 42a0da3..6ba927c 100644 --- a/WebCms/App_Data/packages/installed/installedPackages.config +++ b/WebCms/App_Data/packages/installed/installedPackages.config @@ -78,4 +78,138 @@ + + MIT License + Robert Foster + + + +
+ + member + + + + /app_plugins/MemberManager/backoffice/dashboard/memberListView.html + + +
+
+
+ + + + + + + + + + + + + + /App_Plugins/MemberManager/package.manifest + /App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js + /App_Plugins/MemberManager/backoffice/dashboard/memberListView.html + /App_Plugins/MemberManager/backoffice/dialogs/member/edit.html + /App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js + /App_Plugins/MemberManager/backoffice/dialogs/member/export.html + /App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js + /App_Plugins/MemberManager/backoffice/dialogs/member/filter.html + /App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js + /App_Plugins/MemberManager/css/memberListView.css + /App_Plugins/MemberManager/resources/member.resource.js + /bin/MemberListView.dll + /bin/ClosedXML.dll + /bin/DocumentFormat.OpenXml.dll + /bin/ExcelNumberFormat.dll + /bin/FastMember.dll + /App_Plugins/MemberManager/lang/cs.xml + /App_Plugins/MemberManager/lang/da.xml + /App_Plugins/MemberManager/lang/de.xml + /App_Plugins/MemberManager/lang/en-gb.xml + /App_Plugins/MemberManager/lang/en-us.xml + /App_Plugins/MemberManager/lang/es.xml + /App_Plugins/MemberManager/lang/fr.xml + /App_Plugins/MemberManager/lang/he.xml + /App_Plugins/MemberManager/lang/it.xml + /App_Plugins/MemberManager/lang/ja.xml + /App_Plugins/MemberManager/lang/ko.xml + /App_Plugins/MemberManager/lang/nb.xml + /App_Plugins/MemberManager/lang/nl.xml + /App_Plugins/MemberManager/lang/pl.xml + /App_Plugins/MemberManager/lang/pt.xml + /App_Plugins/MemberManager/lang/ru.xml + /App_Plugins/MemberManager/lang/sv.xml + /App_Plugins/MemberManager/lang/tr.xml + /App_Plugins/MemberManager/lang/zh-tw.xml + /App_Plugins/MemberManager/lang/zh.xml + + + + + + + +
\ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js b/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js new file mode 100644 index 0000000..f2152c0 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js @@ -0,0 +1,720 @@ +/** + * @ngdoc controller + * @name MemberManager.List.ViewController + * @function + * + * @description + * The controller for the member list view + * + * @param {object} $scope Scope + * @param {object} $routeParams Route Parameters + * @param {object} $timeout Timeout + * @param {object} $location Location + * @param {object} memberResource Member Resource + * @param {object} memberExtResource Member Extension Resource (custom) + * @param {object} memberTypeResource Member Type Resource + * @param {object} notificationsService Notifications Service + * @param {object} iconHelper Icon Helper + * @param {object} dialogService Dialog Service + * @param {object} editorState Editor State + * @param {object} localizationService Localisation Service + * @param {object} listViewHelper Listview Helper + */ +function memberListViewController($scope, $routeParams, $timeout, $location, memberResource, memberExtResource, memberTypeResource, notificationsService, iconHelper, dialogService, editorState, localizationService, listViewHelper) { + + var deleteItemCallback, getIdCallback, createEditUrlCallback; + + $scope.model = {}; + $scope.model.config = { + pageSize: 100, + orderBy: 'Name', + orderDirection: 'asc', + includeProperties: [ + { alias: 'email', header: 'Email', isSystem: 1 }, + { alias: 'memberGroups', header: 'Group', isSystem: 1 }, + { alias: 'firstName', header: 'First Name', isSystem: 0 }, + { alias: 'lastName', header: 'Last Name', isSystem: 0 }, + { alias: 'phone', header: 'Phone', isSystem: 0 }, + { alias: 'isApproved', header: 'Approved', isSystem: 1 }, + { alias: 'isLockedOut', header: 'Locked Out', isSystem: 1 } + ], + layouts: [ + { name: 'List', path: 'views/propertyeditors/listview/layouts/list/list.html', icon: 'icon-list', isSystem: 1, selected: true } + ], + bulkActionPermissions: { + allowBulkDelete: true, + allowExport: true + } + }; + + $scope.currentNodePermissions = null; + + if (editorState.current) { + //Fetch current node allowed actions for the current user + //This is the current node & not each individual child node in the list + var currentUserPermissions = editorState.current.allowedActions; + + //Create a nicer model rather than the funky & hard to remember permissions strings + $scope.currentNodePermissions = { + "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O + "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C + "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D + "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M + }; + } + + $scope.entityType = "member"; + $scope.memberGroups = null; + + getContentTypesCallback = memberTypeResource.getTypes; + deleteItemCallback = memberResource.deleteByKey; + getIdCallback = function (selected) { + return selected.key; + }; + createEditUrlCallback = function (item) { + return "/" + $scope.entityType + "/" + $scope.entityType + "/edit/" + item.key; + }; + + $scope.pagination = []; + $scope.isNew = false; + $scope.actionInProgress = false; + $scope.selection = []; + $scope.folders = []; + $scope.listViewResultSet = { + totalPages: 0, + items: [] + }; + + $scope.createAllowedButtonSingle = false; + $scope.createAllowedButtonMulti = false; + + $scope.options = { + pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, + pageNumber: $routeParams.page && !isNaN($routeParams.page) && Number($routeParams.page) > 0 ? $routeParams.page : 1, + filterData: { + filter: null + }, + orderBy: ($scope.model.config.orderBy ? $scope.model.config.orderBy : 'Name').trim(), + orderDirection: $scope.model.config.orderDirection ? $scope.model.config.orderDirection.trim() : "asc", + orderBySystemField: true, + includeProperties: $scope.model.config.includeProperties ? $scope.model.config.includeProperties : [ + { alias: 'updateDate', header: 'Last edited', isSystem: 1 }, + { alias: 'updater', header: 'Last edited by', isSystem: 1 } + ], + layout: { + layouts: $scope.model.config.layouts, + activeLayout: listViewHelper.getLayout($routeParams.id, $scope.model.config.layouts) + }, + allowBulkDelete: $scope.model.config.bulkActionPermissions.allowBulkDelete, + allowExport: $scope.model.config.bulkActionPermissions.allowExport + }; + + // Check if selected order by field is actually custom field + for (var j = 0; j < $scope.options.includeProperties.length; j++) { + var includedProperty = $scope.options.includeProperties[j]; + if (includedProperty.alias.toLowerCase() === $scope.options.orderBy.toLowerCase()) { + $scope.options.orderBySystemField = includedProperty.isSystem === 1; + break; + } + } + + //update all of the system includeProperties to enable sorting + _.each($scope.options.includeProperties, function (e, i) { + + //NOTE: special case for contentTypeAlias, it's a system property that cannot be sorted + // to do that, we'd need to update the base query for content to include the content type alias column + // which requires another join and would be slower. BUT We are doing this for members so not sure it makes a diff? + if (e.alias !== "contentTypeAlias") { + e.allowSorting = true; + } + + if (e.isSystem) { + //localize the header + var key = getLocalizedKey(e.alias); + localizationService.localize(key).then(function (v) { + e.header = v; + }); + } + }); + + function showNotificationsAndReset(err, reload, successMsg) { + + //check if response is ysod + if (err.status && err.status >= 500) { + + // Open ysod overlay + $scope.ysodOverlay = { + view: "ysod", + error: err, + show: true + }; + } + + $timeout(function () { + $scope.bulkStatus = ""; + $scope.actionInProgress = false; + }, + 500); + + if (reload === true) { + $scope.reloadView(); + } + + if (err.data && angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } else if (successMsg) { + localizationService.localize("bulk_done") + .then(function (v) { + notificationsService.success(v, successMsg); + }); + } + } + + $scope.next = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView(); + }; + + $scope.goToPage = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.reloadView(); + }; + + $scope.prev = function (pageNumber) { + $scope.options.pageNumber = pageNumber; + $scope.getContent(); + }; + + $scope.isSortDirection = function (col, direction) { + return $scope.options.orderBy.toUpperCase() === col.toUpperCase() && $scope.options.orderDirection === direction; + }; + + $scope.sort = function (field) { + + $scope.options.orderBy = field; + + if ($scope.options.orderDirection === "desc") { + $scope.options.orderDirection = "asc"; + } else { + $scope.options.orderDirection = "desc"; + } + + $scope.reloadView(); + }; + + $scope.getContent = function (contentId) { + $scope.reloadView(); + }; + + /*Loads the search results, based on parameters set in prev,next,sort and so on*/ + /*Pagination is done by an array of objects, due angularJS's funky way of monitoring state + with simple values */ + + $scope.reloadView = function () { + $scope.viewLoaded = false; + $scope.folders = []; + + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + + memberExtResource.getMembers($scope.options).then(function (data) { + + $scope.actionInProgress = false; + $scope.listViewResultSet = data; + + //update all values for display + if ($scope.listViewResultSet.items) { + _.each($scope.listViewResultSet.items, function (e, index) { + setPropertyValues(e); + }); + } + + $scope.viewLoaded = true; + + //NOTE: This might occur if we are requesting a higher page number than what is actually available, for example + // if you have more than one page and you delete all items on the last page. In this case, we need to reset to the last + // available page and then re-load again + if ($scope.options.pageNumber > $scope.listViewResultSet.totalPages) { + $scope.options.pageNumber = $scope.listViewResultSet.totalPages; + + //reload! + $scope.reloadView(); + } + + }); + + }; + + var searchListView = _.debounce(function () { + $scope.$apply(function () { + makeSearch(); + }); + }, 500); + + $scope.forceSearch = function (ev) { + //13: enter + switch (ev.keyCode) { + case 13: + makeSearch(); + break; + } + }; + + $scope.enterSearch = function () { + $scope.viewLoaded = false; + searchListView(); + }; + + function makeSearch() { + if ($scope.options.filterData.filter !== null && $scope.options.filterData.filter !== undefined) { + $scope.options.pageNumber = 1; + $scope.getContent(); + } + } + + $scope.isAnythingSelected = function () { + if ($scope.selection.length === 0) { + return false; + } else { + return true; + } + }; + + $scope.selectedItemsCount = function () { + return $scope.selection.length; + }; + + $scope.clearSelection = function () { + listViewHelper.clearSelection($scope.listViewResultSet.items, $scope.folders, $scope.selection); + }; + + $scope.getIcon = function (entry) { + return iconHelper.convertFromLegacyIcon(entry.icon); + }; + + function serial(selected, fn, getStatusMsg, index) { + return fn(selected, index).then(function (content) { + index++; + $scope.bulkStatus = getStatusMsg(index, selected.length); + return index < selected.length ? serial(selected, fn, getStatusMsg, index) : content; + }, function (err) { + var reload = index > 0; + showNotificationsAndReset(err, reload); + return err; + }); + } + + function applySelected(fn, getStatusMsg, getSuccessMsg, confirmMsg) { + var selected = $scope.selection; + if (selected.length === 0) + return; + if (confirmMsg && !confirm(confirmMsg)) + return; + + $scope.actionInProgress = true; + $scope.bulkStatus = getStatusMsg(0, selected.length); + + return serial(selected, fn, getStatusMsg, 0).then(function (result) { + // executes once the whole selection has been processed + // in case of an error (caught by serial), result will be the error + if (!(result.data && angular.isArray(result.data.notifications))) + showNotificationsAndReset(result, true, getSuccessMsg(selected.length)); + }); + } + + function getCustomPropertyValue(alias, properties) { + var value = ''; + var index = 0; + var foundAlias = false; + for (var i = 0; i < properties.length; i++) { + if (properties[i].alias === alias) { + foundAlias = true; + break; + } + index++; + } + + if (foundAlias) { + value = properties[index].value; + } + + return value; + } + + /** This ensures that the correct value is set for each item in a row, we don't want to call a function during interpolation or ng-bind as performance is really bad that way */ + + function setPropertyValues(result) { + + //set the edit url + result.editPath = createEditUrlCallback(result); + + _.each($scope.options.includeProperties, function (e, i) { + + var alias = e.alias; + + // First try to pull the value directly from the alias (e.g. updatedBy) + var value = result[alias]; + + // If we have IsApproved or IsLocked, then customise the display. + if (alias === "isLockedOut") { + value = getLockedDescription(value); + } + if (alias === "isApproved") { + value = getSuspendedDescription(value); + } + + // if we have an array, then concat all values separated by comma + if (Array.isArray(value) && value.length > 0) { + value = _.reduce(value, function (memo, val) { return memo + ', ' + val; }); + } + + // If this returns an object, look for the name property of that (e.g. owner.name) + if (value === Object(value)) { + value = value['name']; + } + + // If we've got nothing yet, look at a user defined property + if (typeof value === 'undefined') { + value = getCustomPropertyValue(alias, result.properties); + } + + // If we have a date, format it + if (isDate(value)) { + value = value.substring(0, value.length - 3); + } + + // set what we've got on the result + result[alias] = value; + }); + + + } + + function isDate(val) { + if (angular.isString(val)) { + return val.match(/^(\d{4})\-(\d{2})\-(\d{2})\ (\d{2})\:(\d{2})\:(\d{2})$/); + } + return false; + } + + function initView() { + //default to root id if the id is undefined + var id = $routeParams.id; + if (id === undefined) { + id = -1; + } + + memberExtResource.getMemberGroups().then(function (groups) { + $scope.memberGroups = groups; + }); + + getContentTypesCallback(id).then(function (listViewAllowedTypes) { + $scope.listViewAllowedTypes = listViewAllowedTypes; + $scope.createAllowedButtonSingle = listViewAllowedTypes.length === 1; + $scope.createAllowedButtonMulti = listViewAllowedTypes.length > 1; + }); + + $scope.contentId = id; + $scope.isTrashed = id === "-20" || id === "-21"; + + if ($scope.options.allowExport) { + memberExtResource.canExport().then(function (result) { + $scope.options.allowExport = result; + }); + } + + $scope.options.bulkActionsAllowed = $scope.options.allowBulkDelete; + + $scope.getContent(); + } + + function getLocalizedKey(alias) { + + switch (alias) { + case "updateDate": + return "content_updateDate"; + case "createDate": + return "content_createDate"; + case "contentTypeAlias": + return "content_membertype"; + case "email": + return "general_email"; + case "username": + return "general_username"; + case "memberGroups": + return "general_memberGroups"; + } + return alias; + } + + $scope.createBlank = function (entityType, docTypeAlias) { + $location + .path("/" + entityType + "/" + entityType + "/edit/") + .search("doctype=" + docTypeAlias + "&create=true"); + }; + + $scope.delete = function () { + var confirmDeleteText = ""; + + localizationService.localize("defaultdialogs_confirmdelete") + .then(function (value) { + confirmDeleteText = value; + + var attempt = + applySelected( + function (selected, index) { return deleteItemCallback(getIdCallback(selected[index])); }, + function (count, total) { + var key = (total === 1 ? "bulk_deletedItemOfItem" : "bulk_deletedItemOfItems"); + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = (total === 1 ? "bulk_deletedItem" : "bulk_deletedItems"); + return localizationService.localize(key, [total]); + }, + confirmDeleteText + "?"); + }); + }; + + $scope.canUnlock = function () { + if (!angular.isArray($scope.selection)) { + return false; + } + return _.some($scope.selection, function (item) { + return !isUnlocked(item); + }); + + }; + + $scope.canApprove = function () { + if (!angular.isArray($scope.selection)) { + return false; + } + return _.some($scope.selection, function (item) { + return isSuspended(item); + }); + + }; + + $scope.canSuspend = function () { + if (!angular.isArray($scope.selection)) { + return false; + } + return _.some($scope.selection, function (item) { + return isApproved(item); + }); + + }; + + isSuspended = function (item) { + return _.some($scope.listViewResultSet.items, function (member) { + if (member.key === item.key) + return member.isApproved === 'Suspended'; + return false; + }); + }; + + isApproved = function (item) { + return _.some($scope.listViewResultSet.items, function (member) { + if (member.key === item.key) + return member.isApproved === 'Approved'; + return false; + }); + }; + + isUnlocked = function (item) { + return _.some($scope.listViewResultSet.items, function (member) { + if (member.key === item.key) + return member.isLockedOut === 'Unlocked'; + return false; + }); + }; + + getLockedIcon = function (locked) { + return locked ? 'icon-lock color-red' : 'icon-unlocked color-green'; + }; + + getSuspendedIcon = function (approved) { + return approved ? 'icon-check color-green' : 'icon-block color-red'; + }; + + getLockedDescription = function (locked) { + return locked ? 'Locked Out' : 'Unlocked'; + }; + + getSuspendedDescription = function (approved) { + return approved ? 'Approved' : 'Suspended'; + }; + + $scope.export = function () { + dialogService.closeAll(); + dialogService.open({ + template: '/app_plugins/MemberManager/backoffice/dialogs/member/export.html', + closeOnSave: false, + filterData: $scope.options.filterData, + memberTypes: $scope.listViewAllowedTypes, + memberGroups: $scope.memberGroups, + totalItems: $scope.listViewResultSet.totalItems, + columns: $scope.options.columns, + callback: function (columns) { + $scope.options.columns = columns; + memberExtResource.getMemberExport($scope.options); + } + }); + }; + + $scope.unlock = function () { + var confirmUnlockText = ""; + + localizationService.localize("memberManager_confirmUnlock") + .then(function (value) { + confirmUnlockText = value; + + let tmpSelected = $scope.selected; + $scope.selected = _.filter(tmpSelected, function (item) { return isUnlocked(item); }); + var attempt = + applySelected( + function (selected, index) { return memberExtResource.unlockById(getIdCallback(selected[index])); }, + function (count, total) { + var key = total === 1 ? "bulk_unlockItemOfItem" : "bulk_unlockItemOfItems"; + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = total === 1 ? "bulk_unlockedItem" : "bulk_unlockedItems"; + return localizationService.localize(key, [total]); + }, + confirmUnlockText + "?"); + if (attempt) { + attempt.then(function () { + $timeout($scope.getContent, 1000); + }); + } + }); + }; + + $scope.approve = function () { + var confirmApproveText = ""; + + localizationService.localize("memberManager_confirmApprove") + .then(function (value) { + confirmApproveText = value; + + let tmpSelected = $scope.selected; + $scope.selected = _.filter(tmpSelected, function (item) { return isApproved(item); }); + + var attempt = + applySelected( + function (selected, index) { return memberExtResource.approveById(getIdCallback(selected[index])); }, + function (count, total) { + var key = total === 1 ? "bulk_approveItemOfItem" : "bulk_approveItemOfItems"; + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = total === 1 ? "bulk_approvedItem" : "bulk_approvedItems"; + return localizationService.localize(key, [total]); + }, + confirmApproveText + "?"); + if (attempt) { + attempt.then(function () { + $timeout($scope.getContent, 1000); + }); + } + }); + }; + + $scope.suspend = function () { + var confirmSuspendText = ""; + + let tmpSelected = $scope.selected; + $scope.selected = _.filter(tmpSelected, function (item) { return isSuspended(item); }); + + localizationService.localize("memberManager_confirmSuspend") + .then(function (value) { + confirmSuspendText = value; + + var attempt = + applySelected( + function (selected, index) { return memberExtResource.suspendById(getIdCallback(selected[index])); }, + function (count, total) { + var key = total === 1 ? "bulk_suspendItemOfItem" : "bulk_suspendItemOfItems"; + return localizationService.localize(key, [count, total]); + }, + function (total) { + var key = total === 1 ? "bulk_suspendedItem" : "bulk_suspendedItems"; + return localizationService.localize(key, [total]); + }, + confirmSuspendText + "?"); + if (attempt) { + attempt.then(function () { + $timeout($scope.getContent, 1000); + }); + } + }); + }; + + $scope.editMember = function (key) { + dialogService.closeAll(); + dialogService.open({ + template: '/app_plugins/MemberManager/backoffice/dialogs/member/edit.html', + key: key, + closeOnSave: true, + //tabFilter: ["Generic properties"], + callback: function (data) { + $scope.getContent(); + } + }); + }; + + $scope.clearFilter = function () { + dialogService.closeAll(); + $scope.options.filterData = { + filter: null + }; + //$scope.searchDisplay = null; + $scope.getContent(); + }; + + $scope.edit = function () { + $scope.filterDialog = {}; + $scope.filterDialog.title = localizationService.localize("memberManager_filter"); + $scope.filterDialog.section = $scope.entityType; + $scope.filterDialog.currentNode = $scope.contentId; + $scope.filterDialog.view = "move"; + $scope.filterDialog.show = true; + + $scope.moveDialog.submit = function (model) { + + if (model.target) { + performMove(model.target); + } + + $scope.moveDialog.show = false; + $scope.moveDialog = null; + }; + + $scope.moveDialog.close = function (oldModel) { + $scope.moveDialog.show = false; + $scope.moveDialog = null; + }; + + }; + + + $scope.filter = function () { + dialogService.closeAll(); + dialogService.open({ + template: '/app_plugins/MemberManager/backoffice/dialogs/member/filter.html', + closeOnSave: false, + filterData: $scope.options.filterData, + memberTypes: $scope.listViewAllowedTypes, + memberGroups: $scope.memberGroups, + callback: function (data) { + $scope.options.filterData = data; + $scope.getContent(); + } + }); + }; + initView(); +} + +angular.module("umbraco").controller("MemberManager.Dashboard.MemberListViewController", memberListViewController); \ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.html b/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.html new file mode 100644 index 0000000..b4d9e1c --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dashboard/memberListView.html @@ -0,0 +1,187 @@ +
+ + +
+ + + + + + + + + + + + + + {{ selectedItemsCount() }} of {{ listViewResultSet.items.length }} selected + + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ +
+
+ {{displayFilter.title}} + {{displayFilter.value}} +
+
+
+ + + + + + +
+ + +
+ + + +
+ diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js new file mode 100644 index 0000000..41895ff --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js @@ -0,0 +1,149 @@ +/** + * @ngdoc controller + * @name MemberManager.Dialogs.Member.EditController + * @function + * + * @description + * The controller for the member editor + * + * @param {any} $scope Scope + * @param {any} $routeParams Route Parameters + * @param {any} $q Queue + * @param {any} memberResource Member Resource + * @param {any} entityResource Entity Resource + * @param {any} serverValidationManager Server Validation Manager + * @param {any} contentEditingHelper Content Editing Helper + * @param {any} fileManager File Manager + * @param {any} formHelper Form Helper + */ +function MemberEditDialogController($scope, $routeParams, $q, memberResource, entityResource, serverValidationManager, contentEditingHelper, fileManager, formHelper) { + //setup scope vars + $scope.model = {}; + $scope.model.defaultButton = null; + $scope.model.subButtons = []; + $scope.model.nodeId = 0; + $scope.loaded = false; + + var dialogOptions = $scope.$parent.dialogOptions; + + function performGet() { + var deferred = $q.defer(); + if (angular.isObject(dialogOptions.entity)) { + $scope.loaded = true; + deferred.resolve(dialogOptions.entity); + } else { + if (dialogOptions.create) { + //we are creating so get an empty member item + memberResource.getScaffold(dialogOptions.contentType) + .then(function (data) { + + $scope.loaded = true; + deferred.resolve(data); + }); + } + else { + if (dialogOptions.key && dialogOptions.key.toString().length < 9) { + entityResource.getById(dialogOptions.key, "Member").then(function (entity) { + memberResource.getByKey(entity.key).then(function (data) { + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + $scope.loaded = true; + + deferred.resolve(data); + }); + }); + } + else { + //we are editing so get the content item from the server + memberResource.getByKey(dialogOptions.key) + .then(function (data) { + + $scope.loaded = true; + + //in one particular special case, after we've created a new item we redirect back to the edit + // route but there might be server validation errors in the collection which we need to display + // after the redirect, so we will bind all subscriptions which will show the server validation errors + // if there are any and then clear them so the collection no longer persists them. + serverValidationManager.executeAndClearAllSubscriptions(); + + deferred.resolve(data); + }); + } + } + } + + return deferred.promise; + } + + function performSave(args) { + var deferred = $q.defer(); + + $scope.busy = true; + + if (formHelper.submitForm({ scope: $scope, statusMessage: args.statusMessage })) { + + $scope.busy = true; + + args.saveMethod($scope.model.entity, $routeParams.create, fileManager.getFiles()) + .then(function (data) { + + formHelper.resetForm({ scope: $scope, notifications: data.notifications }); + + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + savedContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.model.entity, data) + }); + + $scope.busy = false; + deferred.resolve(data); + + }, function (err) { + + contentEditingHelper.handleSaveError({ + redirectOnFailure: false, + err: err, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.model.entity, err.data) + }); + + $scope.busy = false; + deferred.reject(err); + }); + } else { + $scope.busy = false; + deferred.reject(); + } + + return deferred.promise; + } + + performGet().then(function (content) { + $scope.model.entity = $scope.filterTabs(content, dialogOptions.tabFilter); + }); + + $scope.filterTabs = function (entity, blackList) { + if (blackList) { + _.each(entity.tabs, function (tab) { + tab.hide = _.contains(blackList, tab.alias); + }); + } + + return entity; + }; + + $scope.save = function () { + performSave({ saveMethod: memberResource.save, statusMessage: "Saving..." }) + .then(function (content) { + if (dialogOptions.closeOnSave) { + $scope.submit(content); + } + }); + }; + +} + +angular.module("umbraco").controller("MemberManager.Dialogs.Member.EditController", MemberEditDialogController); diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.html b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.html new file mode 100644 index 0000000..e90ae82 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/edit.html @@ -0,0 +1,45 @@ +
+ +
+
+ +
+ + + + +
+
\ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js new file mode 100644 index 0000000..f5bbdad --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js @@ -0,0 +1,116 @@ +angular.module("umbraco").controller("MemberManager.Dialogs.Member.ExportController", + function ($scope, memberExtResource) { + + $scope.defaultButton = null; + $scope.subButtons = []; + + var dialogOptions = $scope.$parent.dialogOptions; + + $scope.filterData = dialogOptions.filterData; + $scope.columns = dialogOptions.columns; + //$scope.memberType = dialogOptions.filterData.memberType; + $scope.totalItems = dialogOptions.totalItems; + $scope.memberTypes = dialogOptions.memberTypes; + $scope.format = "Excel"; + + $scope.allColumnsSelected = false; + + function init() { + var buttons = { + defaultButton: createButtonDefinition("E"), + subButtons: [] + }; + + $scope.defaultButton = buttons.defaultButton; + $scope.subButtons = buttons.subButtons; + + memberExtResource.getMemberColumns(dialogOptions.filterData.memberType).then(function (data) { + // We use this to preserve the original filter data. + $scope.columnList = setSelected(data, $scope.columns); + $scope.allColumnsSelected = $scope.allSelected($scope.columnList); + }); + + } + + function createButtonDefinition(ch) { + switch (ch) { + case "E": + //export action + return { + letter: ch, + labelKey: "memberManager_export", + label: "Export", + handler: $scope.export, + hotKey: "ctrl+e", + hotKeyWhenHidden: true, + alias: "export" + }; + default: + return null; + } + } + + // Loop through a list of select options and set selected for values that appear in a filter list + setSelected = function (list, filter) { + // Convert string arrays to a select item object. + list = _.map(list, function (item) { + if (typeof item === "string") { + let value = item; + item = { + id: value.replace(" ", "_"), + name: value, + alias: value, + selected: false + }; + } + return item; + }); + + if (filter) { + for (var i = 0; i < filter.length; i++) { + for (var j = 0; j < list.length; j++) { + if (list[j].id === filter[i].replace(" ", "_")) { + list[j].selected = true; + } + } + } + } + + return list; + }; + + processColumnList = function (list) { + var filteredList = _.filter(list, function (i) { + return i.selected; + }); + + return _.map(filteredList, function (item) { + return item.alias; + }); + }; + + // Methods to manage select/deselect all in checkbox lists. + $scope.selectAll = function (selectionList) { + _.each(selectionList, function (item) { item.selected = $scope.allColumnsSelected; }); + }; + + $scope.allSelected = function (selectionList) { + return _.filter(selectionList, function (item) { return item.selected; }).length === selectionList.length; + }; + + $scope.export = function () { + $scope.submit(processColumnList($scope.columnList)); + }; + + // this method is called for all action buttons and then we proxy based on the btn definition + $scope.performAction = function (btn) { + + if (!btn || !angular.isFunction(btn.handler)) { + throw "btn.handler must be a function reference"; + } + + btn.handler.apply(this); + }; + + init(); + }); \ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.html b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.html new file mode 100644 index 0000000..1f0b2ee --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/export.html @@ -0,0 +1,90 @@ +
+ +
+
+

+ Export +

+
+ + + +
+
\ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js new file mode 100644 index 0000000..b769272 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js @@ -0,0 +1,167 @@ +angular.module("umbraco").controller("MemberManager.Dialogs.Member.FilterController", + function ($scope) { + + $scope.defaultButton = null; + $scope.subButtons = []; + var dialogOptions = $scope.$parent.dialogOptions; + + $scope.filterData = dialogOptions.filterData; + $scope.memberTypes = dialogOptions.memberTypes; + $scope.memberGroups = dialogOptions.memberGroups; + + function init() { + var buttons = { + defaultButton: createButtonDefinition("F"), + subButtons: [ + createButtonDefinition("C") + ] + }; + + $scope.defaultButton = buttons.defaultButton; + $scope.subButtons = buttons.subButtons; + + // We use this to preserve the original filter data. + $scope.memberGroupFilter = setSelected($scope.memberGroups, $scope.filterData.memberGroups); + } + + function createButtonDefinition(ch) { + switch (ch) { + case "F": + //publish action + return { + letter: ch, + labelKey: "memberManager_applyFilter", + label: "Apply Filter", + handler: $scope.applyFilter, + hotKey: "ctrl+f", + hotKeyWhenHidden: true, + alias: "applyFilter" + }; + case "C": + //send to publish + return { + letter: ch, + labelKey: "memberManager_clearFilter", + label: "Clear Filter", + handler: $scope.clearFilter, + hotKey: "ctrl+c", + hotKeyWhenHidden: true, + alias: "sendToPublish" + }; + default: + return null; + } + } + + // Generate model for representing the search + getFilterModel = function () { + var displaySearch = new Array(); + + //if ($scope.filterData.filter) { + // displaySearch.push({ title: "Search", value: $scope.filterData.filter }); + //} + if ($scope.filterData.memberType) { + displaySearch.push({ title: "Member Type", value: _getMemberTypeName() }); + } + if ($scope.filterData.f_umbracoMemberApproved) { + displaySearch.push({ title: "Approved", value: $scope.filterData.f_umbracoMemberApproved === "true,1" ? "Approved" : "Suspended" }); + } + + if ($scope.filterData.f_umbracoMemberLockedOut) { + displaySearch.push({ title: "Locked Out", value: $scope.filterData.f_umbracoMemberLockedOut === "true,1" ? "Locked Out" : "Active" }); + } + + return { + filter: $scope.filterData.filter, + memberType: $scope.filterData.memberType, + memberGroups: processFilterList(displaySearch, "Member Groups", $scope.memberGroupFilter), + f_umbracoMemberApproved: !$scope.filterData.f_umbracoMemberApproved ? "" : $scope.filterData.f_umbracoMemberApproved, + f_umbracoMemberLockedOut: !$scope.filterData.f_umbracoMemberLockedOut ? "" : $scope.filterData.f_umbracoMemberLockedOut, + + display: displaySearch + }; + }; + + function _getMemberTypeName() { + + var type = _.filter($scope.memberTypes, function (item) { + return item.alias === $scope.filterData.memberType; + }); + + return _.map(type, function (item) { + return ' ' + item.name; + }).join(); + + } + + // Loop through a list of select options and set selected for values that appear in a filter list + setSelected = function (list, filter) { + // Convert string arrays to a select item object. + list = _.map(list, function (item) { + if (typeof item === "string") { + let value = item; + item = { + id: value.replace(" ", "_"), + name: value, + alias: value, + selected: false + }; + } + return item; + }); + + if (filter) { + for (var i = 0; i < filter.length; i++) { + for (var j = 0; j < list.length; j++) { + if (list[j].id === filter[i].replace(" ", "_")) { + list[j].selected = true; + } + } + } + } + + return list; + }; + + + processFilterList = function (display, title, list) { + var filteredList = _.filter(list, function (i) { + return i.selected; + }); + + var displayVal = _.map(filteredList, function (item) { + return ' ' + item.name; + }).join(); + + if (displayVal) { + display.push({ title: title, value: displayVal }); + } + return _.map(filteredList, function (item) { + return item.alias; + }); + }; + + $scope.applyFilter = function () { + $scope.submit(getFilterModel()); + }; + + $scope.clearFilter = function () { + $scope.filterData = { + filter: null + }; + $scope.submit($scope.filterData); + }; + + // this method is called for all action buttons and then we proxy based on the btn definition + $scope.performAction = function (btn) { + + if (!btn || !angular.isFunction(btn.handler)) { + throw "btn.handler must be a function reference"; + } + + btn.handler.apply(this); + }; + + + init(); + }); \ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.html b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.html new file mode 100644 index 0000000..9eaf774 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/backoffice/dialogs/member/filter.html @@ -0,0 +1,95 @@ +
+ +
+
+

+ Filter +

+
+ + + +
+
\ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/css/memberListView.css b/WebCms/App_Plugins/MemberManager/css/memberListView.css new file mode 100644 index 0000000..f975bd9 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/css/memberListView.css @@ -0,0 +1,61 @@ +.umb-listview.membermanager a { + text-decoration: none; } + +.umb-listview.membermanager .umb-table__name { + -ms-flex: 1 1 5%; + flex: 1 1 5%; + max-width: 20%; } + +.umb-listview.membermanager .no-results { + padding: 20px; + background-color: #f8f8f8; + border: 1px solid #dbdbdb; + text-align: center; + color: #999; + margin-top: 20px; } + +.filter-display { + padding: 1em; + background-color: #f8f8f8; + border: 1px solid #dbdbdb; + margin-bottom: 1em; } + .filter-display > div { + overflow: auto; + margin-bottom: 5px; } + .filter-display > div:last-child { + margin-bottom: 0; } + .filter-display > div strong { + float: left; + width: 180px; + display: block; } + .filter-display > div span { + float: left; + width: auto; } + +.filter-options .filter-display { + margin-bottom: 0; } + +.filter-options .filter-checkbox-list { + margin-bottom: 1em; } + .filter-options .filter-checkbox-list .filter-content { + max-height: 10.4em; + overflow-y: scroll; + border: solid 1px #eee; + border-radius: .25em; } + .filter-options .filter-checkbox-list .filter-content, .filter-options .filter-checkbox-list .filter-header { + padding: .5em; } + .filter-options .filter-checkbox-list .filter-content > div, .filter-options .filter-checkbox-list .filter-header > div { + overflow: auto; } + .filter-options .filter-checkbox-list .filter-content > div input[type="checkbox"], .filter-options .filter-checkbox-list .filter-header > div input[type="checkbox"] { + float: left; + /*margin-top: 10px;*/ + margin-right: 10px; } + .filter-options .filter-checkbox-list .filter-content > div label.control-label, .filter-options .filter-checkbox-list .filter-header > div label.control-label { + float: left; + margin: 0; + padding: 0; + width: auto; + width: 90%; + font-weight: 400; } + .filter-options .filter-checkbox-list .filter-header { + margin-bottom: .5em; } diff --git a/WebCms/App_Plugins/MemberManager/lang/cs.xml b/WebCms/App_Plugins/MemberManager/lang/cs.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/cs.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/da.xml b/WebCms/App_Plugins/MemberManager/lang/da.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/da.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/de.xml b/WebCms/App_Plugins/MemberManager/lang/de.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/de.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/en-gb.xml b/WebCms/App_Plugins/MemberManager/lang/en-gb.xml new file mode 100644 index 0000000..4aa7515 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/en-gb.xml @@ -0,0 +1,43 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Export + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/en-us.xml b/WebCms/App_Plugins/MemberManager/lang/en-us.xml new file mode 100644 index 0000000..6d9c39c --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/en-us.xml @@ -0,0 +1,43 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Export + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/es.xml b/WebCms/App_Plugins/MemberManager/lang/es.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/es.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/fr.xml b/WebCms/App_Plugins/MemberManager/lang/fr.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/fr.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/he.xml b/WebCms/App_Plugins/MemberManager/lang/he.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/he.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/it.xml b/WebCms/App_Plugins/MemberManager/lang/it.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/it.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/ja.xml b/WebCms/App_Plugins/MemberManager/lang/ja.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/ja.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/ko.xml b/WebCms/App_Plugins/MemberManager/lang/ko.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/ko.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/nb.xml b/WebCms/App_Plugins/MemberManager/lang/nb.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/nb.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/nl.xml b/WebCms/App_Plugins/MemberManager/lang/nl.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/nl.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/pl.xml b/WebCms/App_Plugins/MemberManager/lang/pl.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/pl.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/pt.xml b/WebCms/App_Plugins/MemberManager/lang/pt.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/pt.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/ru.xml b/WebCms/App_Plugins/MemberManager/lang/ru.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/ru.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/sv.xml b/WebCms/App_Plugins/MemberManager/lang/sv.xml new file mode 100644 index 0000000..e28a187 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/sv.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/tr.xml b/WebCms/App_Plugins/MemberManager/lang/tr.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/tr.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/zh-tw.xml b/WebCms/App_Plugins/MemberManager/lang/zh-tw.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/zh-tw.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/lang/zh.xml b/WebCms/App_Plugins/MemberManager/lang/zh.xml new file mode 100644 index 0000000..9729a05 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/lang/zh.xml @@ -0,0 +1,42 @@ + + + + Robert Foster + + + + Approved + Locked + Groups + + + Approve + Suspend + Apply Filter + Clear Filter + + + Unlocking %0% Member + Unlocking %0% of %1% Members + Unlocked %0% Member + Unlocked %0% Members + Suspending %0% Member + Suspending %0% of %1% Members + Suspended %0% Member + Suspended %0% Members + Approving %0% Member + Approving %0% of %1% Members + Approved %0% Member + Approved %0% Members + + + Filter + Clear Filter + Export Members + Apply Filter + Exporting large numbers of members may take some time. Are you sure you want to continue? + Are you sure you want to unlock the selected members? + Are you sure you want to suspend the selected members? + Are you sure you want to approve the selected members? + + diff --git a/WebCms/App_Plugins/MemberManager/package.manifest b/WebCms/App_Plugins/MemberManager/package.manifest new file mode 100644 index 0000000..6882e0b --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/package.manifest @@ -0,0 +1,12 @@ +{ + javascript: [ + "~/App_Plugins/MemberManager/resources/member.resource.js", + "~/App_Plugins/MemberManager/backoffice/dashboard/memberListView.controller.js", + "~/App_Plugins/MemberManager/backoffice/dialogs/member/edit.controller.js", + "~/App_Plugins/MemberManager/backoffice/dialogs/member/filter.controller.js", + "~/App_Plugins/MemberManager/backoffice/dialogs/member/export.controller.js" + ], + css : [ + "~/App_Plugins/MemberManager/css/memberListView.css" + ] +} \ No newline at end of file diff --git a/WebCms/App_Plugins/MemberManager/resources/member.resource.js b/WebCms/App_Plugins/MemberManager/resources/member.resource.js new file mode 100644 index 0000000..40257e5 --- /dev/null +++ b/WebCms/App_Plugins/MemberManager/resources/member.resource.js @@ -0,0 +1,204 @@ +/** + * @ngdoc service + * @name umbraco.resources.memberExtResource + * + * @description Member Management + * + * @returns {umbraco.resources.memberExtResource} memberExtResource + * + * @param {any} $http Http Service + * @param {any} $window Window + * @param {any} umbRequestHelper Umbraco Request Helper + **/ +function memberExtResource($http, $window, umbRequestHelper) { + var memberExtResource = { + approveById: function (id) { + if (!id) { + throw "id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post("Backoffice/MemberManager/MemberApi/PostApprove?" + + umbRequestHelper.dictionaryToQueryString( + [{ id: id }])), + 'Failed to approve member with id ' + id); + }, + suspendById: function (id) { + if (!id) { + throw "id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post("Backoffice/MemberManager/MemberApi/PostSuspend?" + + umbRequestHelper.dictionaryToQueryString( + [{ id: id }])), + 'Failed to approve member with id ' + id); + }, + unlockById: function (id) { + if (!id) { + throw "id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post("Backoffice/MemberManager/MemberApi/PostUnlock?" + + umbRequestHelper.dictionaryToQueryString( + [{ id: id }])), + 'Failed to approve member with id ' + id); + }, + getMembers: function (options) { + + var defaults = { + pageSize: 0, + pageNumber: 0, + filterData: { filter: null }, + orderDirection: "Ascending", + orderBy: "SortOrder" + }; + if (options === undefined) { + options = {}; + } + //overwrite the defaults if there are any specified + angular.extend(defaults, options); + //now copy back to the options we will use + options = defaults; + //change asc/desc + if (options.orderDirection === "asc") { + options.orderDirection = "Ascending"; + } + else if (options.orderDirection === "desc") { + options.orderDirection = "Descending"; + } + + // Create the querystring dictionary + var querystring = _filterToDictionary(options.filterData); + querystring.push({ pageNumber: options.pageNumber }); + querystring.push({ pageSize: options.pageSize }); + querystring.push({ orderBy: options.orderBy }); + querystring.push({ orderDirection: options.orderDirection }); + + return umbRequestHelper.resourcePromise( + $http.get("Backoffice/MemberManager/MemberApi/GetMembers?" + + umbRequestHelper.dictionaryToQueryString(querystring)), + 'Failed to retrieve members'); + }, + getMemberExport: function (options) { + + var defaults = { + filterData: { filter: null }, + orderDirection: "Ascending", + orderBy: "SortOrder", + format: "Excel", + columns: null + }; + if (options === undefined) { + options = {}; + } + //overwrite the defaults if there are any specified + angular.extend(defaults, options); + + //now copy back to the options we will use + options = defaults; + + //change asc/desc + if (options.orderDirection === "asc") { + options.orderDirection = "Ascending"; + } + else if (options.orderDirection === "desc") { + options.orderDirection = "Descending"; + } + + // Create the querystring dictionary + var querystring = _filterToDictionary(options.filterData); + querystring.push({ orderBy: options.orderBy }); + querystring.push({ orderDirection: options.orderDirection }); + querystring.push({ format: options.format }); + querystring.push({ columns: options.columns }); + + var config = { responseType: 'blob' }; + + // Solution taken from http://jaliyaudagedara.blogspot.com/2016/05/angularjs-download-files-by-sending.html + $http.get("Backoffice/MemberManager/MemberApi/GetMembersExport?" + umbRequestHelper.dictionaryToQueryString(querystring), + config).success(function (data, status, headers) { + headers = headers(); + try { + var filename = headers['x-filename']; + + if (!filename) { + var result = headers['content-disposition'].split(';')[1].trim().split('=')[1]; + filename = result.replace(/"/g, ''); + } + + var contentType = headers['content-type']; + + var linkElement = document.createElement('a'); + + var blob = new Blob([data], { type: contentType }); + var url = window.URL.createObjectURL(blob); + + linkElement.setAttribute('href', url); + linkElement.setAttribute("download", filename); + + var clickEvent = new MouseEvent("click", { + "view": window, + "bubbles": true, + "cancelable": false + }); + linkElement.dispatchEvent(clickEvent); + } catch (ex) { + console.log(ex); + } + + }).error(function (data) { + console.log(data); + }); + }, + getMemberGroups: function () { + return umbRequestHelper.resourcePromise( + $http.get("Backoffice/MemberManager/MemberApi/GetMemberGroups"), + 'Failed to retrieve groups'); + }, + getMemberColumns: function (memberType) { + if (memberType === undefined || memberType === null) { + return umbRequestHelper.resourcePromise( + $http.get("Backoffice/MemberManager/MemberApi/GetMemberColumns"), + 'Failed to retrieve groups'); + } else { + return umbRequestHelper.resourcePromise( + $http.get("Backoffice/MemberManager/MemberApi/GetMemberColumns?memberType=" + memberType), + 'Failed to retrieve groups'); + } + }, + canExport: function () { + return umbRequestHelper.resourcePromise( + $http.get("Backoffice/MemberManager/MemberApi/GetCanExport"), + false); + } + }; + + function _filterToDictionary(filter) { + if (!filter) + return; + var dict = []; + + for (prop in filter) { + if (filter.hasOwnProperty(prop) && + filter[prop] && + (prop.startsWith('f_') || prop === 'filter' || prop === 'memberType' || prop === 'memberGroups') && + filter[prop].length > 0) { + + // Add a new dictionary entry. + var entry = {}; + entry[prop] = filter[prop]; + dict.push(entry); + + } + } + + return dict; + + } + + return memberExtResource; +} + +angular.module('umbraco.resources').factory('memberExtResource', memberExtResource); diff --git a/WebCms/Config/ClientDependency.config b/WebCms/Config/ClientDependency.config index 16400b3..08f9584 100644 --- a/WebCms/Config/ClientDependency.config +++ b/WebCms/Config/ClientDependency.config @@ -10,7 +10,7 @@ NOTES: * Compression/Combination/Minification is not enabled unless debug="false" is specified on the 'compiliation' element in the web.config * A new version will invalidate both client and server cache and create new persisted files --> - +