Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
9c38fec6
Commit
9c38fec6
authored
Apr 11, 2018
by
Winnie Hellmann
Committed by
Clement Ho
Apr 11, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Shorten protected branch / tag access level dropdown text
parent
c79679f6
Changes
20
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
300 additions
and
607 deletions
+300
-607
app/assets/stylesheets/pages/projects.scss
app/assets/stylesheets/pages/projects.scss
+0
-5
app/views/projects/protected_branches/shared/_branches_list.html.haml
...ojects/protected_branches/shared/_branches_list.html.haml
+1
-1
app/views/projects/protected_tags/shared/_tags_list.html.haml
...views/projects/protected_tags/shared/_tags_list.html.haml
+1
-1
ee/app/assets/javascripts/projects/settings/access_dropdown.js
...p/assets/javascripts/projects/settings/access_dropdown.js
+56
-41
ee/app/assets/javascripts/projects/settings/constants.js
ee/app/assets/javascripts/projects/settings/constants.js
+13
-0
ee/app/assets/javascripts/protected_branches/protected_branch_create.js
...javascripts/protected_branches/protected_branch_create.js
+11
-7
ee/app/assets/javascripts/protected_branches/protected_branch_edit.js
...s/javascripts/protected_branches/protected_branch_edit.js
+33
-24
ee/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js
...vascripts/protected_tags/protected_tag_access_dropdown.js
+0
-476
ee/app/assets/javascripts/protected_tags/protected_tag_create.js
...assets/javascripts/protected_tags/protected_tag_create.js
+11
-7
ee/app/assets/javascripts/protected_tags/protected_tag_edit.js
...p/assets/javascripts/protected_tags/protected_tag_edit.js
+28
-20
ee/app/views/projects/protected_branches/ee/_access_level_dropdown.html.haml
...ts/protected_branches/ee/_access_level_dropdown.html.haml
+0
-9
ee/app/views/projects/protected_branches/ee/_protected_branch_access_summary.html.haml
...ed_branches/ee/_protected_branch_access_summary.html.haml
+2
-2
ee/app/views/projects/protected_tags/ee/_access_level_dropdown.html.haml
...ojects/protected_tags/ee/_access_level_dropdown.html.haml
+0
-9
ee/app/views/projects/protected_tags/ee/_protected_tag_access_summary.html.haml
...protected_tags/ee/_protected_tag_access_summary.html.haml
+1
-1
ee/app/views/projects/settings/ee/_access_level_dropdown.html.haml
...ews/projects/settings/ee/_access_level_dropdown.html.haml
+4
-0
ee/changelogs/unreleased/winh-protection-push-dropdowns.yml
ee/changelogs/unreleased/winh-protection-push-dropdowns.yml
+5
-0
ee/spec/support/ee/protected_branch_helpers.rb
ee/spec/support/ee/protected_branch_helpers.rb
+3
-1
spec/features/protected_branches_spec.rb
spec/features/protected_branches_spec.rb
+2
-2
spec/features/protected_tags_spec.rb
spec/features/protected_tags_spec.rb
+3
-1
spec/javascripts/ee/projects/settings/access_dropdown_spec.js
.../javascripts/ee/projects/settings/access_dropdown_spec.js
+126
-0
No files found.
app/assets/stylesheets/pages/projects.scss
View file @
9c38fec6
...
...
@@ -935,11 +935,6 @@ pre.light-well {
}
}
.dropdown-menu-toggle
{
width
:
100%
;
max-width
:
300px
;
}
.flash-container
{
padding
:
0
;
}
...
...
app/views/projects/protected_branches/shared/_branches_list.html.haml
View file @
9c38fec6
.p
anel.panel-default.p
rotected-branches-list.js-protected-branches-list
.protected-branches-list.js-protected-branches-list
-
if
@protected_branches
.
empty?
.panel-heading
%h3
.panel-title
...
...
app/views/projects/protected_tags/shared/_tags_list.html.haml
View file @
9c38fec6
.p
anel.panel-default.p
rotected-tags-list.js-protected-tags-list
.protected-tags-list.js-protected-tags-list
-
if
@protected_tags
.
empty?
.panel-heading
%h3
.panel-title
...
...
ee/app/assets/javascripts/pro
tected_branches/protected_branch_
access_dropdown.js
→
ee/app/assets/javascripts/pro
jects/settings/
access_dropdown.js
View file @
9c38fec6
/* eslint-disable no-underscore-dangle, class-methods-use-this */
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Flash
from
'
~/flash
'
;
import
{
n__
}
from
'
~/locale
'
;
import
{
LEVEL_TYPES
,
LEVEL_ID_PROP
,
ACCESS_LEVEL_NONE
}
from
'
./constants
'
;
export
default
class
ProtectedBranch
AccessDropdown
{
export
default
class
AccessDropdown
{
constructor
(
options
)
{
const
{
$dropdown
,
accessLevel
,
accessLevelsData
,
}
=
options
;
const
{
$dropdown
,
accessLevel
,
accessLevelsData
}
=
options
;
this
.
options
=
options
;
this
.
groups
=
[];
this
.
accessLevel
=
accessLevel
;
this
.
accessLevelsData
=
accessLevelsData
.
roles
;
this
.
$dropdown
=
$dropdown
;
this
.
$wrap
=
this
.
$dropdown
.
closest
(
`.
${
this
.
accessLevel
}
-container`
);
this
.
$protectedTagsContainer
=
$
(
'
.js-protected-branches-container
'
);
this
.
usersPath
=
'
/autocomplete/users.json
'
;
this
.
groupsPath
=
'
/autocomplete/project_groups.json
'
;
this
.
defaultLabel
=
this
.
$dropdown
.
data
(
'
defaultLabel
'
);
...
...
@@ -47,7 +42,7 @@ export default class ProtectedBranchAccessDropdown {
onHide
();
}
},
clicked
:
(
options
)
=>
{
clicked
:
options
=>
{
const
{
$el
,
e
}
=
options
;
const
item
=
options
.
selectedObj
;
...
...
@@ -56,7 +51,7 @@ export default class ProtectedBranchAccessDropdown {
if
(
$el
.
is
(
'
.is-active
'
))
{
if
(
item
.
id
===
this
.
noOneObj
.
id
)
{
// remove all others selected items
this
.
accessLevelsData
.
forEach
(
(
level
)
=>
{
this
.
accessLevelsData
.
forEach
(
level
=>
{
if
(
level
.
id
!==
item
.
id
)
{
this
.
removeSelectedItem
(
level
);
}
...
...
@@ -65,7 +60,9 @@ export default class ProtectedBranchAccessDropdown {
// remove selected item visually
this
.
$wrap
.
find
(
`.item-
${
item
.
type
}
`
).
removeClass
(
'
is-active
'
);
}
else
{
const
$noOne
=
this
.
$wrap
.
find
(
`.is-active.item-
${
item
.
type
}
[data-role-id="
${
this
.
noOneObj
.
id
}
"]`
);
const
$noOne
=
this
.
$wrap
.
find
(
`.is-active.item-
${
item
.
type
}
[data-role-id="
${
this
.
noOneObj
.
id
}
"]`
,
);
if
(
$noOne
.
length
)
{
$noOne
.
removeClass
(
'
is-active
'
);
this
.
removeSelectedItem
(
this
.
noOneObj
);
...
...
@@ -86,6 +83,8 @@ export default class ProtectedBranchAccessDropdown {
}
},
});
this
.
$dropdown
.
find
(
'
.dropdown-toggle-text
'
).
text
(
this
.
toggleLabel
());
}
persistPreselectedItems
()
{
...
...
@@ -95,7 +94,7 @@ export default class ProtectedBranchAccessDropdown {
return
;
}
const
persistedItems
=
itemsToPreselect
.
map
(
(
item
)
=>
{
const
persistedItems
=
itemsToPreselect
.
map
(
item
=>
{
const
persistedItem
=
Object
.
assign
({},
item
);
persistedItem
.
persisted
=
true
;
return
persistedItem
;
...
...
@@ -120,7 +119,7 @@ export default class ProtectedBranchAccessDropdown {
getInputData
()
{
const
selectedItems
=
this
.
getAllSelectedItems
();
const
accessLevels
=
selectedItems
.
map
(
(
item
)
=>
{
const
accessLevels
=
selectedItems
.
map
(
item
=>
{
const
obj
=
{};
if
(
typeof
item
.
id
!==
'
undefined
'
)
{
...
...
@@ -222,14 +221,11 @@ export default class ProtectedBranchAccessDropdown {
return
true
;
}
if
(
item
.
type
===
LEVEL_TYPES
.
USER
&&
item
.
user_id
===
itemToDelete
.
id
)
{
if
(
item
.
type
===
LEVEL_TYPES
.
USER
&&
item
.
user_id
===
itemToDelete
.
id
)
{
index
=
i
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
ROLE
&&
item
.
access_level
===
itemToDelete
.
id
)
{
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
ROLE
&&
item
.
access_level
===
itemToDelete
.
id
)
{
index
=
i
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
GROUP
&&
item
.
group_id
===
itemToDelete
.
id
)
{
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
GROUP
&&
item
.
group_id
===
itemToDelete
.
id
)
{
index
=
i
;
}
...
...
@@ -256,34 +252,48 @@ export default class ProtectedBranchAccessDropdown {
toggleLabel
()
{
const
currentItems
=
this
.
getSelectedItems
();
const
types
=
_
.
groupBy
(
currentItems
,
item
=>
item
.
type
);
let
label
=
[];
const
$dropdownToggleText
=
this
.
$dropdown
.
find
(
'
.dropdown-toggle-text
'
);
if
(
currentItems
.
length
)
{
label
=
Object
.
keys
(
LEVEL_TYPES
).
map
((
levelType
)
=>
{
const
typeName
=
LEVEL_TYPES
[
levelType
];
const
numberOfTypes
=
types
[
typeName
]
?
types
[
typeName
].
length
:
0
;
const
text
=
numberOfTypes
===
1
?
typeName
:
`
${
typeName
}
s`
;
if
(
currentItems
.
length
===
0
)
{
$dropdownToggleText
.
addClass
(
'
is-default
'
);
return
this
.
defaultLabel
;
}
return
`
${
numberOfTypes
}
${
text
}
`
;
});
}
else
{
label
.
push
(
this
.
defaultLabel
);
$dropdownToggleText
.
removeClass
(
'
is-default
'
);
if
(
currentItems
.
length
===
1
&&
currentItems
[
0
].
type
===
LEVEL_TYPES
.
ROLE
)
{
const
roleData
=
this
.
accessLevelsData
.
find
(
data
=>
data
.
id
===
currentItems
[
0
].
access_level
);
return
roleData
.
text
;
}
const
labelPieces
=
[];
const
counts
=
_
.
countBy
(
currentItems
,
item
=>
item
.
type
);
if
(
counts
[
LEVEL_TYPES
.
ROLE
]
>
0
)
{
labelPieces
.
push
(
n__
(
'
1 role
'
,
'
%d roles
'
,
counts
[
LEVEL_TYPES
.
ROLE
]));
}
if
(
counts
[
LEVEL_TYPES
.
USER
]
>
0
)
{
labelPieces
.
push
(
n__
(
'
1 user
'
,
'
%d users
'
,
counts
[
LEVEL_TYPES
.
USER
]));
}
this
.
$dropdown
.
find
(
'
.dropdown-toggle-text
'
).
toggleClass
(
'
is-default
'
,
!
currentItems
.
length
);
if
(
counts
[
LEVEL_TYPES
.
GROUP
]
>
0
)
{
labelPieces
.
push
(
n__
(
'
1 group
'
,
'
%d groups
'
,
counts
[
LEVEL_TYPES
.
GROUP
]));
}
return
label
.
join
(
'
,
'
);
return
label
Pieces
.
join
(
'
,
'
);
}
getData
(
query
,
callback
)
{
Promise
.
all
([
this
.
getUsers
(
query
),
this
.
groupsData
?
Promise
.
resolve
(
this
.
groupsData
)
:
this
.
getGroups
(),
]).
then
(([
usersResponse
,
groupsResponse
])
=>
{
])
.
then
(([
usersResponse
,
groupsResponse
])
=>
{
this
.
groupsData
=
groupsResponse
;
callback
(
this
.
consolidateData
(
usersResponse
.
data
,
groupsResponse
.
data
));
}).
catch
(()
=>
Flash
(
'
Failed to load groups & users.
'
));
})
.
catch
(()
=>
Flash
(
'
Failed to load groups & users.
'
));
}
consolidateData
(
usersResponse
,
groupsResponse
)
{
...
...
@@ -308,12 +318,15 @@ export default class ProtectedBranchAccessDropdown {
/*
* Build groups
*/
const
groups
=
groupsResponse
.
map
(
group
=>
({
...
group
,
type
:
LEVEL_TYPES
.
GROUP
}));
const
groups
=
groupsResponse
.
map
(
group
=>
({
...
group
,
type
:
LEVEL_TYPES
.
GROUP
,
}));
/*
* Build roles
*/
const
roles
=
this
.
accessLevelsData
.
map
(
(
level
)
=>
{
const
roles
=
this
.
accessLevelsData
.
map
(
level
=>
{
/* eslint-disable no-param-reassign */
// This re-assignment is intentional as
// level.type property is being used in removeSelectedItem()
...
...
@@ -327,7 +340,7 @@ export default class ProtectedBranchAccessDropdown {
/*
* Build users
*/
const
users
=
selectedItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
USER
).
map
(
(
item
)
=>
{
const
users
=
selectedItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
USER
).
map
(
item
=>
{
// Save identifiers for easy-checking more later
map
.
push
(
LEVEL_TYPES
.
USER
+
item
.
user_id
);
...
...
@@ -342,7 +355,7 @@ export default class ProtectedBranchAccessDropdown {
// Has to be checked against server response
// because the selected item can be in filter results
usersResponse
.
forEach
(
(
response
)
=>
{
usersResponse
.
forEach
(
response
=>
{
// Add is it has not been added
if
(
map
.
indexOf
(
LEVEL_TYPES
.
USER
+
response
.
id
)
===
-
1
)
{
const
user
=
Object
.
assign
({},
response
);
...
...
@@ -454,7 +467,9 @@ export default class ProtectedBranchAccessDropdown {
groupRowHtml
(
group
,
isActive
)
{
const
isActiveClass
=
isActive
||
''
;
const
avatarEl
=
group
.
avatar_url
?
`<img src="
${
group
.
avatar_url
}
" class="avatar avatar-inline" width="30">`
:
''
;
const
avatarEl
=
group
.
avatar_url
?
`<img src="
${
group
.
avatar_url
}
" class="avatar avatar-inline" width="30">`
:
''
;
return
`
<li>
...
...
ee/app/assets/javascripts/projects/settings/constants.js
0 → 100644
View file @
9c38fec6
export
const
LEVEL_TYPES
=
{
ROLE
:
'
role
'
,
USER
:
'
user
'
,
GROUP
:
'
group
'
,
};
export
const
LEVEL_ID_PROP
=
{
ROLE
:
'
access_level
'
,
USER
:
'
user_id
'
,
GROUP
:
'
group_id
'
,
};
export
const
ACCESS_LEVEL_NONE
=
0
;
ee/app/assets/javascripts/protected_branches/protected_branch_create.js
View file @
9c38fec6
...
...
@@ -3,8 +3,8 @@ import axios from '~/lib/utils/axios_utils';
import
AccessorUtilities
from
'
~/lib/utils/accessor
'
;
import
Flash
from
'
~/flash
'
;
import
CreateItemDropdown
from
'
~/create_item_dropdown
'
;
import
AccessDropdown
from
'
ee/projects/settings/access_dropdown
'
;
import
{
ACCESS_LEVELS
,
LEVEL_TYPES
}
from
'
./constants
'
;
import
ProtectedBranchAccessDropdown
from
'
./protected_branch_access_dropdown
'
;
const
PB_LOCAL_STORAGE_KEY
=
'
protected-branches-defaults
'
;
...
...
@@ -30,7 +30,7 @@ export default class ProtectedBranchCreate {
this
.
onSelectCallback
=
this
.
onSelect
.
bind
(
this
);
// Allowed to Merge dropdown
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
]
=
new
ProtectedBranch
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
]
=
new
AccessDropdown
({
$dropdown
:
$allowedToMergeDropdown
,
accessLevelsData
:
gon
.
merge_access_levels
,
onSelect
:
this
.
onSelectCallback
,
...
...
@@ -38,7 +38,7 @@ export default class ProtectedBranchCreate {
});
// Allowed to Push dropdown
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
]
=
new
ProtectedBranch
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
]
=
new
AccessDropdown
({
$dropdown
:
$allowedToPushDropdown
,
accessLevelsData
:
gon
.
push_access_levels
,
onSelect
:
this
.
onSelectCallback
,
...
...
@@ -82,12 +82,12 @@ export default class ProtectedBranchCreate {
},
};
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
(
level
)
=>
{
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
level
=>
{
const
accessLevel
=
ACCESS_LEVELS
[
level
];
const
selectedItems
=
this
[
`
${
accessLevel
}
_dropdown`
].
getSelectedItems
();
const
levelAttributes
=
[];
selectedItems
.
forEach
(
(
item
)
=>
{
selectedItems
.
forEach
(
item
=>
{
if
(
item
.
type
===
LEVEL_TYPES
.
USER
)
{
levelAttributes
.
push
({
user_id
:
item
.
user_id
,
...
...
@@ -115,10 +115,14 @@ export default class ProtectedBranchCreate {
if
(
savedDefaults
!=
null
)
{
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
].
setSelectedItems
(
savedDefaults
.
merge
);
let
updatedLabel
=
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
].
toggleLabel
();
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
].
$dropdown
.
find
(
'
.dropdown-toggle-text
'
).
text
(
updatedLabel
);
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
].
$dropdown
.
find
(
'
.dropdown-toggle-text
'
)
.
text
(
updatedLabel
);
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
].
setSelectedItems
(
savedDefaults
.
push
);
updatedLabel
=
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
].
toggleLabel
();
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
].
$dropdown
.
find
(
'
.dropdown-toggle-text
'
).
text
(
updatedLabel
);
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
].
$dropdown
.
find
(
'
.dropdown-toggle-text
'
)
.
text
(
updatedLabel
);
}
}
}
...
...
ee/app/assets/javascripts/protected_branches/protected_branch_edit.js
View file @
9c38fec6
...
...
@@ -4,8 +4,8 @@ import $ from 'jquery';
import
_
from
'
underscore
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Flash
from
'
~/flash
'
;
import
AccessDropdown
from
'
ee/projects/settings/access_dropdown
'
;
import
{
ACCESS_LEVELS
,
LEVEL_TYPES
}
from
'
./constants
'
;
import
ProtectedBranchAccessDropdown
from
'
./protected_branch_access_dropdown
'
;
export
default
class
ProtectedBranchEdit
{
constructor
(
options
)
{
...
...
@@ -15,15 +15,19 @@ export default class ProtectedBranchEdit {
this
.
$allowedToMergeDropdown
=
this
.
$wrap
.
find
(
'
.js-allowed-to-merge
'
);
this
.
$allowedToPushDropdown
=
this
.
$wrap
.
find
(
'
.js-allowed-to-push
'
);
this
.
$wraps
[
ACCESS_LEVELS
.
MERGE
]
=
this
.
$allowedToMergeDropdown
.
closest
(
`.
${
ACCESS_LEVELS
.
MERGE
}
-container`
);
this
.
$wraps
[
ACCESS_LEVELS
.
PUSH
]
=
this
.
$allowedToPushDropdown
.
closest
(
`.
${
ACCESS_LEVELS
.
PUSH
}
-container`
);
this
.
$wraps
[
ACCESS_LEVELS
.
MERGE
]
=
this
.
$allowedToMergeDropdown
.
closest
(
`.
${
ACCESS_LEVELS
.
MERGE
}
-container`
,
);
this
.
$wraps
[
ACCESS_LEVELS
.
PUSH
]
=
this
.
$allowedToPushDropdown
.
closest
(
`.
${
ACCESS_LEVELS
.
PUSH
}
-container`
,
);
this
.
buildDropdowns
();
}
buildDropdowns
()
{
// Allowed to merge dropdown
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
]
=
new
ProtectedBranch
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
MERGE
}
_dropdown`
]
=
new
AccessDropdown
({
accessLevel
:
ACCESS_LEVELS
.
MERGE
,
accessLevelsData
:
gon
.
merge_access_levels
,
$dropdown
:
this
.
$allowedToMergeDropdown
,
...
...
@@ -32,7 +36,7 @@ export default class ProtectedBranchEdit {
});
// Allowed to push dropdown
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
]
=
new
ProtectedBranch
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
PUSH
}
_dropdown`
]
=
new
AccessDropdown
({
accessLevel
:
ACCESS_LEVELS
.
PUSH
,
accessLevelsData
:
gon
.
push_access_levels
,
$dropdown
:
this
.
$allowedToPushDropdown
,
...
...
@@ -64,12 +68,14 @@ export default class ProtectedBranchEdit {
return
acc
;
},
{});
axios
.
patch
(
this
.
$wrap
.
data
(
'
url
'
),
{
axios
.
patch
(
this
.
$wrap
.
data
(
'
url
'
),
{
protected_branch
:
formData
,
}).
then
(({
data
})
=>
{
})
.
then
(({
data
})
=>
{
this
.
hasChanges
=
false
;
Object
.
keys
(
ACCESS_LEVELS
).
forEach
((
level
)
=>
{
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
level
=>
{
const
accessLevelName
=
ACCESS_LEVELS
[
level
];
// The data coming from server will be the new persisted *state* for each dropdown
...
...
@@ -77,7 +83,8 @@ export default class ProtectedBranchEdit {
});
this
.
$allowedToMergeDropdown
.
enable
();
this
.
$allowedToPushDropdown
.
enable
();
}).
catch
(()
=>
{
})
.
catch
(()
=>
{
this
.
$allowedToMergeDropdown
.
enable
();
this
.
$allowedToPushDropdown
.
enable
();
Flash
(
'
Failed to update branch!
'
,
null
,
$
(
'
.js-protected-branches-list
'
));
...
...
@@ -85,12 +92,14 @@ export default class ProtectedBranchEdit {
}
setSelectedItemsToDropdown
(
items
=
[],
dropdownName
)
{
const
itemsToAdd
=
items
.
map
(
(
currentItem
)
=>
{
const
itemsToAdd
=
items
.
map
(
currentItem
=>
{
if
(
currentItem
.
user_id
)
{
// Do this only for users for now
// get the current data for selected items
const
selectedItems
=
this
[
dropdownName
].
getSelectedItems
();
const
currentSelectedItem
=
_
.
findWhere
(
selectedItems
,
{
user_id
:
currentItem
.
user_id
});
const
currentSelectedItem
=
_
.
findWhere
(
selectedItems
,
{
user_id
:
currentItem
.
user_id
,
});
return
{
id
:
currentItem
.
id
,
...
...
ee/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js
deleted
100644 → 0
View file @
c79679f6
/* eslint-disable no-underscore-dangle, class-methods-use-this */
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Flash
from
'
~/flash
'
;
import
{
LEVEL_TYPES
,
LEVEL_ID_PROP
,
ACCESS_LEVEL_NONE
}
from
'
./constants
'
;
export
default
class
ProtectedTagAccessDropdown
{
constructor
(
options
)
{
const
{
$dropdown
,
accessLevel
,
accessLevelsData
,
}
=
options
;
this
.
options
=
options
;
this
.
groups
=
[];
this
.
accessLevel
=
accessLevel
;
this
.
accessLevelsData
=
accessLevelsData
.
roles
;
this
.
$dropdown
=
$dropdown
;
this
.
$wrap
=
this
.
$dropdown
.
closest
(
`.
${
this
.
accessLevel
}
-container`
);
this
.
$protectedTagsContainer
=
$
(
'
.js-protected-tags-container
'
);
this
.
usersPath
=
'
/autocomplete/users.json
'
;
this
.
groupsPath
=
'
/autocomplete/project_groups.json
'
;
this
.
defaultLabel
=
this
.
$dropdown
.
data
(
'
defaultLabel
'
);
this
.
setSelectedItems
([]);
this
.
persistPreselectedItems
();
this
.
noOneObj
=
this
.
accessLevelsData
.
find
(
level
=>
level
.
id
===
ACCESS_LEVEL_NONE
);
this
.
initDropdown
();
}
initDropdown
()
{
const
self
=
this
;
const
{
onSelect
,
onHide
}
=
this
.
options
;
this
.
$dropdown
.
glDropdown
({
data
:
this
.
getData
.
bind
(
this
),
selectable
:
true
,
filterable
:
true
,
filterRemote
:
true
,
multiSelect
:
this
.
$dropdown
.
hasClass
(
'
js-multiselect
'
),
renderRow
:
this
.
renderRow
.
bind
(
this
),
toggleLabel
:
this
.
toggleLabel
.
bind
(
this
),
hidden
()
{
if
(
onHide
)
{
onHide
();
}
},
clicked
:
(
options
)
=>
{
const
{
$el
,
e
}
=
options
;
const
item
=
options
.
selectedObj
;
e
.
preventDefault
();
if
(
$el
.
is
(
'
.is-active
'
))
{
if
(
item
.
id
===
self
.
noOneObj
.
id
)
{
self
.
accessLevelsData
.
forEach
((
level
)
=>
{
if
(
level
.
id
!==
item
.
id
)
{
self
.
removeSelectedItem
(
level
);
}
});
self
.
$wrap
.
find
(
`.item-
${
item
.
type
}
`
).
removeClass
(
'
is-active
'
);
}
else
{
const
$noOne
=
self
.
$wrap
.
find
(
`.is-active.item-
${
item
.
type
}
[data-role-id="
${
self
.
noOneObj
.
id
}
"]`
);
if
(
$noOne
.
length
)
{
$noOne
.
removeClass
(
'
is-active
'
);
self
.
removeSelectedItem
(
self
.
noOneObj
);
}
}
$el
.
addClass
(
`is-active item-
${
item
.
type
}
`
);
self
.
addSelectedItem
(
item
);
}
else
{
self
.
removeSelectedItem
(
item
);
}
if
(
onSelect
)
{
onSelect
(
item
,
$el
,
this
);
}
},
});
}
persistPreselectedItems
()
{
const
itemsToPreselect
=
this
.
$dropdown
.
data
(
'
preselectedItems
'
);
if
(
!
itemsToPreselect
||
!
itemsToPreselect
.
length
)
{
return
;
}
const
persistedItems
=
itemsToPreselect
.
map
((
item
)
=>
{
const
persistedItem
=
Object
.
assign
({},
item
);
persistedItem
.
persisted
=
true
;
return
persistedItem
;
});
this
.
setSelectedItems
(
persistedItems
);
}
setSelectedItems
(
items
=
[])
{
this
.
items
=
items
;
}
getSelectedItems
()
{
return
this
.
items
.
filter
(
item
=>
!
item
.
_destroy
);
}
getAllSelectedItems
()
{
return
this
.
items
;
}
getInputData
()
{
const
selectedItems
=
this
.
getAllSelectedItems
();
const
accessLevels
=
selectedItems
.
map
((
item
)
=>
{
const
obj
=
{};
if
(
typeof
item
.
id
!==
'
undefined
'
)
{
obj
.
id
=
item
.
id
;
}
if
(
typeof
item
.
_destroy
!==
'
undefined
'
)
{
obj
.
_destroy
=
item
.
_destroy
;
}
if
(
item
.
type
===
LEVEL_TYPES
.
ROLE
)
{
obj
.
access_level
=
item
.
access_level
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
USER
)
{
obj
.
user_id
=
item
.
user_id
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
GROUP
)
{
obj
.
group_id
=
item
.
group_id
;
}
return
obj
;
});
return
accessLevels
;
}
addSelectedItem
(
selectedItem
)
{
let
itemToAdd
=
{};
let
index
=
-
1
;
let
alreadyAdded
=
false
;
const
selectedItems
=
this
.
getAllSelectedItems
();
// Compare IDs based on selectedItem.type
selectedItems
.
forEach
((
item
,
i
)
=>
{
let
comparator
;
switch
(
selectedItem
.
type
)
{
case
LEVEL_TYPES
.
ROLE
:
comparator
=
LEVEL_ID_PROP
.
ROLE
;
// If the item already exists, just use it
if
(
item
[
comparator
]
===
selectedItem
.
id
)
{
alreadyAdded
=
true
;
}
break
;
case
LEVEL_TYPES
.
GROUP
:
comparator
=
LEVEL_ID_PROP
.
GROUP
;
break
;
case
LEVEL_TYPES
.
USER
:
comparator
=
LEVEL_ID_PROP
.
USER
;
break
;
default
:
break
;
}
if
(
selectedItem
.
id
===
item
[
comparator
])
{
index
=
i
;
}
});
if
(
alreadyAdded
)
{
return
;
}
if
(
index
!==
-
1
&&
selectedItems
[
index
].
_destroy
)
{
delete
selectedItems
[
index
].
_destroy
;
return
;
}
itemToAdd
.
type
=
selectedItem
.
type
;
if
(
selectedItem
.
type
===
LEVEL_TYPES
.
USER
)
{
itemToAdd
=
{
user_id
:
selectedItem
.
id
,
name
:
selectedItem
.
name
||
'
-name1
'
,
username
:
selectedItem
.
username
||
'
-username1
'
,
avatar_url
:
selectedItem
.
avatar_url
||
'
-avatar_url1
'
,
type
:
LEVEL_TYPES
.
USER
,
};
}
else
if
(
selectedItem
.
type
===
LEVEL_TYPES
.
ROLE
)
{
itemToAdd
=
{
access_level
:
selectedItem
.
id
,
type
:
LEVEL_TYPES
.
ROLE
,
};
}
else
if
(
selectedItem
.
type
===
LEVEL_TYPES
.
GROUP
)
{
itemToAdd
=
{
group_id
:
selectedItem
.
id
,
type
:
LEVEL_TYPES
.
GROUP
,
};
}
this
.
items
.
push
(
itemToAdd
);
}
removeSelectedItem
(
itemToDelete
)
{
let
index
=
-
1
;
const
selectedItems
=
this
.
getAllSelectedItems
();
// To find itemToDelete on selectedItems, first we need the index
selectedItems
.
every
((
item
,
i
)
=>
{
if
(
item
.
type
!==
itemToDelete
.
type
)
{
return
true
;
}
if
(
item
.
type
===
LEVEL_TYPES
.
USER
&&
item
.
user_id
===
itemToDelete
.
id
)
{
index
=
i
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
ROLE
&&
item
.
access_level
===
itemToDelete
.
id
)
{
index
=
i
;
}
else
if
(
item
.
type
===
LEVEL_TYPES
.
GROUP
&&
item
.
group_id
===
itemToDelete
.
id
)
{
index
=
i
;
}
// Break once we have index set
return
!
(
index
>
-
1
);
});
// if ItemToDelete is not really selected do nothing
if
(
index
===
-
1
)
{
return
;
}
if
(
selectedItems
[
index
].
persisted
)
{
// If we toggle an item that has been already marked with _destroy
if
(
selectedItems
[
index
].
_destroy
)
{
delete
selectedItems
[
index
].
_destroy
;
}
else
{
selectedItems
[
index
].
_destroy
=
'
1
'
;
}
}
else
{
selectedItems
.
splice
(
index
,
1
);
}
}
toggleLabel
()
{
const
currentItems
=
this
.
getSelectedItems
();
const
types
=
_
.
groupBy
(
currentItems
,
item
=>
item
.
type
);
let
label
=
[];
if
(
currentItems
.
length
)
{
label
=
Object
.
keys
(
LEVEL_TYPES
).
map
((
levelType
)
=>
{
const
typeName
=
LEVEL_TYPES
[
levelType
];
const
numberOfTypes
=
types
[
typeName
]
?
types
[
typeName
].
length
:
0
;
const
text
=
numberOfTypes
===
1
?
typeName
:
`
${
typeName
}
s`
;
return
`
${
numberOfTypes
}
${
text
}
`
;
});
}
else
{
label
.
push
(
this
.
defaultLabel
);
}
this
.
$dropdown
.
find
(
'
.dropdown-toggle-text
'
).
toggleClass
(
'
is-default
'
,
!
currentItems
.
length
);
return
label
.
join
(
'
,
'
);
}
getData
(
query
,
callback
)
{
Promise
.
all
([
this
.
getUsers
(
query
),
this
.
groupsData
?
Promise
.
resolve
(
this
.
groupsData
)
:
this
.
getGroups
(),
]).
then
(([
usersResponse
,
groupsResponse
])
=>
{
this
.
groupsData
=
groupsResponse
;
callback
(
this
.
consolidateData
(
usersResponse
.
data
,
groupsResponse
.
data
));
}).
catch
(()
=>
Flash
(
'
Failed to load groups & users.
'
));
}
consolidateData
(
usersResponse
,
groupsResponse
)
{
let
consolidatedData
=
[];
const
map
=
[];
const
selectedItems
=
this
.
getSelectedItems
();
// ID property is handled differently locally from the server
//
// For Groups
// In dropdown: `id`
// For submit: `group_id`
//
// For Roles
// In dropdown: `id`
// For submit: `access_level`
//
// For Users
// In dropdown: `id`
// For submit: `user_id`
/*
* Build groups
*/
const
groups
=
groupsResponse
.
map
(
group
=>
({
...
group
,
type
:
LEVEL_TYPES
.
GROUP
}));
/*
* Build roles
*/
const
roles
=
this
.
accessLevelsData
.
map
((
level
)
=>
{
/* eslint-disable no-param-reassign */
// This re-assignment is intentional as
// level.type property is being used in removeSelectedItem()
// for comparision, and accessLevelsData is provided by
// gon.create_access_levels which doesn't have `type` included.
// See this discussion https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1629#note_31285823
level
.
type
=
LEVEL_TYPES
.
ROLE
;
return
level
;
});
/*
* Build users
*/
const
users
=
selectedItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
USER
).
map
((
item
)
=>
{
// Save identifiers for easy-checking more later
map
.
push
(
LEVEL_TYPES
.
USER
+
item
.
user_id
);
return
{
id
:
item
.
user_id
,
name
:
item
.
name
,
username
:
item
.
username
,
avatar_url
:
item
.
avatar_url
,
type
:
LEVEL_TYPES
.
USER
,
};
});
// Has to be checked against server response
// because the selected item can be in filter results
usersResponse
.
forEach
((
response
)
=>
{
// Add is it has not been added
if
(
map
.
indexOf
(
LEVEL_TYPES
.
USER
+
response
.
id
)
===
-
1
)
{
const
user
=
Object
.
assign
({},
response
);
user
.
type
=
LEVEL_TYPES
.
USER
;
users
.
push
(
user
);
}
});
if
(
roles
.
length
)
{
consolidatedData
=
consolidatedData
.
concat
([{
header
:
'
Roles
'
}],
roles
);
}
if
(
groups
.
length
)
{
if
(
roles
.
length
)
{
consolidatedData
=
consolidatedData
.
concat
([
'
divider
'
]);
}
consolidatedData
=
consolidatedData
.
concat
([{
header
:
'
Groups
'
}],
groups
);
}
if
(
users
.
length
)
{
consolidatedData
=
consolidatedData
.
concat
([
'
divider
'
],
[{
header
:
'
Users
'
}],
users
);
}
return
consolidatedData
;
}
getUsers
(
query
)
{
return
axios
.
get
(
this
.
buildUrl
(
gon
.
relative_url_root
,
this
.
usersPath
),
{
params
:
{
search
:
query
,
per_page
:
20
,
active
:
true
,
project_id
:
gon
.
current_project_id
,
push_code
:
true
,
},
});
}
getGroups
()
{
return
axios
.
get
(
this
.
buildUrl
(
gon
.
relative_url_root
,
this
.
groupsPath
),
{
params
:
{
project_id
:
gon
.
current_project_id
,
},
});
}
buildUrl
(
urlRoot
,
url
)
{
let
newUrl
;
if
(
urlRoot
!==
null
)
{
newUrl
=
urlRoot
.
replace
(
/
\/
$/
,
''
)
+
url
;
}
return
newUrl
;
}
renderRow
(
item
)
{
let
criteria
=
{};
let
groupRowEl
;
// Detect if the current item is already saved so we can add
// the `is-active` class so the item looks as marked
switch
(
item
.
type
)
{
case
LEVEL_TYPES
.
USER
:
criteria
=
{
user_id
:
item
.
id
};
break
;
case
LEVEL_TYPES
.
ROLE
:
criteria
=
{
access_level
:
item
.
id
};
break
;
case
LEVEL_TYPES
.
GROUP
:
criteria
=
{
group_id
:
item
.
id
};
break
;
default
:
break
;
}
const
isActive
=
_
.
findWhere
(
this
.
getSelectedItems
(),
criteria
)
?
'
is-active
'
:
''
;
switch
(
item
.
type
)
{
case
LEVEL_TYPES
.
USER
:
groupRowEl
=
this
.
userRowHtml
(
item
,
isActive
);
break
;
case
LEVEL_TYPES
.
ROLE
:
groupRowEl
=
this
.
roleRowHtml
(
item
,
isActive
);
break
;
case
LEVEL_TYPES
.
GROUP
:
groupRowEl
=
this
.
groupRowHtml
(
item
,
isActive
);
break
;
default
:
groupRowEl
=
''
;
break
;
}
return
groupRowEl
;
}
userRowHtml
(
user
,
isActive
)
{
const
isActiveClass
=
isActive
||
''
;
return
`
<li>
<a href="#" class="
${
isActiveClass
}
">
<img src="
${
user
.
avatar_url
}
" class="avatar avatar-inline" width="30">
<strong class="dropdown-menu-user-full-name">
${
user
.
name
}
</strong>
<span class="dropdown-menu-user-username">
${
user
.
username
}
</span>
</a>
</li>
`
;
}
groupRowHtml
(
group
,
isActive
)
{
const
isActiveClass
=
isActive
||
''
;
const
avatarEl
=
group
.
avatar_url
?
`<img src="
${
group
.
avatar_url
}
" class="avatar avatar-inline" width="30">`
:
''
;
return
`
<li>
<a href="#" class="
${
isActiveClass
}
">
${
avatarEl
}
<span class="dropdown-menu-group-groupname">
${
group
.
name
}
</span>
</a>
</li>
`
;
}
roleRowHtml
(
role
,
isActive
)
{
const
isActiveClass
=
isActive
||
''
;
return
`
<li>
<a href="#" class="
${
isActiveClass
}
item-
${
role
.
type
}
" data-role-id="
${
role
.
id
}
">
${
role
.
text
}
</a>
</li>
`
;
}
}
ee/app/assets/javascripts/protected_tags/protected_tag_create.js
View file @
9c38fec6
import
$
from
'
jquery
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Flash
from
'
~/flash
'
;
import
create
Flash
from
'
~/flash
'
;
import
CreateItemDropdown
from
'
~/create_item_dropdown
'
;
import
{
s__
}
from
'
~/locale
'
;
import
AccessDropdown
from
'
ee/projects/settings/access_dropdown
'
;
import
{
ACCESS_LEVELS
,
LEVEL_TYPES
}
from
'
./constants
'
;
import
ProtectedTagAccessDropdown
from
'
./protected_tag_access_dropdown
'
;
export
default
class
ProtectedTagCreate
{
constructor
()
{
...
...
@@ -24,7 +25,7 @@ export default class ProtectedTagCreate {
this
.
onSelectCallback
=
this
.
onSelect
.
bind
(
this
);
// Allowed to Create dropdown
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
]
=
new
ProtectedTag
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
]
=
new
AccessDropdown
({
$dropdown
:
$allowedToCreateDropdown
,
accessLevelsData
:
gon
.
create_access_levels
,
onSelect
:
this
.
onSelectCallback
,
...
...
@@ -44,7 +45,9 @@ export default class ProtectedTagCreate {
// Enable submit button after selecting an option
onSelect
()
{
const
$allowedToCreate
=
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
].
getSelectedItems
();
const
toggle
=
!
(
this
.
$form
.
find
(
'
input[name="protected_tag[name]"]
'
).
val
()
&&
$allowedToCreate
.
length
);
const
toggle
=
!
(
this
.
$form
.
find
(
'
input[name="protected_tag[name]"]
'
).
val
()
&&
$allowedToCreate
.
length
);
this
.
$form
.
find
(
'
input[type="submit"]
'
).
attr
(
'
disabled
'
,
toggle
);
}
...
...
@@ -61,12 +64,12 @@ export default class ProtectedTagCreate {
},
};
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
(
level
)
=>
{
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
level
=>
{
const
accessLevel
=
ACCESS_LEVELS
[
level
];
const
selectedItems
=
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
].
getSelectedItems
();
const
levelAttributes
=
[];
selectedItems
.
forEach
(
(
item
)
=>
{
selectedItems
.
forEach
(
item
=>
{
if
(
item
.
type
===
LEVEL_TYPES
.
USER
)
{
levelAttributes
.
push
({
user_id
:
item
.
user_id
,
...
...
@@ -94,6 +97,7 @@ export default class ProtectedTagCreate {
axios
[
this
.
$form
.
attr
(
'
method
'
)](
this
.
$form
.
attr
(
'
action
'
),
this
.
getFormData
())
.
then
(()
=>
{
location
.
reload
();
}).
catch
(()
=>
Flash
(
'
Failed to protect the tag
'
));
})
.
catch
(()
=>
createFlash
(
s__
(
'
ProjectSettings|Failed to protect the tag
'
)));
}
}
ee/app/assets/javascripts/protected_tags/protected_tag_edit.js
View file @
9c38fec6
...
...
@@ -3,9 +3,10 @@
import
$
from
'
jquery
'
;
import
_
from
'
underscore
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
Flash
from
'
~/flash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
s__
}
from
'
~/locale
'
;
import
AccessDropdown
from
'
ee/projects/settings/access_dropdown
'
;
import
{
ACCESS_LEVELS
,
LEVEL_TYPES
}
from
'
./constants
'
;
import
ProtectedTagAccessDropdown
from
'
./protected_tag_access_dropdown
'
;
export
default
class
ProtectedTagEdit
{
constructor
(
options
)
{
...
...
@@ -13,14 +14,16 @@ export default class ProtectedTagEdit {
this
.
$wrap
=
options
.
$wrap
;
this
.
$allowedToCreateDropdownButton
=
this
.
$wrap
.
find
(
'
.js-allowed-to-create
'
);
this
.
$allowedToCreateDropdownContainer
=
this
.
$allowedToCreateDropdownButton
.
closest
(
'
.create_access_levels-container
'
);
this
.
$allowedToCreateDropdownContainer
=
this
.
$allowedToCreateDropdownButton
.
closest
(
'
.create_access_levels-container
'
,
);
this
.
buildDropdowns
();
}
buildDropdowns
()
{
// Allowed to create dropdown
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
]
=
new
ProtectedTag
AccessDropdown
({
this
[
`
${
ACCESS_LEVELS
.
CREATE
}
_dropdown`
]
=
new
AccessDropdown
({
accessLevel
:
ACCESS_LEVELS
.
CREATE
,
accessLevelsData
:
gon
.
create_access_levels
,
$dropdown
:
this
.
$allowedToCreateDropdownButton
,
...
...
@@ -52,30 +55,35 @@ export default class ProtectedTagEdit {
return
acc
;
},
{});
axios
.
patch
(
this
.
$wrap
.
data
(
'
url
'
),
{
axios
.
patch
(
this
.
$wrap
.
data
(
'
url
'
),
{
protected_tag
:
formData
,
}).
then
(({
data
})
=>
{
})
.
then
(({
data
})
=>
{
this
.
hasChanges
=
false
;
Object
.
keys
(
ACCESS_LEVELS
).
forEach
((
level
)
=>
{
Object
.
keys
(
ACCESS_LEVELS
).
forEach
(
level
=>
{
const
accessLevelName
=
ACCESS_LEVELS
[
level
];
// The data coming from server will be the new persisted *state* for each dropdown
this
.
setSelectedItemsToDropdown
(
data
[
accessLevelName
],
`
${
accessLevelName
}
_dropdown`
);
});
}).
catch
(()
=>
{
})
.
catch
(()
=>
{
$
.
scrollTo
(
0
);
Flash
(
'
Failed to update tag!
'
);
createFlash
(
s__
(
'
ProjectSettings|Failed to update tag!
'
)
);
});
}
setSelectedItemsToDropdown
(
items
=
[],
dropdownName
)
{
const
itemsToAdd
=
items
.
map
(
(
currentItem
)
=>
{
const
itemsToAdd
=
items
.
map
(
currentItem
=>
{
if
(
currentItem
.
user_id
)
{
// Do this only for users for now
// get the current data for selected items
const
selectedItems
=
this
[
dropdownName
].
getSelectedItems
();
const
currentSelectedItem
=
_
.
findWhere
(
selectedItems
,
{
user_id
:
currentItem
.
user_id
});
const
currentSelectedItem
=
_
.
findWhere
(
selectedItems
,
{
user_id
:
currentItem
.
user_id
,
});
return
{
id
:
currentItem
.
id
,
...
...
ee/app/views/projects/protected_branches/ee/_access_level_dropdown.html.haml
deleted
100644 → 0
View file @
c79679f6
-
default_label
=
'Select'
-
dropdown_label
=
default_label
%div
{
class:
"#{input_basic_name}-container"
}
-
if
access_levels
.
present?
-
dropdown_label
=
[
pluralize
(
level_frequencies
[
:role
],
'role'
),
pluralize
(
level_frequencies
[
:user
],
'user'
),
pluralize
(
level_frequencies
[
:group
],
'group'
)].
to_sentence
=
dropdown_tag
(
dropdown_label
,
options:
{
toggle_class:
"
#{
toggle_class
}
js-multiselect"
,
dropdown_class:
'dropdown-menu-user dropdown-menu-selectable'
,
filter:
true
,
data:
{
default_label:
default_label
,
preselected_items:
access_levels_data
(
access_levels
)
}
})
ee/app/views/projects/protected_branches/ee/_protected_branch_access_summary.html.haml
View file @
9c38fec6
%td
=
render
partial:
'projects/
protected_branche
s/ee/access_level_dropdown'
,
locals:
{
protected_branch:
protected_branch
,
access_levels:
protected_branch
.
merge_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_branch
.
merge_access_levels
),
input_basic_name:
'merge_access_levels'
,
toggle_class:
'js-allowed-to-merge'
}
=
render
partial:
'projects/
setting
s/ee/access_level_dropdown'
,
locals:
{
protected_branch:
protected_branch
,
access_levels:
protected_branch
.
merge_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_branch
.
merge_access_levels
),
input_basic_name:
'merge_access_levels'
,
toggle_class:
'js-allowed-to-merge'
}
%td
=
render
partial:
'projects/
protected_branche
s/ee/access_level_dropdown'
,
locals:
{
protected_branch:
protected_branch
,
access_levels:
protected_branch
.
push_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_branch
.
push_access_levels
),
input_basic_name:
'push_access_levels'
,
toggle_class:
'js-allowed-to-push'
}
=
render
partial:
'projects/
setting
s/ee/access_level_dropdown'
,
locals:
{
protected_branch:
protected_branch
,
access_levels:
protected_branch
.
push_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_branch
.
push_access_levels
),
input_basic_name:
'push_access_levels'
,
toggle_class:
'js-allowed-to-push'
}
ee/app/views/projects/protected_tags/ee/_access_level_dropdown.html.haml
deleted
100644 → 0
View file @
c79679f6
-
default_label
=
'Select'
-
dropdown_label
=
default_label
%div
{
class:
"#{input_basic_name}-container"
}
-
if
access_levels
.
present?
-
dropdown_label
=
[
pluralize
(
level_frequencies
[
:role
],
'role'
),
pluralize
(
level_frequencies
[
:user
],
'user'
),
pluralize
(
level_frequencies
[
:group
],
'group'
)].
to_sentence
=
dropdown_tag
(
dropdown_label
,
options:
{
toggle_class:
"
#{
toggle_class
}
js-multiselect"
,
dropdown_class:
'dropdown-menu-user dropdown-menu-selectable'
,
filter:
true
,
data:
{
default_label:
default_label
,
preselected_items:
access_levels_data
(
access_levels
)
}
})
ee/app/views/projects/protected_tags/ee/_protected_tag_access_summary.html.haml
View file @
9c38fec6
%td
=
render
partial:
'projects/
protected_ta
gs/ee/access_level_dropdown'
,
locals:
{
protected_tag:
protected_tag
,
access_levels:
protected_tag
.
create_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_tag
.
create_access_levels
),
input_basic_name:
'create_access_levels'
,
toggle_class:
'js-allowed-to-create'
}
=
render
partial:
'projects/
settin
gs/ee/access_level_dropdown'
,
locals:
{
protected_tag:
protected_tag
,
access_levels:
protected_tag
.
create_access_levels
,
level_frequencies:
access_level_frequencies
(
protected_tag
.
create_access_levels
),
input_basic_name:
'create_access_levels'
,
toggle_class:
'js-allowed-to-create'
}
ee/app/views/projects/settings/ee/_access_level_dropdown.html.haml
0 → 100644
View file @
9c38fec6
-
default_label
=
s_
(
'RepositorySettingsAccessLevel|Select'
)
%div
{
class:
"#{input_basic_name}-container"
}
=
dropdown_tag
(
default_label
,
options:
{
toggle_class:
"
#{
toggle_class
}
js-multiselect"
,
dropdown_class:
'dropdown-menu-user dropdown-menu-selectable'
,
filter:
true
,
data:
{
default_label:
default_label
,
preselected_items:
access_levels_data
(
access_levels
)
}
})
ee/changelogs/unreleased/winh-protection-push-dropdowns.yml
0 → 100644
View file @
9c38fec6
---
title
:
Shorten protected branch / tag access level dropdown text
merge_request
:
5091
author
:
type
:
changed
ee/spec/support/ee/protected_branch_helpers.rb
View file @
9c38fec6
...
...
@@ -5,7 +5,9 @@ module EE
find
(
".js-allowed-to-
#{
operation
}
"
).
click
wait_for_requests
within
(
'.dropdown-content'
)
do
Array
(
option
).
each
{
|
opt
|
click_on
(
opt
)
}
end
find
(
".js-allowed-to-
#{
operation
}
"
).
click
# needed to submit form in some cases
end
...
...
spec/features/protected_branches_spec.rb
View file @
9c38fec6
...
...
@@ -84,10 +84,10 @@ feature 'Protected Branches', :js do
within
form
do
page
.
within
(
".js-allowed-to-merge"
)
do
expect
(
page
.
find
(
".dropdown-toggle-text"
)).
to
have_content
(
"
1 role, 0 users, 0 groups
"
)
expect
(
page
.
find
(
".dropdown-toggle-text"
)).
to
have_content
(
"
No one
"
)
end
page
.
within
(
".js-allowed-to-push"
)
do
expect
(
page
.
find
(
".dropdown-toggle-text"
)).
to
have_content
(
"
1 role, 0 users, 0 group
s"
)
expect
(
page
.
find
(
".dropdown-toggle-text"
)).
to
have_content
(
"
Developers + Master
s"
)
end
end
end
...
...
spec/features/protected_tags_spec.rb
View file @
9c38fec6
...
...
@@ -13,7 +13,9 @@ feature 'Protected Tags', :js do
find
(
".js-allowed-to-
#{
operation
}
"
).
click
wait_for_requests
within
(
'.dropdown-content'
)
do
Array
(
option
).
each
{
|
opt
|
click_on
(
opt
)
}
end
find
(
".js-allowed-to-
#{
operation
}
"
).
click
# needed to submit form in some cases
end
...
...
spec/javascripts/ee/projects/settings/access_dropdown_spec.js
0 → 100644
View file @
9c38fec6
import
$
from
'
jquery
'
;
import
AccessDropdown
from
'
ee/projects/settings/access_dropdown
'
;
import
{
LEVEL_TYPES
}
from
'
ee/projects/settings/constants
'
;
describe
(
'
AccessDropdown
'
,
()
=>
{
const
defaultLabel
=
'
dummy default label
'
;
let
dropdown
;
beforeEach
(()
=>
{
setFixtures
(
`
<div id="dummy-dropdown">
<span class="dropdown-toggle-text"></span>
</div>
`
);
const
$dropdown
=
$
(
'
#dummy-dropdown
'
);
$dropdown
.
data
(
'
defaultLabel
'
,
defaultLabel
);
const
options
=
{
$dropdown
,
accessLevelsData
:
{
roles
:
[
{
id
:
42
,
text
:
'
Dummy Role
'
,
},
],
},
};
dropdown
=
new
AccessDropdown
(
options
);
});
describe
(
'
toggleLabel
'
,
()
=>
{
let
$dropdownToggleText
;
const
dummyItems
=
[
{
type
:
LEVEL_TYPES
.
ROLE
,
access_level
:
42
},
{
type
:
LEVEL_TYPES
.
USER
},
{
type
:
LEVEL_TYPES
.
USER
},
{
type
:
LEVEL_TYPES
.
GROUP
},
{
type
:
LEVEL_TYPES
.
GROUP
},
{
type
:
LEVEL_TYPES
.
GROUP
},
];
beforeEach
(()
=>
{
$dropdownToggleText
=
$
(
'
.dropdown-toggle-text
'
);
});
it
(
'
displays number of items
'
,
()
=>
{
dropdown
.
setSelectedItems
(
dummyItems
);
$dropdownToggleText
.
addClass
(
'
is-default
'
);
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
'
1 role, 2 users, 3 groups
'
);
expect
(
$dropdownToggleText
).
not
.
toHaveClass
(
'
is-default
'
);
});
describe
(
'
without selected items
'
,
()
=>
{
beforeEach
(()
=>
{
dropdown
.
setSelectedItems
([]);
});
it
(
'
falls back to default label
'
,
()
=>
{
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
defaultLabel
);
expect
(
$dropdownToggleText
).
toHaveClass
(
'
is-default
'
);
});
});
describe
(
'
with only role
'
,
()
=>
{
beforeEach
(()
=>
{
dropdown
.
setSelectedItems
(
dummyItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
ROLE
));
$dropdownToggleText
.
addClass
(
'
is-default
'
);
});
it
(
'
displays the role name
'
,
()
=>
{
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
'
Dummy Role
'
);
expect
(
$dropdownToggleText
).
not
.
toHaveClass
(
'
is-default
'
);
});
});
describe
(
'
with only users
'
,
()
=>
{
beforeEach
(()
=>
{
dropdown
.
setSelectedItems
(
dummyItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
USER
));
$dropdownToggleText
.
addClass
(
'
is-default
'
);
});
it
(
'
displays number of users
'
,
()
=>
{
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
'
2 users
'
);
expect
(
$dropdownToggleText
).
not
.
toHaveClass
(
'
is-default
'
);
});
});
describe
(
'
with only groups
'
,
()
=>
{
beforeEach
(()
=>
{
dropdown
.
setSelectedItems
(
dummyItems
.
filter
(
item
=>
item
.
type
===
LEVEL_TYPES
.
GROUP
));
$dropdownToggleText
.
addClass
(
'
is-default
'
);
});
it
(
'
displays number of groups
'
,
()
=>
{
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
'
3 groups
'
);
expect
(
$dropdownToggleText
).
not
.
toHaveClass
(
'
is-default
'
);
});
});
describe
(
'
with users and groups
'
,
()
=>
{
beforeEach
(()
=>
{
const
selectedTypes
=
[
LEVEL_TYPES
.
GROUP
,
LEVEL_TYPES
.
USER
];
dropdown
.
setSelectedItems
(
dummyItems
.
filter
(
item
=>
selectedTypes
.
includes
(
item
.
type
)));
$dropdownToggleText
.
addClass
(
'
is-default
'
);
});
it
(
'
displays number of groups
'
,
()
=>
{
const
label
=
dropdown
.
toggleLabel
();
expect
(
label
).
toBe
(
'
2 users, 3 groups
'
);
expect
(
$dropdownToggleText
).
not
.
toHaveClass
(
'
is-default
'
);
});
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment