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"; 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); 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) { controller($scope, modelManager) {
$scope.formattedPermissions = $scope.permissions.map(function(permission){ $scope.permissionsString = function() {
if(permission.name === "stream-items") { var finalString = "";
var types = permission.content_types.map(function(type){ let permissionsCount = $scope.permissions.length;
var desc = modelManager.humanReadableDisplayForContentType(type);
if(desc) {
return desc + "s";
} else {
return "items of type " + type;
}
})
var typesString = "";
var separator = ", ";
for(var i = 0;i < types.length;i++) { let addSeparator = (index, length) => {
var type = types[i]; if(index > 0) {
if(i == 0) { if(index == length - 1) {
// first element if(length == 2) {
typesString = typesString + type; return " and ";
} else if(i == types.length - 1) { } else {
// last element return ", and "
if(types.length > 2) {
typesString += separator + "and " + type;
} else if(types.length == 2) {
typesString = typesString + " and " + type;
} }
} else { } else {
typesString += separator + type; return ", ";
} }
} }
return typesString; return "";
} 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];
} }
})
$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 get-context-client-data
install-local-component install-local-component
toggle-activate-component toggle-activate-component
request-permissions
*/ */
if(message.action === "stream-items") { if(message.action === "stream-items") {
@@ -252,6 +253,8 @@ class ComponentManager {
} else if(message.action === "toggle-activate-component") { } else if(message.action === "toggle-activate-component") {
let componentToToggle = this.modelManager.findItem(message.data.uuid); let componentToToggle = this.modelManager.findItem(message.data.uuid);
this.handleToggleComponentMessage(component, componentToToggle, message); this.handleToggleComponentMessage(component, componentToToggle, message);
} else if(message.action === "request-permissions") {
this.handleRequestPermissionsMessage(component, message);
} }
// Notify observers // 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) { handleSetComponentDataMessage(component, message) {
// A component setting its own data does not require special permissions // A component setting its own data does not require special permissions
this.runWithPermissions(component, [], () => { this.runWithPermissions(component, [], () => {
@@ -459,7 +468,6 @@ class ComponentManager {
}); });
} }
handleToggleComponentMessage(sourceComponent, targetComponent, message) { handleToggleComponentMessage(sourceComponent, targetComponent, message) {
if(targetComponent.area == "modal") { if(targetComponent.area == "modal") {
this.openModalComponent(targetComponent); this.openModalComponent(targetComponent);
@@ -504,7 +512,7 @@ class ComponentManager {
matching = acquiredPermissions.find((candidate) => { matching = acquiredPermissions.find((candidate) => {
return Array.isArray(candidate.content_types) return Array.isArray(candidate.content_types)
&& Array.isArray(required.content_types) && Array.isArray(required.content_types)
&& candidate.content_types.containsSubset(required.content_types); && candidate.content_types.containsPrimitiveSubset(required.content_types);
}); });
if(!matching) { if(!matching) {
@@ -514,8 +522,6 @@ class ComponentManager {
} }
} }
// var acquiredMatchesRequested = angular.toJson(component.permissions.sort()) === angular.toJson(requestedPermissions.sort());
if(!acquiredMatchesRequired) { if(!acquiredMatchesRequired) {
this.promptForPermissions(component, requiredPermissions, function(approved){ this.promptForPermissions(component, requiredPermissions, function(approved){
if(approved) { if(approved) {
@@ -528,9 +534,6 @@ class ComponentManager {
} }
promptForPermissions(component, permissions, callback) { 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); var scope = this.$rootScope.$new(true);
scope.component = component; scope.component = component;
scope.permissions = permissions; scope.permissions = permissions;
@@ -547,28 +550,47 @@ class ComponentManager {
this.syncManager.sync(); this.syncManager.sync();
} }
for(var existing of this.permissionDialogs) { this.permissionDialogs = this.permissionDialogs.filter((pendingDialog) => {
if(existing.component === component && existing.actionBlock) { // Remove self
existing.actionBlock(approved); if(pendingDialog == scope) {
return false;
} }
} if(approved && pendingDialog.component == component) {
// remove pending dialogs that are encapsulated by already approved permissions, and run its function
this.permissionDialogs = this.permissionDialogs.filter(function(dialog){ if(pendingDialog.permissions == permissions || permissions.containsObjectSubset(pendingDialog.permissions)) {
return dialog.component !== component; pendingDialog.actionBlock && pendingDialog.actionBlock(approved);
return false;
}
}
return true;
}) })
if(this.permissionDialogs.length > 0) {
this.presentDialog(this.permissionDialogs[0]);
}
}.bind(this); }.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); this.permissionDialogs.push(scope);
if(!existingDialog) { if(!existingDialog) {
var el = this.$compile( "<permissions-modal component='component' permissions='permissions' callback='callback' class='modal'></permissions-modal>" )(scope); this.presentDialog(scope);
angular.element(document.body).append(el);
} else { } else {
console.log("Existing dialog, not presenting."); 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) { openModalComponent(component) {
var scope = this.$rootScope.$new(true); var scope = this.$rootScope.$new(true);
scope.component = component; scope.component = component;

View File

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