Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only see sensible depths of the access tree #1264

Closed
wants to merge 133 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
133 commits
Select commit Hold shift + click to select a range
d4ba4b6
Add the access model with some basic specs
kitallis Jul 28, 2020
798b3c3
WIP
kitallis Jul 28, 2020
1d3ef0d
Rename resource to scope
kitallis Jul 28, 2020
9839df3
Add a factory template for access
kitallis Jul 28, 2020
5338be1
Merge branch 'permissions-data-model' into permissions-access-methods
kitallis Jul 28, 2020
80bee5c
Add the three basic scoping methods
kitallis Jul 28, 2020
2b4b04b
Add more thorough validation specs
kitallis Jul 28, 2020
545a0bc
Merge branch 'permissions-data-model' into permissions-access-methods
kitallis Jul 28, 2020
03ba593
Rename ALLOWED_SCOPES to ALLOWED_RESOURCES
kitallis Jul 28, 2020
d6fdccd
Instead of inclusion:nil, use absence
kitallis Jul 29, 2020
d127930
Change mode to role
kitallis Jul 29, 2020
9406e31
Merge branch 'permissions-data-model' into permissions-access-methods
kitallis Jul 29, 2020
c6079ec
mode to role in factories
kitallis Jul 29, 2020
f21c824
Merge branch 'permissions-data-model' into permissions-access-methods
kitallis Jul 29, 2020
34ba637
Access.organizations specs
kitallis Jul 29, 2020
8fe631a
Access.organizations specs (pass 2)
kitallis Jul 29, 2020
69bc235
Rename mode to role in breaking spec
vkrmis Aug 3, 2020
8f35424
Merge branch 'permissions-data-model' into permissions-access-methods
vkrmis Aug 3, 2020
224f70a
Add a .can? method on User
vkrmis Aug 3, 2020
1e7b17e
Delegate user.can? to access model
vkrmis Aug 4, 2020
7ba5f87
Refactor the can? method for clarity and purpose
kitallis Aug 6, 2020
fdd518e
Add uniqueness constraint on user<>role
kitallis Aug 6, 2020
691d0cb
Add a validation for user to only have 1 role
kitallis Aug 6, 2020
4e5491b
Fix specs
kitallis Aug 6, 2020
7abeb1c
Specs to validate that user should only ever have one role thru Access
kitallis Aug 6, 2020
2482ea3
Minor aesthetic
kitallis Aug 6, 2020
4419d89
Add delegates to User for Access
kitallis Aug 6, 2020
a5f6210
Add specs to scope against FacilityGroups
kitallis Aug 6, 2020
344f119
Specs for Access.can?
kitallis Aug 6, 2020
8900c5a
Merge branch 'master' into permissions-access-methods
kitallis Aug 6, 2020
e9aa57a
Complete the pending Access spec
kitallis Aug 11, 2020
fdaccce
How it would look like if we moved the access_level to User
kitallis Aug 11, 2020
a7f32d8
Add react-checkbox-tree
kitallis Aug 12, 2020
1b99db9
Add a db constraint for access_level
kitallis Aug 13, 2020
75c8194
Get the build green
kitallis Aug 14, 2020
503749a
Add power_user validation in Access
kitallis Aug 14, 2020
91c1a81
Remove the power_user related specs from Access
kitallis Aug 14, 2020
05ece07
Wrap up the pending Access specs
kitallis Aug 14, 2020
b905b8e
Fix broken build
kitallis Aug 14, 2020
8c53a24
Merge branch 'master' into permissions-access-methods
kitallis Aug 17, 2020
7e9d5c3
Add a basic v2 of the InviteAdminForm
kitallis Aug 17, 2020
06d055e
Merge branch 'permissions-access-methods' into permissions-ui
kitallis Aug 17, 2020
592fafb
Fix bad merge
kitallis Aug 17, 2020
850ced1
Add checkbox and dropdowns to admin form
danySam Aug 17, 2020
4160776
Add jstree
kitallis Aug 17, 2020
45b465e
Add an InviteAdminPresenter layer to derive the access_tree for an admin
kitallis Aug 18, 2020
073da3c
Thanks standardrb
kitallis Aug 18, 2020
b8518c3
Add Facility Access nested and parent-aware checkboxes
kitallis Aug 18, 2020
572f862
Make the checkbox JS work with rails forms
kitallis Aug 19, 2020
a83f833
Change the datastructure for access_tree a bit
kitallis Aug 19, 2020
25bfa8a
Add some css
kitallis Aug 19, 2020
135757c
Rename input keys
kitallis Aug 19, 2020
53f96f2
On form submit, return the selected facilities
kitallis Aug 19, 2020
b1fad3f
Minor JS refactor
kitallis Aug 19, 2020
b2ad570
Add User#access_tree to eventually replace it in InviteAdminPresenter
kitallis Aug 19, 2020
688729d
Some notes from claudio
kitallis Aug 19, 2020
398c0c5
Add an access_control method for UI -> controller
kitallis Aug 19, 2020
80191aa
Add fuzzysort because I’d like to use this for search eventually
kitallis Aug 19, 2020
3ae4ea4
Move grant_access to User
kitallis Aug 20, 2020
cb7432e
Add spacer before checkboxes in tree
danySam Aug 20, 2020
e6ffd9b
Complete the basic, first horizontal invitation slice!
kitallis Aug 20, 2020
f388bfa
Make facility access card look like Figma design
danySam Aug 20, 2020
d72061e
Make facility access list collapsible
danySam Aug 20, 2020
bc7c7cf
Only allow grantable access_levels
kitallis Aug 20, 2020
a1e518b
Format new.html.erb
danySam Aug 21, 2020
5aa476c
Change hover color
danySam Aug 21, 2020
ccfe127
Merge branch 'permissions-ui' of https://github.com/simpledotorg/simp…
danySam Aug 21, 2020
4ac7060
Deprecate Presenter in favour of User#access_tree
kitallis Aug 21, 2020
ec74bfc
Save the user in a transaction
kitallis Aug 21, 2020
c82aa88
Better Access Level dropdowns
kitallis Aug 21, 2020
5dd89fb
Fix broken access level
kitallis Aug 21, 2020
a850629
Remove the unused React form
kitallis Aug 21, 2020
a7212f8
Merge branch 'master' into permissions-ui
kitallis Aug 21, 2020
7512692
Fix extra margin in CSS
danySam Aug 21, 2020
b03a6c3
Refactor grant_access
kitallis Aug 21, 2020
0f87863
Remove access-related bloat from User and move to Accessible
kitallis Aug 21, 2020
0749d23
Remove parent_id functions
kitallis Aug 21, 2020
1c178c4
Minor fixes
kitallis Aug 21, 2020
598e31a
Fix indeterminate state not being reset on top level check
danySam Aug 21, 2020
57ed207
Merge branch 'permissions-ui' of https://github.com/simpledotorg/simp…
danySam Aug 21, 2020
622b651
Cleanup invite_admin.js
danySam Aug 24, 2020
5d4a285
Code cleanup
danySam Aug 24, 2020
06f6849
Fix animation for item collapse
danySam Aug 24, 2020
45f620e
Remove the usage of JSON parsing in hidden input tag
danySam Aug 24, 2020
315a3d7
Move polyfills to separate JS file
danySam Aug 24, 2020
def2a09
Refactor: Cleanups names and functions calls
danySam Aug 24, 2020
730198e
Move all Access-related stuff to UserAccess and delegate from User
kitallis Aug 24, 2020
982fba3
Various refactors (sorry lazy to type this commit out)
kitallis Aug 25, 2020
59ce9ed
Various refactors (sorry lazy to type this commit out)
kitallis Aug 25, 2020
6ea60a4
Merge branch 'permissions-ui' of https://github.com/simpledotorg/simp…
danySam Aug 25, 2020
fd50227
Fix alignment issue in dropdown arrow
danySam Aug 25, 2020
7b34396
Use X / Y facilities language as in design
danySam Aug 25, 2020
201b48b
Merge branch 'master' into permissions-ui
kitallis Aug 25, 2020
be94c3a
Remove unused packages
kitallis Aug 25, 2020
5cfc0a0
Merge branch 'permissions-ui' of https://github.com/simpledotorg/simp…
danySam Aug 25, 2020
0fd8d26
Fix background and hover colors
danySam Aug 25, 2020
a3f9eda
Remove unused packages
kitallis Aug 25, 2020
8375e08
Small JS refactor
danySam Aug 25, 2020
739fb8c
Merge branch 'permissions-ui' of https://github.com/simpledotorg/simp…
danySam Aug 25, 2020
017e551
UI: Move dropdown button before text
danySam Aug 25, 2020
48a090b
Pull out the access_tree into a partial
kitallis Aug 25, 2020
a009653
Pull out some generic admin access view functions
kitallis Aug 25, 2020
8349963
Minor css reformat
kitallis Aug 25, 2020
0d35a98
Remove unnecessary changes
kitallis Aug 25, 2020
aaa567e
Refactor some JS and load it from within invite_admin.js
kitallis Aug 25, 2020
3a5c764
Show only total number of facilties if user has access to all
danySam Aug 25, 2020
4104f41
UI: Yellow background for items only when expanded
danySam Aug 25, 2020
cde3340
Minor refactor
kitallis Aug 25, 2020
efbfd71
Add specs for permitted_access_levels
kitallis Aug 25, 2020
b85c077
Minor refactor in InvitationsController
kitallis Aug 25, 2020
f571d0b
Add specs for UserAccess#access_tree
kitallis Aug 25, 2020
8993677
Add specs for UserAccess#grant_access
kitallis Aug 25, 2020
5014047
Add parallel methods for the EmailAuth -> Invite flow
kitallis Aug 26, 2020
bc0dde2
Remove yellow background from leaf facility item nodes
danySam Aug 26, 2020
1ff6e9b
Fix dropdown arrow not rotating
danySam Aug 26, 2020
5cac8b4
Add comments about the parallel methods
kitallis Aug 26, 2020
942fbcb
Thanks standard
kitallis Aug 26, 2020
c1c3846
Merge branch 'master' into permissions-ui
kitallis Aug 26, 2020
663be2c
The Add New Admin button is flipper flag aware
kitallis Aug 26, 2020
fcc3ca7
Pull the parallel dark launch methods back up into standard controllers
kitallis Aug 26, 2020
b12ec79
Remove the form submission checkbox hack and Just Use HTML™
kitallis Aug 26, 2020
5d4010e
Allow editing an existing admin
kitallis Aug 26, 2020
9f294a3
Minor JS cleanup
danySam Aug 27, 2020
5bbb361
Move dropdown arrow to the beginning of the checkbox item
danySam Aug 28, 2020
2d447c1
Fix dropdown toggle for collapse/expand
danySam Aug 28, 2020
75879e4
Remove extraneous JS
kitallis Aug 30, 2020
e075a59
Make the access tree more efficient
kitallis Aug 30, 2020
0809b7b
Delegate access_tree straight off the User
kitallis Aug 30, 2020
d188b7a
Make UserAccess#grant_access more efficient
kitallis Aug 30, 2020
b3db69e
Merge branch 'make-grant-access-more-efficient' into permissions-edit-ui
kitallis Aug 30, 2020
2c522e7
Add selective tree depth based on your access tree
kitallis Aug 30, 2020
10428cd
Simplify logic to collapse checkbox tree
danySam Sep 1, 2020
f22d5a3
Refactor containsClass logic
danySam Sep 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ gem "uuidtools", require: false
gem "view_component"
gem "whenever", require: false
gem "wkhtmltoimage-binary"
gem "memery"

