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
0
Merge Requests
0
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
Jérome Perrin
gitlab-ce
Commits
5c207883
Commit
5c207883
authored
Apr 26, 2018
by
Mario de la Ossa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Backport of 4084-epics-username-autocomplete
parent
e71351d4
Changes
13
Show whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
235 additions
and
44 deletions
+235
-44
app/assets/javascripts/gfm_auto_complete.js
app/assets/javascripts/gfm_auto_complete.js
+7
-2
app/assets/javascripts/notes/components/comment_form.vue
app/assets/javascripts/notes/components/comment_form.vue
+2
-1
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+13
-0
app/models/ability.rb
app/models/ability.rb
+8
-0
app/models/concerns/participable.rb
app/models/concerns/participable.rb
+4
-0
app/models/group.rb
app/models/group.rb
+31
-0
app/models/namespace.rb
app/models/namespace.rb
+7
-0
app/services/concerns/users/participable_service.rb
app/services/concerns/users/participable_service.rb
+41
-0
app/services/projects/participants_service.rb
app/services/projects/participants_service.rb
+1
-31
app/views/layouts/_init_auto_complete.html.haml
app/views/layouts/_init_auto_complete.html.haml
+5
-10
spec/helpers/application_helper_spec.rb
spec/helpers/application_helper_spec.rb
+12
-0
spec/models/group_spec.rb
spec/models/group_spec.rb
+89
-0
spec/models/namespace_spec.rb
spec/models/namespace_spec.rb
+15
-0
No files found.
app/assets/javascripts/gfm_auto_complete.js
View file @
5c207883
...
@@ -408,7 +408,10 @@ class GfmAutoComplete {
...
@@ -408,7 +408,10 @@ class GfmAutoComplete {
fetchData
(
$input
,
at
)
{
fetchData
(
$input
,
at
)
{
if
(
this
.
isLoadingData
[
at
])
return
;
if
(
this
.
isLoadingData
[
at
])
return
;
this
.
isLoadingData
[
at
]
=
true
;
this
.
isLoadingData
[
at
]
=
true
;
const
dataSource
=
this
.
dataSources
[
GfmAutoComplete
.
atTypeMap
[
at
]];
if
(
this
.
cachedData
[
at
])
{
if
(
this
.
cachedData
[
at
])
{
this
.
loadData
(
$input
,
at
,
this
.
cachedData
[
at
]);
this
.
loadData
(
$input
,
at
,
this
.
cachedData
[
at
]);
}
else
if
(
GfmAutoComplete
.
atTypeMap
[
at
]
===
'
emojis
'
)
{
}
else
if
(
GfmAutoComplete
.
atTypeMap
[
at
]
===
'
emojis
'
)
{
...
@@ -418,12 +421,14 @@ class GfmAutoComplete {
...
@@ -418,12 +421,14 @@ class GfmAutoComplete {
GfmAutoComplete
.
glEmojiTag
=
glEmojiTag
;
GfmAutoComplete
.
glEmojiTag
=
glEmojiTag
;
})
})
.
catch
(()
=>
{
this
.
isLoadingData
[
at
]
=
false
;
});
.
catch
(()
=>
{
this
.
isLoadingData
[
at
]
=
false
;
});
}
else
{
}
else
if
(
dataSource
)
{
AjaxCache
.
retrieve
(
this
.
dataSources
[
GfmAutoComplete
.
atTypeMap
[
at
]]
,
true
)
AjaxCache
.
retrieve
(
dataSource
,
true
)
.
then
((
data
)
=>
{
.
then
((
data
)
=>
{
this
.
loadData
(
$input
,
at
,
data
);
this
.
loadData
(
$input
,
at
,
data
);
})
})
.
catch
(()
=>
{
this
.
isLoadingData
[
at
]
=
false
;
});
.
catch
(()
=>
{
this
.
isLoadingData
[
at
]
=
false
;
});
}
else
{
this
.
isLoadingData
[
at
]
=
false
;
}
}
}
}
...
...
app/assets/javascripts/notes/components/comment_form.vue
View file @
5c207883
...
@@ -100,8 +100,9 @@ export default {
...
@@ -100,8 +100,9 @@ export default {
};
};
},
},
supportQuickActions
()
{
supportQuickActions
()
{
return
true
;
// Disable quick actions support for Epics
// Disable quick actions support for Epics
return
this
.
noteableType
!==
constants
.
EPIC_NOTEABLE_TYPE
;
//
return this.noteableType !== constants.EPIC_NOTEABLE_TYPE;
},
},
markdownDocsPath
()
{
markdownDocsPath
()
{
return
this
.
getNotesData
.
markdownDocsPath
;
return
this
.
getNotesData
.
markdownDocsPath
;
...
...
app/helpers/application_helper.rb
View file @
5c207883
...
@@ -332,4 +332,17 @@ module ApplicationHelper
...
@@ -332,4 +332,17 @@ module ApplicationHelper
_
(
'You are on a read-only GitLab instance.'
)
_
(
'You are on a read-only GitLab instance.'
)
end
end
def
autocomplete_data_sources
(
object
,
noteable_type
)
return
{}
unless
object
&&
noteable_type
{
members:
members_project_autocomplete_sources_path
(
object
,
type:
noteable_type
,
type_id:
params
[
:id
]),
issues:
issues_project_autocomplete_sources_path
(
object
),
merge_requests:
merge_requests_project_autocomplete_sources_path
(
object
),
labels:
labels_project_autocomplete_sources_path
(
object
,
type:
noteable_type
,
type_id:
params
[
:id
]),
milestones:
milestones_project_autocomplete_sources_path
(
object
),
commands:
commands_project_autocomplete_sources_path
(
object
,
type:
noteable_type
,
type_id:
params
[
:id
])
}
end
end
end
app/models/ability.rb
View file @
5c207883
...
@@ -10,6 +10,14 @@ class Ability
...
@@ -10,6 +10,14 @@ class Ability
end
end
end
end
# Given a list of users and a group this method returns the users that can
# read the given group.
def
users_that_can_read_group
(
users
,
group
)
DeclarativePolicy
.
subject_scope
do
users
.
select
{
|
u
|
allowed?
(
u
,
:read_group
,
group
)
}
end
end
# Given a list of users and a snippet this method returns the users that can
# Given a list of users and a snippet this method returns the users that can
# read the given snippet.
# read the given snippet.
def
users_that_can_read_personal_snippet
(
users
,
snippet
)
def
users_that_can_read_personal_snippet
(
users
,
snippet
)
...
...
app/models/concerns/participable.rb
View file @
5c207883
...
@@ -98,6 +98,10 @@ module Participable
...
@@ -98,6 +98,10 @@ module Participable
participants
.
merge
(
ext
.
users
)
participants
.
merge
(
ext
.
users
)
filter_by_ability
(
participants
)
end
def
filter_by_ability
(
participants
)
case
self
case
self
when
PersonalSnippet
when
PersonalSnippet
Ability
.
users_that_can_read_personal_snippet
(
participants
.
to_a
,
self
)
Ability
.
users_that_can_read_personal_snippet
(
participants
.
to_a
,
self
)
...
...
app/models/group.rb
View file @
5c207883
...
@@ -238,6 +238,13 @@ class Group < Namespace
...
@@ -238,6 +238,13 @@ class Group < Namespace
.
where
(
source_id:
self_and_descendants
.
reorder
(
nil
).
select
(
:id
))
.
where
(
source_id:
self_and_descendants
.
reorder
(
nil
).
select
(
:id
))
end
end
# Returns all members that are part of the group, it's subgroups, and ancestor groups
def
direct_and_indirect_members
GroupMember
.
active_without_invites_and_requests
.
where
(
source_id:
self_and_hierarchy
.
reorder
(
nil
).
select
(
:id
))
end
def
users_with_parents
def
users_with_parents
User
User
.
where
(
id:
members_with_parents
.
select
(
:user_id
))
.
where
(
id:
members_with_parents
.
select
(
:user_id
))
...
@@ -250,6 +257,30 @@ class Group < Namespace
...
@@ -250,6 +257,30 @@ class Group < Namespace
.
reorder
(
nil
)
.
reorder
(
nil
)
end
end
# Returns all users that are members of the group because:
# 1. They belong to the group
# 2. They belong to a project that belongs to the group
# 3. They belong to a sub-group or project in such sub-group
# 4. They belong to an ancestor group
def
direct_and_indirect_users
union
=
Gitlab
::
SQL
::
Union
.
new
([
User
.
where
(
id:
direct_and_indirect_members
.
select
(
:user_id
))
.
reorder
(
nil
),
project_users_with_descendants
])
User
.
from
(
"(
#{
union
.
to_sql
}
)
#{
User
.
table_name
}
"
)
end
# Returns all users that are members of projects
# belonging to the current group or sub-groups
def
project_users_with_descendants
User
.
joins
(
projects: :group
)
.
where
(
namespaces:
{
id:
self_and_descendants
.
select
(
:id
)
})
end
def
max_member_access_for_user
(
user
)
def
max_member_access_for_user
(
user
)
return
GroupMember
::
OWNER
if
user
.
admin?
return
GroupMember
::
OWNER
if
user
.
admin?
...
...
app/models/namespace.rb
View file @
5c207883
...
@@ -163,6 +163,13 @@ class Namespace < ActiveRecord::Base
...
@@ -163,6 +163,13 @@ class Namespace < ActiveRecord::Base
projects
.
with_shared_runners
.
any?
projects
.
with_shared_runners
.
any?
end
end
# Returns all ancestors, self, and descendants of the current namespace.
def
self_and_hierarchy
Gitlab
::
GroupHierarchy
.
new
(
self
.
class
.
where
(
id:
id
))
.
all_groups
end
# Returns all the ancestors of the current namespaces.
# Returns all the ancestors of the current namespaces.
def
ancestors
def
ancestors
return
self
.
class
.
none
unless
parent_id
return
self
.
class
.
none
unless
parent_id
...
...
app/services/concerns/users/participable_service.rb
0 → 100644
View file @
5c207883
module
Users
module
ParticipableService
extend
ActiveSupport
::
Concern
included
do
attr_reader
:noteable
end
def
noteable_owner
return
[]
unless
noteable
&&
noteable
.
author
.
present?
[
as_hash
(
noteable
.
author
)]
end
def
participants_in_noteable
return
[]
unless
noteable
users
=
noteable
.
participants
(
current_user
)
sorted
(
users
)
end
def
sorted
(
users
)
users
.
uniq
.
to_a
.
compact
.
sort_by
(
&
:username
).
map
do
|
user
|
as_hash
(
user
)
end
end
def
groups
current_user
.
authorized_groups
.
sort_by
(
&
:path
).
map
do
|
group
|
count
=
group
.
users
.
count
{
username:
group
.
full_path
,
name:
group
.
full_name
,
count:
count
,
avatar_url:
group
.
avatar_url
}
end
end
private
def
as_hash
(
user
)
{
username:
user
.
username
,
name:
user
.
name
,
avatar_url:
user
.
avatar_url
}
end
end
end
app/services/projects/participants_service.rb
View file @
5c207883
module
Projects
module
Projects
class
ParticipantsService
<
BaseService
class
ParticipantsService
<
BaseService
attr_reader
:noteabl
e
include
Users
::
ParticipableServic
e
def
execute
(
noteable
)
def
execute
(
noteable
)
@noteable
=
noteable
@noteable
=
noteable
...
@@ -10,36 +10,6 @@ module Projects
...
@@ -10,36 +10,6 @@ module Projects
participants
.
uniq
participants
.
uniq
end
end
def
noteable_owner
return
[]
unless
noteable
&&
noteable
.
author
.
present?
[{
name:
noteable
.
author
.
name
,
username:
noteable
.
author
.
username
,
avatar_url:
noteable
.
author
.
avatar_url
}]
end
def
participants_in_noteable
return
[]
unless
noteable
users
=
noteable
.
participants
(
current_user
)
sorted
(
users
)
end
def
sorted
(
users
)
users
.
uniq
.
to_a
.
compact
.
sort_by
(
&
:username
).
map
do
|
user
|
{
username:
user
.
username
,
name:
user
.
name
,
avatar_url:
user
.
avatar_url
}
end
end
def
groups
current_user
.
authorized_groups
.
sort_by
(
&
:path
).
map
do
|
group
|
count
=
group
.
users
.
count
{
username:
group
.
full_path
,
name:
group
.
full_name
,
count:
count
,
avatar_url:
group
.
avatar_url
}
end
end
def
all_members
def
all_members
count
=
project
.
team
.
members
.
flatten
.
count
count
=
project
.
team
.
members
.
flatten
.
count
[{
username:
"all"
,
name:
"All Project and Group Members"
,
count:
count
}]
[{
username:
"all"
,
name:
"All Project and Group Members"
,
count:
count
}]
...
...
app/views/layouts/_init_auto_complete.html.haml
View file @
5c207883
-
project
=
@target_project
||
@project
-
object
=
@target_project
||
@project
||
@group
-
noteable_type
=
@noteable
.
class
if
@noteable
.
present?
-
noteable_type
=
@noteable
.
class
if
@noteable
.
present?
-
if
project
-
datasources
=
autocomplete_data_sources
(
object
,
noteable_type
)
-
if
object
-# haml-lint:disable InlineJavaScript
-# haml-lint:disable InlineJavaScript
:javascript
:javascript
gl
=
window
.
gl
||
{};
gl
=
window
.
gl
||
{};
gl
.
GfmAutoComplete
=
gl
.
GfmAutoComplete
||
{};
gl
.
GfmAutoComplete
=
gl
.
GfmAutoComplete
||
{};
gl
.
GfmAutoComplete
.
dataSources
=
{
gl
.
GfmAutoComplete
.
dataSources
=
#{
datasources
.
to_json
}
;
members
:
"
#{
members_project_autocomplete_sources_path
(
project
,
type:
noteable_type
,
type_id:
params
[
:id
])
}
"
,
issues
:
"
#{
issues_project_autocomplete_sources_path
(
project
)
}
"
,
mergeRequests
:
"
#{
merge_requests_project_autocomplete_sources_path
(
project
)
}
"
,
labels
:
"
#{
labels_project_autocomplete_sources_path
(
project
,
type:
noteable_type
,
type_id:
params
[
:id
])
}
"
,
milestones
:
"
#{
milestones_project_autocomplete_sources_path
(
project
)
}
"
,
commands
:
"
#{
commands_project_autocomplete_sources_path
(
project
,
type:
noteable_type
,
type_id:
params
[
:id
])
}
"
};
spec/helpers/application_helper_spec.rb
View file @
5c207883
...
@@ -290,4 +290,16 @@ describe ApplicationHelper do
...
@@ -290,4 +290,16 @@ describe ApplicationHelper do
end
end
end
end
end
end
describe
'#autocomplete_data_sources'
do
let
(
:project
)
{
create
(
:project
)
}
let
(
:noteable_type
)
{
Issue
}
it
'returns paths for autocomplete_sources_controller'
do
sources
=
helper
.
autocomplete_data_sources
(
project
,
noteable_type
)
expect
(
sources
.
keys
).
to
match_array
([
:members
,
:issues
,
:merge_requests
,
:labels
,
:milestones
,
:commands
])
sources
.
keys
.
each
do
|
key
|
expect
(
sources
[
key
]).
not_to
be_nil
end
end
end
end
end
spec/models/group_spec.rb
View file @
5c207883
...
@@ -424,6 +424,95 @@ describe Group do
...
@@ -424,6 +424,95 @@ describe Group do
end
end
end
end
describe
'#direct_and_indirect_members'
,
:nested_groups
do
let!
(
:group
)
{
create
(
:group
,
:nested
)
}
let!
(
:sub_group
)
{
create
(
:group
,
parent:
group
)
}
let!
(
:master
)
{
group
.
parent
.
add_user
(
create
(
:user
),
GroupMember
::
MASTER
)
}
let!
(
:developer
)
{
group
.
add_user
(
create
(
:user
),
GroupMember
::
DEVELOPER
)
}
let!
(
:other_developer
)
{
group
.
add_user
(
create
(
:user
),
GroupMember
::
DEVELOPER
)
}
it
'returns parents members'
do
expect
(
group
.
direct_and_indirect_members
).
to
include
(
developer
)
expect
(
group
.
direct_and_indirect_members
).
to
include
(
master
)
end
it
'returns descendant members'
do
expect
(
group
.
direct_and_indirect_members
).
to
include
(
other_developer
)
end
end
describe
'#users_with_descendants'
,
:nested_groups
do
let
(
:user_a
)
{
create
(
:user
)
}
let
(
:user_b
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let
(
:deep_nested_group
)
{
create
(
:group
,
parent:
nested_group
)
}
it
'returns member users on every nest level without duplication'
do
group
.
add_developer
(
user_a
)
nested_group
.
add_developer
(
user_b
)
deep_nested_group
.
add_developer
(
user_a
)
expect
(
group
.
users_with_descendants
).
to
contain_exactly
(
user_a
,
user_b
)
expect
(
nested_group
.
users_with_descendants
).
to
contain_exactly
(
user_a
,
user_b
)
expect
(
deep_nested_group
.
users_with_descendants
).
to
contain_exactly
(
user_a
)
end
end
describe
'#direct_and_indirect_users'
,
:nested_groups
do
let
(
:user_a
)
{
create
(
:user
)
}
let
(
:user_b
)
{
create
(
:user
)
}
let
(
:user_c
)
{
create
(
:user
)
}
let
(
:user_d
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let
(
:deep_nested_group
)
{
create
(
:group
,
parent:
nested_group
)
}
let
(
:project
)
{
create
(
:project
,
namespace:
group
)
}
before
do
group
.
add_developer
(
user_a
)
group
.
add_developer
(
user_c
)
nested_group
.
add_developer
(
user_b
)
deep_nested_group
.
add_developer
(
user_a
)
project
.
add_developer
(
user_d
)
end
it
'returns member users on every nest level without duplication'
do
expect
(
group
.
direct_and_indirect_users
).
to
contain_exactly
(
user_a
,
user_b
,
user_c
,
user_d
)
expect
(
nested_group
.
direct_and_indirect_users
).
to
contain_exactly
(
user_a
,
user_b
,
user_c
)
expect
(
deep_nested_group
.
direct_and_indirect_users
).
to
contain_exactly
(
user_a
,
user_b
,
user_c
)
end
it
'does not return members of projects belonging to ancestor groups'
do
expect
(
nested_group
.
direct_and_indirect_users
).
not_to
include
(
user_d
)
end
end
describe
'#project_users_with_descendants'
,
:nested_groups
do
let
(
:user_a
)
{
create
(
:user
)
}
let
(
:user_b
)
{
create
(
:user
)
}
let
(
:user_c
)
{
create
(
:user
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let
(
:deep_nested_group
)
{
create
(
:group
,
parent:
nested_group
)
}
let
(
:project_a
)
{
create
(
:project
,
namespace:
group
)
}
let
(
:project_b
)
{
create
(
:project
,
namespace:
nested_group
)
}
let
(
:project_c
)
{
create
(
:project
,
namespace:
deep_nested_group
)
}
it
'returns members of all projects in group and subgroups'
do
project_a
.
add_developer
(
user_a
)
project_b
.
add_developer
(
user_b
)
project_c
.
add_developer
(
user_c
)
expect
(
group
.
project_users_with_descendants
).
to
contain_exactly
(
user_a
,
user_b
,
user_c
)
expect
(
nested_group
.
project_users_with_descendants
).
to
contain_exactly
(
user_b
,
user_c
)
expect
(
deep_nested_group
.
project_users_with_descendants
).
to
contain_exactly
(
user_c
)
end
end
describe
'#user_ids_for_project_authorizations'
do
describe
'#user_ids_for_project_authorizations'
do
it
'returns the user IDs for which to refresh authorizations'
do
it
'returns the user IDs for which to refresh authorizations'
do
master
=
create
(
:user
)
master
=
create
(
:user
)
...
...
spec/models/namespace_spec.rb
View file @
5c207883
...
@@ -399,6 +399,21 @@ describe Namespace do
...
@@ -399,6 +399,21 @@ describe Namespace do
end
end
end
end
describe
'#self_and_hierarchy'
,
:nested_groups
do
let!
(
:group
)
{
create
(
:group
,
path:
'git_lab'
)
}
let!
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let!
(
:deep_nested_group
)
{
create
(
:group
,
parent:
nested_group
)
}
let!
(
:very_deep_nested_group
)
{
create
(
:group
,
parent:
deep_nested_group
)
}
let!
(
:another_group
)
{
create
(
:group
,
path:
'gitllab'
)
}
let!
(
:another_group_nested
)
{
create
(
:group
,
path:
'foo'
,
parent:
another_group
)
}
it
'returns the correct tree'
do
expect
(
group
.
self_and_hierarchy
).
to
contain_exactly
(
group
,
nested_group
,
deep_nested_group
,
very_deep_nested_group
)
expect
(
nested_group
.
self_and_hierarchy
).
to
contain_exactly
(
group
,
nested_group
,
deep_nested_group
,
very_deep_nested_group
)
expect
(
very_deep_nested_group
.
self_and_hierarchy
).
to
contain_exactly
(
group
,
nested_group
,
deep_nested_group
,
very_deep_nested_group
)
end
end
describe
'#ancestors'
,
:nested_groups
do
describe
'#ancestors'
,
:nested_groups
do
let
(
:group
)
{
create
(
:group
)
}
let
(
:group
)
{
create
(
:group
)
}
let
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
let
(
:nested_group
)
{
create
(
:group
,
parent:
group
)
}
...
...
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