diff --git a/app/assets/javascripts/app/app.js b/app/assets/javascripts/app/app.js
index d75836ddc..af4ac2d2e 100644
--- a/app/assets/javascripts/app/app.js
+++ b/app/assets/javascripts/app/app.js
@@ -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));
+}
diff --git a/app/assets/javascripts/app/directives/views/permissionsModal.js b/app/assets/javascripts/app/directives/views/permissionsModal.js
index fb7609d55..0e1764910 100644
--- a/app/assets/javascripts/app/directives/views/permissionsModal.js
+++ b/app/assets/javascripts/app/directives/views/permissionsModal.js
@@ -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 + ".";
+ }
}
}
diff --git a/app/assets/javascripts/app/services/componentManager.js b/app/assets/javascripts/app/services/componentManager.js
index 627ad5119..f28c8c4f0 100644
--- a/app/assets/javascripts/app/services/componentManager.js
+++ b/app/assets/javascripts/app/services/componentManager.js
@@ -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( "" )(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( "" )(dialog);
+ angular.element(document.body).append(el);
+ }
+
openModalComponent(component) {
var scope = this.$rootScope.$new(true);
scope.component = component;
diff --git a/app/assets/templates/directives/permissions-modal.html.haml b/app/assets/templates/directives/permissions-modal.html.haml
index d88280a3a..63a5dc38f 100644
--- a/app/assets/templates/directives/permissions-modal.html.haml
+++ b/app/assets/templates/directives/permissions-modal.html.haml
@@ -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