group :development, :test do
gem "byebug", platforms: [:mri, :mingw, :x64_mingw]
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ GEM
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
memery (1.3.0)
ruby2_keywords (~> 0.0.2)
method_source (1.0.0)
mimemagic (0.3.5)
mini_mime (1.0.2)
Expand Down Expand Up @@ -435,6 +437,7 @@ GEM
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.10.1)
ruby2_keywords (0.0.2)
ruby_dep (1.5.0)
rubyzip (2.3.0)
safe_yaml (1.0.5)
Expand Down Expand Up @@ -580,6 +583,7 @@ DEPENDENCIES
launchy
listen (>= 3.0.5, < 3.2)
lodash-rails
memery
newrelic_rpm
parallel_tests
passenger
Expand Down
138 changes: 138 additions & 0 deletions app/assets/javascripts/invite_admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// loads at page refresh
//
window.addEventListener("DOMContentLoaded", inviteAdmin);
window.addEventListener("DOMContentLoaded", editAdmin);

function inviteAdmin() {
selectAllListener()
checkboxItemListener()
resourceRowCollapseListener()
}

function editAdmin() {
const SELECTOR = "input.access-input"
const facilityAccessDiv = document.getElementById("facility-access")

// list of all checkboxes under facilityAccessDiv
const checkboxes = nodeListToArray(SELECTOR, facilityAccessDiv)
const checkedCheckboxes = checkboxes.filter(check => check.checked)

for (let checkbox of checkedCheckboxes) {
updateParentCheckedState(checkbox, SELECTOR)
}
}

//
// listeners
//
function selectAllListener() {
const selectAllDiv = document.getElementById("select_all_facilities")
const SELECTOR = "input.access-input"
const facilityAccessDiv = document.getElementById("facility-access")
const checkboxes = nodeListToArray(SELECTOR, facilityAccessDiv)

selectAllDiv.addEventListener("change", _ => {
if (selectAllDiv.checked) {
for (let checkbox of checkboxes) {
checkbox.checked = true
}
} else {
for (let checkbox of checkboxes) {
checkbox.checked = false
}
}
})
}

function checkboxItemListener() {
const SELECTOR = "input.access-input"
const facilityAccessDiv = document.getElementById("facility-access")

// list of all checkboxes under facilityAccessDiv
const checkboxes = nodeListToArray(SELECTOR, facilityAccessDiv)

addEventListener("change", e => {
const targetCheckbox = e.target

// exit if change event did not come from list of checkboxes
if (checkboxes.indexOf(targetCheckbox) === -1) return
updateChildrenCheckedState(targetCheckbox, SELECTOR)
updateParentCheckedState(targetCheckbox, SELECTOR)
})
}

function resourceRowCollapseListener() {
const facilityAccessItems = document.getElementsByClassName("access-ratio")

for (const item of facilityAccessItems) {
item.addEventListener("click", onFacilityAccessItemToggled)
}
}

//
// behaviour
//

function toggleItemCollapsed(element) {
const collapsed = element.classList.contains("collapsed")

if (collapsed) {
element.classList.remove("collapsed")
} else {
element.classList.add("collapsed")
}
}

function onFacilityAccessItemToggled({ target }) {
const children = Array.from(target.closest("li").childNodes)
const parentItem = target.closest(".access-item")
const wrapper = children.find(containsClass("access-item-wrapper"))

if (wrapper) {
toggleItemCollapsed(parentItem)
}
}

function updateParentCheckedState(element, selector) {
// find parent and sibling checkboxes
const parent = (element.closest(["ul"]).parentNode).querySelector(selector)
const siblings = nodeListToArray(selector, parent.closest("li").querySelector(["ul"]))

// get checked state of siblings
// are every or some siblings checked (using Boolean as test function)
const checkStatus = siblings.map(check => check.checked)
const every = checkStatus.every(Boolean)
const some = checkStatus.some(Boolean)

// check parent if all siblings are checked
// set indeterminate if not all and not none are checked
parent.checked = every
parent.indeterminate = some && !every

// recurse until check is the top most parent
if (element !== parent) updateParentCheckedState(parent, selector)
}

function updateChildrenCheckedState(parent, selector) {
// check/uncheck children (includes check itself)
const children = nodeListToArray(selector, parent.closest("li"))

children.forEach(child => {
// reset indeterminate state for children
child.indeterminate = false
child.checked = parent.checked
})
}

//
// helpers
//

// helper function to create nodeArrays (not collections)
const nodeListToArray = (selector, parent = document) =>
[].slice.call(parent.querySelectorAll(selector))

// returns a function that checks if element contains class
const containsClass = (className) => ({ classList }) =>
classList && classList.contains(className)
31 changes: 31 additions & 0 deletions app/assets/javascripts/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Element.remove(), CharacterData.remove(), DocumentType.remove()
(function (arr) {
arr.forEach(function (item) {
if (item.hasOwnProperty('remove')) {
return;
}
Object.defineProperty(item, 'remove', {
configurable: true,
enumerable: true,
writable: true,
value: function remove() {
this.parentNode.removeChild(this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

// Element.closest()
if (window.Element && !Element.prototype.closest) {
Element.prototype.closest =
function (s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i,
el = this;
do {
i = matches.length;
while (--i >= 0 && matches.item(i) !== el) { };
} while ((i < 0) && (el = el.parentElement));
return el;
};
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@import "partials/snapshots";
@import "partials/search";
@import "partials/reports";
@import "partials/invite_admin";

#help * {
scroll-behavior: smooth;
Expand Down
122 changes: 122 additions & 0 deletions app/assets/stylesheets/partials/_invite_admin.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
@mixin transition($duration) {
-webkit-transition: $duration;
-moz-transition: $duration;
-ms-transition: $duration;
-o-transition: $duration;
transition: $duration;
}

#facility-access {
.card {
&.access-tree {
padding: 0px;
min-height: 460px;
}
}

.select-all-access-items {
height: 100%;
display: flex;
padding: 5px 0 5px 0;

.form-check {
margin: 10px;

label {
font-weight: 100;
}
}
}

ul {
list-style-type: none;
display: block;
height: 100%;
padding-left: 0px;
margin: 0px;

li {
height: 100%;
padding: 0px;
border-top: 1px solid var(--light);
position: relative;
margin-bottom: -1px;
}

.access-item {
height: 100%;
display: flex;

&.organization {
div {
label {
font-weight: 600;
}
}
}

&.collapsed {
.dropdown {
i {
transform: rotate(-90deg);
@include transition(0.3s);
}
}

& + .access-item-wrapper {
height: 0px;
overflow: hidden;
@include transition(0.1s);
}
}

&:not(.collapsed) {
background: #fff8e0;
}

&:hover {
background: var(--light);
@include transition(0.1s);
}

.spacer {
padding-left: 20px;
}

.form-check {
margin: 10px;

label {
font-weight: 100;
}
}

.dropdown {
padding: 10px;
margin-left: 5px;
margin-right: -20px;

i {
transform: rotate(0deg);
margin: 0px 8px 2px 8px;
@include transition(0.2s);
pointer-events: none;
}

&.hidden {
visibility: hidden;
}
}

.access-ratio {
padding: 12px;
margin-left: auto;
font-size: 12px;
color: var(--secondary);
font-weight: normal;
font-style: normal;
cursor: pointer;
}
}
}
}