Permissions formatting, dialog queue

This commit is contained in:
Mo Bitar
2018-01-21 13:14:15 -06:00
parent 12ada8eb00
commit 614d7ba20a
4 changed files with 101 additions and 52 deletions

View File

@@ -41,6 +41,12 @@ function isMacApplication() {
return window && window.process && window.process.type && window.process.platform == "darwin";
}
Array.prototype.containsSubset = function(array) {
/* Use with numbers and strings, not objects */
Array.prototype.containsPrimitiveSubset = function(array) {
return !array.some(val => this.indexOf(val) === -1);
}
/* Use with numbers and strings, not objects */
Array.prototype.containsObjectSubset = function(array) {
return !array.some(val => !_.find(this, val));
}

View File

@@ -30,46 +30,68 @@ class PermissionsModal {
controller($scope, modelManager) {
$scope.formattedPermissions = $scope.permissions.map(function(permission){
if(permission.name === "stream-items") {
var types = permission.content_types.map(function(type){
var desc = modelManager.humanReadableDisplayForContentType(type);
if(desc) {
return desc + "s";
} else {
return "items of type " + type;
}
})
var typesString = "";
var separator = ", ";
$scope.permissionsString = function() {
var finalString = "";
let permissionsCount = $scope.permissions.length;
for(var i = 0;i < types.length;i++) {
var type = types[i];
if(i == 0) {
// first element
typesString = typesString + type;
} else if(i == types.length - 1) {
// last element
if(types.length > 2) {
typesString += separator + "and " + type;
} else if(types.length == 2) {
typesString = typesString + " and " + type;
let addSeparator = (index, length) => {
if(index > 0) {
if(index == length - 1) {
if(length == 2) {
return " and ";
} else {
return ", and "
}
} else {
typesString += separator + type;
return ", ";
}
}
return typesString;
} else if(permission.name === "stream-context-item") {
var mapping = {
"editor-stack" : "working note",
"note-tags" : "working note",
"editor-editor": "working note"
}
return mapping[$scope.component.area];
return "";
}
})
$scope.permissions.forEach((permission, index) => {
if(permission.name === "stream-items") {
var types = permission.content_types.map(function(type){
var desc = modelManager.humanReadableDisplayForContentType(type);
if(desc) {
return desc + "s";
} else {
return "items of type " + type;
}
})
var typesString = "";
for(var i = 0;i < types.length;i++) {
var type = types[i];
typesString += addSeparator(i, types.length + permissionsCount - index - 1);
typesString += type;
}
finalString += addSeparator(index, permissionsCount);
finalString += typesString;
if(types.length >= 2 && index < permissionsCount - 1) {
// If you have a list of types, and still an additional root-level permission coming up, add a comma
finalString += ", ";
}
} else if(permission.name === "stream-context-item") {
var mapping = {
"editor-stack" : "working note",
"note-tags" : "working note",
"editor-editor": "working note"
}
finalString += addSeparator(index, permissionsCount, true);
finalString += mapping[$scope.component.area];
}
})
return finalString + ".";
}
}
}

View File

@@ -235,6 +235,7 @@ class ComponentManager {
get-context-client-data
install-local-component
toggle-activate-component
request-permissions
*/
if(message.action === "stream-items") {
@@ -252,6 +253,8 @@ class ComponentManager {
} else if(message.action === "toggle-activate-component") {
let componentToToggle = this.modelManager.findItem(message.data.uuid);
this.handleToggleComponentMessage(component, componentToToggle, message);
} else if(message.action === "request-permissions") {
this.handleRequestPermissionsMessage(component, message);
}
// Notify observers
@@ -450,6 +453,12 @@ class ComponentManager {
});
}
handleRequestPermissionsMessage(component, message) {
this.runWithPermissions(component, message.data.permissions, () => {
this.replyToMessage(component, message, {approved: true});
});
}
handleSetComponentDataMessage(component, message) {
// A component setting its own data does not require special permissions
this.runWithPermissions(component, [], () => {
@@ -459,7 +468,6 @@ class ComponentManager {
});
}
handleToggleComponentMessage(sourceComponent, targetComponent, message) {
if(targetComponent.area == "modal") {
this.openModalComponent(targetComponent);
@@ -504,7 +512,7 @@ class ComponentManager {
matching = acquiredPermissions.find((candidate) => {
return Array.isArray(candidate.content_types)
&& Array.isArray(required.content_types)
&& candidate.content_types.containsSubset(required.content_types);
&& candidate.content_types.containsPrimitiveSubset(required.content_types);
});
if(!matching) {
@@ -514,8 +522,6 @@ class ComponentManager {
}
}
// var acquiredMatchesRequested = angular.toJson(component.permissions.sort()) === angular.toJson(requestedPermissions.sort());
if(!acquiredMatchesRequired) {
this.promptForPermissions(component, requiredPermissions, function(approved){
if(approved) {
@@ -528,9 +534,6 @@ class ComponentManager {
}
promptForPermissions(component, permissions, callback) {
// since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result
var existingDialog = _.find(this.permissionDialogs, {component: component});
var scope = this.$rootScope.$new(true);
scope.component = component;
scope.permissions = permissions;
@@ -547,28 +550,47 @@ class ComponentManager {
this.syncManager.sync();
}
for(var existing of this.permissionDialogs) {
if(existing.component === component && existing.actionBlock) {
existing.actionBlock(approved);
this.permissionDialogs = this.permissionDialogs.filter((pendingDialog) => {
// Remove self
if(pendingDialog == scope) {
return false;
}
}
this.permissionDialogs = this.permissionDialogs.filter(function(dialog){
return dialog.component !== component;
if(approved && pendingDialog.component == component) {
// remove pending dialogs that are encapsulated by already approved permissions, and run its function
if(pendingDialog.permissions == permissions || permissions.containsObjectSubset(pendingDialog.permissions)) {
pendingDialog.actionBlock && pendingDialog.actionBlock(approved);
return false;
}
}
return true;
})
if(this.permissionDialogs.length > 0) {
this.presentDialog(this.permissionDialogs[0]);
}
}.bind(this);
// since these calls are asyncronous, multiple dialogs may be requested at the same time. We only want to present one and trigger all callbacks based on one modal result
var existingDialog = _.find(this.permissionDialogs, {component: component});
this.permissionDialogs.push(scope);
if(!existingDialog) {
var el = this.$compile( "<permissions-modal component='component' permissions='permissions' callback='callback' class='modal'></permissions-modal>" )(scope);
angular.element(document.body).append(el);
this.presentDialog(scope);
} else {
console.log("Existing dialog, not presenting.");
}
}
presentDialog(dialog) {
var permissions = dialog.permissions;
var component = dialog.component;
var callback = dialog.callback;
var el = this.$compile( "<permissions-modal component='component' permissions='permissions' callback='callback' class='modal'></permissions-modal>" )(dialog);
angular.element(document.body).append(el);
}
openModalComponent(component) {
var scope = this.$rootScope.$new(true);
scope.component = component;

View File

@@ -12,8 +12,7 @@
%h3
%strong {{component.name}}
would like to interact with your
%span{"ng-repeat" => "permission in formattedPermissions"}
{{permission}}.
{{permissionsString()}}
.panel-row
%p