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
Kazuhiko Shiozaki
gitlab-ce
Commits
5098ad9d
Commit
5098ad9d
authored
Nov 17, 2015
by
Dmitriy Zaporozhets
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'remove-code-duplication'
Signed-off-by:
Dmitriy Zaporozhets
<
dmitriy.zaporozhets@gmail.com
>
parents
6f6c75cb
fab04fac
Changes
22
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
371 additions
and
434 deletions
+371
-434
.gitlab-ci.yml
.gitlab-ci.yml
+0
-1
app/controllers/concerns/issues_action.rb
app/controllers/concerns/issues_action.rb
+14
-0
app/controllers/concerns/merge_requests_action.rb
app/controllers/concerns/merge_requests_action.rb
+9
-0
app/controllers/dashboard_controller.rb
app/controllers/dashboard_controller.rb
+3
-17
app/controllers/groups_controller.rb
app/controllers/groups_controller.rb
+3
-17
app/helpers/diff_helper.rb
app/helpers/diff_helper.rb
+16
-16
app/helpers/gitlab_markdown_helper.rb
app/helpers/gitlab_markdown_helper.rb
+24
-28
app/helpers/namespaces_helper.rb
app/helpers/namespaces_helper.rb
+0
-9
app/helpers/selects_helper.rb
app/helpers/selects_helper.rb
+14
-2
app/mailers/emails/issues.rb
app/mailers/emails/issues.rb
+32
-36
app/mailers/emails/notes.rb
app/mailers/emails/notes.rb
+40
-35
app/models/project_services/slack_service/note_message.rb
app/models/project_services/slack_service/note_message.rb
+16
-15
app/services/issuable_base_service.rb
app/services/issuable_base_service.rb
+35
-0
app/services/issues/update_service.rb
app/services/issues/update_service.rb
+9
-27
app/services/merge_requests/update_service.rb
app/services/merge_requests/update_service.rb
+9
-30
app/services/notification_service.rb
app/services/notification_service.rb
+18
-28
lib/gitlab/markdown/abstract_reference_filter.rb
lib/gitlab/markdown/abstract_reference_filter.rb
+100
-0
lib/gitlab/markdown/issue_reference_filter.rb
lib/gitlab/markdown/issue_reference_filter.rb
+7
-56
lib/gitlab/markdown/merge_request_reference_filter.rb
lib/gitlab/markdown/merge_request_reference_filter.rb
+6
-55
lib/gitlab/markdown/snippet_reference_filter.rb
lib/gitlab/markdown/snippet_reference_filter.rb
+6
-55
lib/gitlab/markdown/user_reference_filter.rb
lib/gitlab/markdown/user_reference_filter.rb
+9
-6
lib/tasks/flay.rake
lib/tasks/flay.rake
+1
-1
No files found.
.gitlab-ci.yml
View file @
5098ad9d
...
@@ -87,4 +87,3 @@ flay:
...
@@ -87,4 +87,3 @@ flay:
tags
:
tags
:
-
ruby
-
ruby
-
mysql
-
mysql
allow_failure
:
true
app/controllers/concerns/issues_action.rb
0 → 100644
View file @
5098ad9d
module
IssuesAction
extend
ActiveSupport
::
Concern
def
issues
@issues
=
get_issues_collection
@issues
=
@issues
.
page
(
params
[
:page
]).
per
(
ApplicationController
::
PER_PAGE
)
@issues
=
@issues
.
preload
(
:author
,
:project
)
respond_to
do
|
format
|
format
.
html
format
.
atom
{
render
layout:
false
}
end
end
end
app/controllers/concerns/merge_requests_action.rb
0 → 100644
View file @
5098ad9d
module
MergeRequestsAction
extend
ActiveSupport
::
Concern
def
merge_requests
@merge_requests
=
get_merge_requests_collection
@merge_requests
=
@merge_requests
.
page
(
params
[
:page
]).
per
(
ApplicationController
::
PER_PAGE
)
@merge_requests
=
@merge_requests
.
preload
(
:author
,
:target_project
)
end
end
app/controllers/dashboard_controller.rb
View file @
5098ad9d
class
DashboardController
<
Dashboard
::
ApplicationController
class
DashboardController
<
Dashboard
::
ApplicationController
include
IssuesAction
include
MergeRequestsAction
before_action
:event_filter
,
only: :activity
before_action
:event_filter
,
only: :activity
before_action
:projects
,
only:
[
:issues
,
:merge_requests
]
before_action
:projects
,
only:
[
:issues
,
:merge_requests
]
respond_to
:html
respond_to
:html
def
merge_requests
@merge_requests
=
get_merge_requests_collection
@merge_requests
=
@merge_requests
.
page
(
params
[
:page
]).
per
(
PER_PAGE
)
@merge_requests
=
@merge_requests
.
preload
(
:author
,
:target_project
)
end
def
issues
@issues
=
get_issues_collection
@issues
=
@issues
.
page
(
params
[
:page
]).
per
(
PER_PAGE
)
@issues
=
@issues
.
preload
(
:author
,
:project
)
respond_to
do
|
format
|
format
.
html
format
.
atom
{
render
layout:
false
}
end
end
def
activity
def
activity
@last_push
=
current_user
.
recent_push
@last_push
=
current_user
.
recent_push
...
...
app/controllers/groups_controller.rb
View file @
5098ad9d
class
GroupsController
<
Groups
::
ApplicationController
class
GroupsController
<
Groups
::
ApplicationController
include
IssuesAction
include
MergeRequestsAction
skip_before_action
:authenticate_user!
,
only:
[
:show
,
:issues
,
:merge_requests
]
skip_before_action
:authenticate_user!
,
only:
[
:show
,
:issues
,
:merge_requests
]
respond_to
:html
respond_to
:html
before_action
:group
,
except:
[
:new
,
:create
]
before_action
:group
,
except:
[
:new
,
:create
]
...
@@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController
...
@@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController
end
end
end
end
def
merge_requests
@merge_requests
=
get_merge_requests_collection
@merge_requests
=
@merge_requests
.
page
(
params
[
:page
]).
per
(
PER_PAGE
)
@merge_requests
=
@merge_requests
.
preload
(
:author
,
:target_project
)
end
def
issues
@issues
=
get_issues_collection
@issues
=
@issues
.
page
(
params
[
:page
]).
per
(
PER_PAGE
)
@issues
=
@issues
.
preload
(
:author
,
:project
)
respond_to
do
|
format
|
format
.
html
format
.
atom
{
render
layout:
false
}
end
end
def
edit
def
edit
end
end
...
...
app/helpers/diff_helper.rb
View file @
5098ad9d
...
@@ -136,25 +136,11 @@ module DiffHelper
...
@@ -136,25 +136,11 @@ module DiffHelper
end
end
def
inline_diff_btn
def
inline_diff_btn
params_copy
=
params
.
dup
diff_btn
(
'Inline'
,
'inline'
,
diff_view
==
'inline'
)
params_copy
[
:view
]
=
'inline'
# Always use HTML to handle case where JSON diff rendered this button
params_copy
.
delete
(
:format
)
link_to
url_for
(
params_copy
),
id:
"inline-diff-btn"
,
class:
(
diff_view
==
'inline'
?
'btn active'
:
'btn'
)
do
'Inline'
end
end
end
def
parallel_diff_btn
def
parallel_diff_btn
params_copy
=
params
.
dup
diff_btn
(
'Side-by-side'
,
'parallel'
,
diff_view
==
'parallel'
)
params_copy
[
:view
]
=
'parallel'
# Always use HTML to handle case where JSON diff rendered this button
params_copy
.
delete
(
:format
)
link_to
url_for
(
params_copy
),
id:
"parallel-diff-btn"
,
class:
(
diff_view
==
'parallel'
?
'btn active'
:
'btn'
)
do
'Side-by-side'
end
end
end
def
submodule_link
(
blob
,
ref
,
repository
=
@repository
)
def
submodule_link
(
blob
,
ref
,
repository
=
@repository
)
...
@@ -191,4 +177,18 @@ module DiffHelper
...
@@ -191,4 +177,18 @@ module DiffHelper
def
editable_diff?
(
diff
)
def
editable_diff?
(
diff
)
!
diff
.
deleted_file
&&
@merge_request
&&
@merge_request
.
source_project
!
diff
.
deleted_file
&&
@merge_request
&&
@merge_request
.
source_project
end
end
private
def
diff_btn
(
title
,
name
,
selected
)
params_copy
=
params
.
dup
params_copy
[
:view
]
=
name
# Always use HTML to handle case where JSON diff rendered this button
params_copy
.
delete
(
:format
)
link_to
url_for
(
params_copy
),
id:
"
#{
name
}
-diff-btn"
,
class:
(
selected
?
'btn active'
:
'btn'
)
do
title
end
end
end
end
app/helpers/gitlab_markdown_helper.rb
View file @
5098ad9d
...
@@ -46,39 +46,13 @@ module GitlabMarkdownHelper
...
@@ -46,39 +46,13 @@ module GitlabMarkdownHelper
end
end
def
markdown
(
text
,
context
=
{})
def
markdown
(
text
,
context
=
{})
return
""
unless
text
.
present?
process_markdown
(
text
,
context
)
context
.
reverse_merge!
(
path:
@path
,
pipeline: :default
,
project:
@project
,
project_wiki:
@project_wiki
,
ref:
@ref
)
user
=
current_user
if
defined?
(
current_user
)
html
=
Gitlab
::
Markdown
.
render
(
text
,
context
)
Gitlab
::
Markdown
.
post_process
(
html
,
pipeline:
context
[
:pipeline
],
project:
@project
,
user:
user
)
end
end
# TODO (rspeicher): Remove all usages of this helper and just call `markdown`
# TODO (rspeicher): Remove all usages of this helper and just call `markdown`
# with a custom pipeline depending on the content being rendered
# with a custom pipeline depending on the content being rendered
def
gfm
(
text
,
options
=
{})
def
gfm
(
text
,
options
=
{})
return
""
unless
text
.
present?
process_markdown
(
text
,
options
,
:gfm
)
options
.
reverse_merge!
(
path:
@path
,
pipeline: :default
,
project:
@project
,
project_wiki:
@project_wiki
,
ref:
@ref
)
user
=
current_user
if
defined?
(
current_user
)
html
=
Gitlab
::
Markdown
.
gfm
(
text
,
options
)
Gitlab
::
Markdown
.
post_process
(
html
,
pipeline:
options
[
:pipeline
],
project:
@project
,
user:
user
)
end
end
def
asciidoc
(
text
)
def
asciidoc
(
text
)
...
@@ -204,4 +178,26 @@ module GitlabMarkdownHelper
...
@@ -204,4 +178,26 @@ module GitlabMarkdownHelper
''
''
end
end
end
end
def
process_markdown
(
text
,
options
,
method
=
:markdown
)
return
""
unless
text
.
present?
options
.
reverse_merge!
(
path:
@path
,
pipeline: :default
,
project:
@project
,
project_wiki:
@project_wiki
,
ref:
@ref
)
user
=
current_user
if
defined?
(
current_user
)
html
=
if
method
==
:gfm
Gitlab
::
Markdown
.
gfm
(
text
,
options
)
else
Gitlab
::
Markdown
.
render
(
text
,
options
)
end
Gitlab
::
Markdown
.
post_process
(
html
,
pipeline:
options
[
:pipeline
],
project:
@project
,
user:
user
)
end
end
end
app/helpers/namespaces_helper.rb
View file @
5098ad9d
...
@@ -17,15 +17,6 @@ module NamespacesHelper
...
@@ -17,15 +17,6 @@ module NamespacesHelper
grouped_options_for_select
(
options
,
selected
)
grouped_options_for_select
(
options
,
selected
)
end
end
def
namespace_select_tag
(
id
,
opts
=
{})
css_class
=
"ajax-namespace-select "
css_class
<<
"multiselect "
if
opts
[
:multiple
]
css_class
<<
(
opts
[
:class
]
||
''
)
value
=
opts
[
:selected
]
||
''
hidden_field_tag
(
id
,
value
,
class:
css_class
)
end
def
namespace_icon
(
namespace
,
size
=
40
)
def
namespace_icon
(
namespace
,
size
=
40
)
if
namespace
.
kind_of?
(
Group
)
if
namespace
.
kind_of?
(
Group
)
group_icon
(
namespace
)
group_icon
(
namespace
)
...
...
app/helpers/selects_helper.rb
View file @
5098ad9d
...
@@ -35,8 +35,20 @@ module SelectsHelper
...
@@ -35,8 +35,20 @@ module SelectsHelper
end
end
def
groups_select_tag
(
id
,
opts
=
{})
def
groups_select_tag
(
id
,
opts
=
{})
css_class
=
"ajax-groups-select "
opts
[
:class
]
||=
''
css_class
<<
"multiselect "
if
opts
[
:multiple
]
opts
[
:class
]
<<
' ajax-groups-select'
select2_tag
(
id
,
opts
)
end
def
namespace_select_tag
(
id
,
opts
=
{})
opts
[
:class
]
||=
''
opts
[
:class
]
<<
' ajax-namespace-select'
select2_tag
(
id
,
opts
)
end
def
select2_tag
(
id
,
opts
=
{})
css_class
=
''
css_class
<<
'multiselect '
if
opts
[
:multiple
]
css_class
<<
(
opts
[
:class
]
||
''
)
css_class
<<
(
opts
[
:class
]
||
''
)
value
=
opts
[
:selected
]
||
''
value
=
opts
[
:selected
]
||
''
...
...
app/mailers/emails/issues.rb
View file @
5098ad9d
module
Emails
module
Emails
module
Issues
module
Issues
def
new_issue_email
(
recipient_id
,
issue_id
)
def
new_issue_email
(
recipient_id
,
issue_id
)
@issue
=
Issue
.
find
(
issue_id
)
issue_mail_with_notification
(
issue_id
,
recipient_id
)
do
@project
=
@issue
.
project
mail_new_thread
(
@issue
,
issue_thread_options
(
@issue
.
author_id
,
recipient_id
))
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
end
mail_new_thread
(
@issue
,
from:
sender
(
@issue
.
author_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
))
SentNotification
.
record
(
@issue
,
recipient_id
,
reply_key
)
end
end
def
reassigned_issue_email
(
recipient_id
,
issue_id
,
previous_assignee_id
,
updated_by_user_id
)
def
reassigned_issue_email
(
recipient_id
,
issue_id
,
previous_assignee_id
,
updated_by_user_id
)
@issue
=
Issue
.
find
(
issue_id
)
issue_mail_with_notification
(
issue_id
,
recipient_id
)
do
@previous_assignee
=
User
.
find_by
(
id:
previous_assignee_id
)
if
previous_assignee_id
@previous_assignee
=
User
.
find_by
(
id:
previous_assignee_id
)
if
previous_assignee_id
@project
=
@issue
.
project
mail_answer_thread
(
@issue
,
issue_thread_options
(
updated_by_user_id
,
recipient_id
))
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
end
mail_answer_thread
(
@issue
,
from:
sender
(
updated_by_user_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
))
SentNotification
.
record
(
@issue
,
recipient_id
,
reply_key
)
end
end
def
closed_issue_email
(
recipient_id
,
issue_id
,
updated_by_user_id
)
def
closed_issue_email
(
recipient_id
,
issue_id
,
updated_by_user_id
)
@issue
=
Issue
.
find
issue_id
issue_mail_with_notification
(
issue_id
,
recipient_id
)
do
@project
=
@issue
.
project
@updated_by
=
User
.
find
updated_by_user_id
@updated_by
=
User
.
find
updated_by_user_id
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
mail_answer_thread
(
@issue
,
issue_thread_options
(
updated_by_user_id
,
recipient_id
))
mail_answer_thread
(
@issue
,
end
from:
sender
(
updated_by_user_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
))
SentNotification
.
record
(
@issue
,
recipient_id
,
reply_key
)
end
end
def
issue_status_changed_email
(
recipient_id
,
issue_id
,
status
,
updated_by_user_id
)
def
issue_status_changed_email
(
recipient_id
,
issue_id
,
status
,
updated_by_user_id
)
@issue
=
Issue
.
find
issue_id
issue_mail_with_notification
(
issue_id
,
recipient_id
)
do
@issue_status
=
status
@issue_status
=
status
@project
=
@issue
.
project
@updated_by
=
User
.
find
updated_by_user_id
@updated_by
=
User
.
find
updated_by_user_id
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
mail_answer_thread
(
@issue
,
issue_thread_options
(
updated_by_user_id
,
recipient_id
))
mail_answer_thread
(
@issue
,
end
from:
sender
(
updated_by_user_id
),
end
private
def
issue_thread_options
(
sender_id
,
recipient_id
)
{
from:
sender
(
sender_id
),
to:
recipient
(
recipient_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
))
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
)
}
end
def
issue_mail_with_notification
(
issue_id
,
recipient_id
)
@issue
=
Issue
.
find
(
issue_id
)
@project
=
@issue
.
project
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
@issue
)
yield
SentNotification
.
record
(
@issue
,
recipient_id
,
reply_key
)
SentNotification
.
record
(
@issue
,
recipient_id
,
reply_key
)
end
end
...
...
app/mailers/emails/notes.rb
View file @
5098ad9d
module
Emails
module
Emails
module
Notes
module
Notes
def
note_commit_email
(
recipient_id
,
note_id
)
def
note_commit_email
(
recipient_id
,
note_id
)
@note
=
Note
.
find
(
note_id
)
note_mail_with_notification
(
note_id
,
recipient_id
)
do
@commit
=
@note
.
noteable
@commit
=
@note
.
noteable
@project
=
@note
.
project
@target_url
=
namespace_project_commit_url
(
*
note_target_url_options
)
@target_url
=
namespace_project_commit_url
(
@project
.
namespace
,
@project
,
@commit
,
anchor:
"note_
#{
@note
.
id
}
"
)
mail_answer_thread
(
@commit
,
mail_answer_thread
(
@commit
,
from:
sender
(
@note
.
author_id
),
from:
sender
(
@note
.
author_id
),
to:
recipient
(
recipient_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@commit
.
title
}
(
#{
@commit
.
short_id
}
)"
))
subject:
subject
(
"
#{
@commit
.
title
}
(
#{
@commit
.
short_id
}
)"
))
end
SentNotification
.
record_note
(
@note
,
recipient_id
,
reply_key
)
end
end
def
note_issue_email
(
recipient_id
,
note_id
)
def
note_issue_email
(
recipient_id
,
note_id
)
@note
=
Note
.
find
(
note_id
)
note_mail_with_notification
(
note_id
,
recipient_id
)
do
@issue
=
@note
.
noteable
@issue
=
@note
.
noteable
@project
=
@note
.
project
@target_url
=
namespace_project_issue_url
(
*
note_target_url_options
)
@target_url
=
namespace_project_issue_url
(
@project
.
namespace
,
@project
,
mail_answer_thread
(
@issue
,
note_thread_options
(
recipient_id
))
@issue
,
anchor:
end
"note_
#{
@note
.
id
}
"
)
mail_answer_thread
(
@issue
,
from:
sender
(
@note
.
author_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@issue
.
title
}
(#
#{
@issue
.
iid
}
)"
))
SentNotification
.
record_note
(
@note
,
recipient_id
,
reply_key
)
end
end
def
note_merge_request_email
(
recipient_id
,
note_id
)
def
note_merge_request_email
(
recipient_id
,
note_id
)
@note
=
Note
.
find
(
note_id
)
note_mail_with_notification
(
note_id
,
recipient_id
)
do
@merge_request
=
@note
.
noteable
@merge_request
=
@note
.
noteable
@project
=
@note
.
project
@target_url
=
namespace_project_merge_request_url
(
*
note_target_url_options
)
@target_url
=
namespace_project_merge_request_url
(
@project
.
namespace
,
mail_answer_thread
(
@merge_request
,
note_thread_options
(
recipient_id
))
@project
,
end
@merge_request
,
anchor:
end
"note_
#{
@note
.
id
}
"
)
mail_answer_thread
(
@merge_request
,
private
def
note_target_url_options
[
@project
.
namespace
,
@project
,
@note
.
noteable
,
anchor:
"note_
#{
@note
.
id
}
"
]
end
def
note_thread_options
(
recipient_id
)
{
from:
sender
(
@note
.
author_id
),
from:
sender
(
@note
.
author_id
),
to:
recipient
(
recipient_id
),
to:
recipient
(
recipient_id
),
subject:
subject
(
"
#{
@merge_request
.
title
}
(#
#{
@merge_request
.
iid
}
)"
))
subject:
subject
(
"
#{
@note
.
noteable
.
title
}
(#
#{
@note
.
noteable
.
iid
}
)"
)
}
end
def
note_mail_with_notification
(
note_id
,
recipient_id
)
@note
=
Note
.
find
(
note_id
)
@project
=
@note
.
project
yield
SentNotification
.
record
_note
(
@note
,
recipient_id
,
reply_key
)
SentNotification
.
record
(
@note
,
recipient_id
,
reply_key
)
end
end
end
end
end
end
app/models/project_services/slack_service/note_message.rb
View file @
5098ad9d
...
@@ -45,30 +45,27 @@ class SlackService
...
@@ -45,30 +45,27 @@ class SlackService
def
create_commit_note
(
commit
)
def
create_commit_note
(
commit
)
commit_sha
=
commit
[
:id
]
commit_sha
=
commit
[
:id
]
commit_sha
=
Commit
.
truncate_sha
(
commit_sha
)
commit_sha
=
Commit
.
truncate_sha
(
commit_sha
)
comm
it_link
=
"[commit
#{
commit_sha
}
](
#{
@note_url
}
)"
comm
ented_on_message
(
title
=
format_title
(
commit
[
:message
])
"[commit
#{
commit_sha
}
](
#{
@note_url
}
)"
,
@message
=
"
#{
@user_name
}
commented on
#{
commit_link
}
in
#{
project_link
}
: *
#{
title
}
*"
format_title
(
commit
[
:message
]))
end
end
def
create_issue_note
(
issue
)
def
create_issue_note
(
issue
)
issue_iid
=
issue
[
:iid
]
commented_on_message
(
note_link
=
"[issue #
#{
issue_iid
}
](
#{
@note_url
}
)"
"[issue #
#{
issue
[
:iid
]
}
](
#{
@note_url
}
)"
,
title
=
format_title
(
issue
[
:title
])
format_title
(
issue
[
:title
]))
@message
=
"
#{
@user_name
}
commented on
#{
note_link
}
in
#{
project_link
}
: *
#{
title
}
*"
end
end
def
create_merge_note
(
merge_request
)
def
create_merge_note
(
merge_request
)
merge_request_id
=
merge_request
[
:iid
]
commented_on_message
(
merge_request_link
=
"[merge request #
#{
merge_request_id
}
](
#{
@note_url
}
)"
"[merge request #
#{
merge_request
[
:iid
]
}
](
#{
@note_url
}
)"
,
title
=
format_title
(
merge_request
[
:title
])
format_title
(
merge_request
[
:title
]))
@message
=
"
#{
@user_name
}
commented on
#{
merge_request_link
}
in
#{
project_link
}
: *
#{
title
}
*"
end
end
def
create_snippet_note
(
snippet
)
def
create_snippet_note
(
snippet
)
snippet_id
=
snippet
[
:id
]
commented_on_message
(
snippet_link
=
"[snippet #
#{
snippet_id
}
](
#{
@note_url
}
)"
"[snippet #
#{
snippet
[
:id
]
}
](
#{
@note_url
}
)"
,
title
=
format_title
(
snippet
[
:title
])
format_title
(
snippet
[
:title
]))
@message
=
"
#{
@user_name
}
commented on
#{
snippet_link
}
in
#{
project_link
}
: *
#{
title
}
*"
end
end
def
description_message
def
description_message
...
@@ -78,5 +75,9 @@ class SlackService
...
@@ -78,5 +75,9 @@ class SlackService
def
project_link
def
project_link
"[
#{
@project_name
}
](
#{
@project_url
}
)"
"[
#{
@project_name
}
](
#{
@project_url
}
)"
end
end
def
commented_on_message
(
target_link
,
title
)
@message
=
"
#{
@user_name
}
commented on
#{
target_link
}
in
#{
project_link
}
: *
#{
title
}
*"
end
end
end
end
end
app/services/issuable_base_service.rb
View file @
5098ad9d
...
@@ -28,6 +28,9 @@ class IssuableBaseService < BaseService
...
@@ -28,6 +28,9 @@ class IssuableBaseService < BaseService
end
end
def
filter_params
(
issuable_ability_name
=
:issue
)
def
filter_params
(
issuable_ability_name
=
:issue
)
params
[
:assignee_id
]
=
""
if
params
[
:assignee_id
]
==
IssuableFinder
::
NONE
params
[
:milestone_id
]
=
""
if
params
[
:milestone_id
]
==
IssuableFinder
::
NONE
ability
=
:"admin_
#{
issuable_ability_name
}
"
ability
=
:"admin_
#{
issuable_ability_name
}
"
unless
can?
(
current_user
,
ability
,
project
)
unless
can?
(
current_user
,
ability
,
project
)
...
@@ -36,4 +39,36 @@ class IssuableBaseService < BaseService
...
@@ -36,4 +39,36 @@ class IssuableBaseService < BaseService
params
.
delete
(
:assignee_id
)
params
.
delete
(
:assignee_id
)
end
end
end
end
def
update
(
issuable
)
change_state
(
issuable
)
filter_params
old_labels
=
issuable
.
labels
.
to_a
if
params
.
present?
&&
issuable
.
update_attributes
(
params
.
merge
(
updated_by:
current_user
))
issuable
.
reset_events_cache
if
issuable
.
labels
!=
old_labels
create_labels_note
(
issuable
,
issuable
.
labels
-
old_labels
,
old_labels
-
issuable
.
labels
)
end
handle_changes
(
issuable
)
issuable
.
create_new_cross_references!
(
current_user
)
execute_hooks
(
issuable
,
'update'
)
end
issuable
end
def
change_state
(
issuable
)
case
params
.
delete
(
:state_event
)
when
'reopen'
reopen_service
.
new
(
project
,
current_user
,
{}).
execute
(
issuable
)
when
'close'
close_service
.
new
(
project
,
current_user
,
{}).
execute
(
issuable
)
end
end
end
end
app/services/issues/update_service.rb
View file @
5098ad9d
module
Issues
module
Issues
class
UpdateService
<
Issues
::
BaseService
class
UpdateService
<
Issues
::
BaseService
def
execute
(
issue
)
def
execute
(
issue
)
case
params
.
delete
(
:state_event
)
update
(
issue
)
when
'reopen'
Issues
::
ReopenService
.
new
(
project
,
current_user
,
{}).
execute
(
issue
)
when
'close'
Issues
::
CloseService
.
new
(
project
,
current_user
,
{}).
execute
(
issue
)
end
params
[
:assignee_id
]
=
""
if
params
[
:assignee_id
]
==
IssuableFinder
::
NONE
params
[
:milestone_id
]
=
""
if
params
[
:milestone_id
]
==
IssuableFinder
::
NONE
filter_params
old_labels
=
issue
.
labels
.
to_a
if
params
.
present?
&&
issue
.
update_attributes
(
params
.
merge
(
updated_by:
current_user
))
issue
.
reset_events_cache
if
issue
.
labels
!=
old_labels
create_labels_note
(
issue
,
issue
.
labels
-
old_labels
,
old_labels
-
issue
.
labels
)
end
handle_changes
(
issue
)
issue
.
create_new_cross_references!
(
current_user
)
execute_hooks
(
issue
,
'update'
)
end
issue
end
end
def
handle_changes
(
issue
)
def
handle_changes
(
issue
)
...
@@ -44,5 +18,13 @@ module Issues
...
@@ -44,5 +18,13 @@ module Issues
create_title_change_note
(
issue
,
issue
.
previous_changes
[
'title'
].
first
)
create_title_change_note
(
issue
,
issue
.
previous_changes
[
'title'
].
first
)
end
end
end
end
def
reopen_service
Issues
::
ReopenService
end
def
close_service
Issues
::
CloseService
end
end
end
end
end
app/services/merge_requests/update_service.rb
View file @
5098ad9d
...
@@ -11,36 +11,7 @@ module MergeRequests
...
@@ -11,36 +11,7 @@ module MergeRequests
params
.
except!
(
:target_project_id
)
params
.
except!
(
:target_project_id
)
params
.
except!
(
:source_branch
)
params
.
except!
(
:source_branch
)
case
params
.
delete
(
:state_event
)
update
(
merge_request
)
when
'reopen'
MergeRequests
::
ReopenService
.
new
(
project
,
current_user
,
{}).
execute
(
merge_request
)
when
'close'
MergeRequests
::
CloseService
.
new
(
project
,
current_user
,
{}).
execute
(
merge_request
)
end
params
[
:assignee_id
]
=
""
if
params
[
:assignee_id
]
==
IssuableFinder
::
NONE
params
[
:milestone_id
]
=
""
if
params
[
:milestone_id
]
==
IssuableFinder
::
NONE
filter_params
old_labels
=
merge_request
.
labels
.
to_a
if
params
.
present?
&&
merge_request
.
update_attributes
(
params
.
merge
(
updated_by:
current_user
))
merge_request
.
reset_events_cache
if
merge_request
.
labels
!=
old_labels
create_labels_note
(
merge_request
,
merge_request
.
labels
-
old_labels
,
old_labels
-
merge_request
.
labels
)
end
handle_changes
(
merge_request
)
merge_request
.
create_new_cross_references!
(
current_user
)
execute_hooks
(
merge_request
,
'update'
)
end
merge_request
end
end
def
handle_changes
(
merge_request
)
def
handle_changes
(
merge_request
)
...
@@ -68,5 +39,13 @@ module MergeRequests
...
@@ -68,5 +39,13 @@ module MergeRequests
merge_request
.
mark_as_unchecked
merge_request
.
mark_as_unchecked
end
end
end
end
def
reopen_service
MergeRequests
::
ReopenService
end
def
close_service
MergeRequests
::
CloseService
end
end
end
end
end
app/services/notification_service.rb
View file @
5098ad9d
...
@@ -276,35 +276,25 @@ class NotificationService
...
@@ -276,35 +276,25 @@ class NotificationService
# Remove users with disabled notifications from array
# Remove users with disabled notifications from array
# Also remove duplications and nil recipients
# Also remove duplications and nil recipients
def
reject_muted_users
(
users
,
project
=
nil
)
def
reject_muted_users
(
users
,
project
=
nil
)
users
=
users
.
to_a
.
compact
.
uniq
reject_users
(
users
,
:disabled?
,
project
)
users
=
users
.
reject
(
&
:blocked?
)
users
.
reject
do
|
user
|
next
user
.
notification
.
disabled?
unless
project
member
=
project
.
project_members
.
find_by
(
user_id:
user
.
id
)
if
!
member
&&
project
.
group
member
=
project
.
group
.
group_members
.
find_by
(
user_id:
user
.
id
)
end
# reject users who globally disabled notification and has no membership
next
user
.
notification
.
disabled?
unless
member
# reject users who disabled notification in project
next
true
if
member
.
notification
.
disabled?
# reject users who have N_GLOBAL in project and disabled in global settings
member
.
notification
.
global?
&&
user
.
notification
.
disabled?
end
end
end
# Remove users with notification level 'Mentioned'
# Remove users with notification level 'Mentioned'
def
reject_mention_users
(
users
,
project
=
nil
)
def
reject_mention_users
(
users
,
project
=
nil
)
reject_users
(
users
,
:mention?
,
project
)
end
# Reject users which method_name from notification object returns true.
#
# Example:
# reject_users(users, :watch?, project)
#
def
reject_users
(
users
,
method_name
,
project
=
nil
)
users
=
users
.
to_a
.
compact
.
uniq
users
=
users
.
to_a
.
compact
.
uniq
users
=
users
.
reject
(
&
:blocked?
)
users
.
reject
do
|
user
|
users
.
reject
do
|
user
|
next
user
.
notification
.
mention?
unless
project
next
user
.
notification
.
send
(
method_name
)
unless
project
member
=
project
.
project_members
.
find_by
(
user_id:
user
.
id
)
member
=
project
.
project_members
.
find_by
(
user_id:
user
.
id
)
...
@@ -313,13 +303,13 @@ class NotificationService
...
@@ -313,13 +303,13 @@ class NotificationService
end
end
# reject users who globally set mention notification and has no membership
# reject users who globally set mention notification and has no membership
next
user
.
notification
.
mention?
unless
member
next
user
.
notification
.
send
(
method_name
)
unless
member
# reject users who set mention notification in project
# reject users who set mention notification in project
next
true
if
member
.
notification
.
mention?
next
true
if
member
.
notification
.
send
(
method_name
)
# reject users who have N_MENTION in project and disabled in global settings
# reject users who have N_MENTION in project and disabled in global settings
member
.
notification
.
global?
&&
user
.
notification
.
mention?
member
.
notification
.
global?
&&
user
.
notification
.
send
(
method_name
)
end
end
end
end
...
...
lib/gitlab/markdown/abstract_reference_filter.rb
0 → 100644
View file @
5098ad9d
require
'gitlab/markdown'
module
Gitlab
module
Markdown
# Issues, Snippets and Merge Requests shares similar functionality in refernce filtering.
# All this functionality moved to this class
class
AbstractReferenceFilter
<
ReferenceFilter
include
CrossProjectReference
def
self
.
object_class
# Implement in child class
# Example: MergeRequest
end
def
self
.
object_name
object_class
.
name
.
underscore
end
def
self
.
object_sym
object_name
.
to_sym
end
def
self
.
data_reference
"data-
#{
object_name
.
dasherize
}
"
end
# Public: Find references in text (like `!123` for merge requests)
#
# AnyReferenceFilter.references_in(text) do |match, object|
# "<a href=...>PREFIX#{object}</a>"
# end
#
# PREFIX - symbol that detects reference (like ! for merge requests)
# object - reference object (snippet, merget request etc)
# text - String text to search.
#
# Yields the String match, the Integer referenced object ID, and an optional String
# of the external project reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
object_class
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
object_sym
].
to_i
,
$~
[
:project
]
end
end
def
self
.
referenced_by
(
node
)
{
object_sym
=>
LazyReference
.
new
(
object_class
,
node
.
attr
(
data_reference
))
}
end
delegate
:object_class
,
:object_sym
,
:references_in
,
to: :class
def
find_object
(
project
,
id
)
# Implement in child class
# Example: project.merge_requests.find
end
def
url_for_object
(
object
,
project
)
# Implement in child class
# Example: project_merge_request_url
end
def
call
replace_text_nodes_matching
(
object_class
.
reference_pattern
)
do
|
content
|
object_link_filter
(
content
)
end
end
# Replace references (like `!123` for merge requests) in text with links
# to the referenced object's details page.
#
# text - String text to replace references in.
#
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def
object_link_filter
(
text
)
references_in
(
text
)
do
|
match
,
id
,
project_ref
|
project
=
project_from_ref
(
project_ref
)
if
project
&&
object
=
find_object
(
project
,
id
)
title
=
escape_once
(
"
#{
object_title
}
:
#{
object
.
title
}
"
)
klass
=
reference_class
(
object_sym
)
data
=
data_attribute
(
project:
project
.
id
,
object_sym
=>
object
.
id
)
url
=
url_for_object
(
object
,
project
)
%(<a href="#{url}" #{data}
title="#{title}"
class="#{klass}">#{match}</a>)
else
match
end
end
end
def
object_title
object_class
.
name
.
titleize
end
end
end
end
lib/gitlab/markdown/issue_reference_filter.rb
View file @
5098ad9d
...
@@ -6,66 +6,17 @@ module Gitlab
...
@@ -6,66 +6,17 @@ module Gitlab
# issues that do not exist are ignored.
# issues that do not exist are ignored.
#
#
# This filter supports cross-project references.
# This filter supports cross-project references.
class
IssueReferenceFilter
<
ReferenceFilter
class
IssueReferenceFilter
<
AbstractReferenceFilter
include
CrossProjectReference
def
self
.
object_class
Issue
# Public: Find `#123` issue references in text
#
# IssueReferenceFilter.references_in(text) do |match, issue, project_ref|
# "<a href=...>##{issue}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, the Integer issue ID, and an optional String of
# the external project reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
Issue
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
:issue
].
to_i
,
$~
[
:project
]
end
end
def
self
.
referenced_by
(
node
)
{
issue:
LazyReference
.
new
(
Issue
,
node
.
attr
(
"data-issue"
))
}
end
def
call
replace_text_nodes_matching
(
Issue
.
reference_pattern
)
do
|
content
|
issue_link_filter
(
content
)
end
end
end
# Replace `#123` issue references in text with links to the referenced
def
find_object
(
project
,
id
)
# issue's details page.
project
.
get_issue
(
id
)
#
# text - String text to replace references in.
#
# Returns a String with `#123` references replaced with links. All links
# have `gfm` and `gfm-issue` class names attached for styling.
def
issue_link_filter
(
text
)
self
.
class
.
references_in
(
text
)
do
|
match
,
id
,
project_ref
|
project
=
self
.
project_from_ref
(
project_ref
)
if
project
&&
issue
=
project
.
get_issue
(
id
)
url
=
url_for_issue
(
id
,
project
,
only_path:
context
[
:only_path
])
title
=
escape_once
(
"Issue:
#{
issue
.
title
}
"
)
klass
=
reference_class
(
:issue
)
data
=
data_attribute
(
project:
project
.
id
,
issue:
issue
.
id
)
%(<a href="#{url}" #{data}
title="#{title}"
class="#{klass}">#{match}</a>)
else
match
end
end
end
end
def
url_for_
issue
(
*
args
)
def
url_for_
object
(
issue
,
project
)
IssuesHelper
.
url_for_issue
(
*
args
)
IssuesHelper
.
url_for_issue
(
issue
.
iid
,
project
,
only_path:
context
[
:only_path
]
)
end
end
end
end
end
end
...
...
lib/gitlab/markdown/merge_request_reference_filter.rb
View file @
5098ad9d
...
@@ -6,65 +6,16 @@ module Gitlab
...
@@ -6,65 +6,16 @@ module Gitlab
# to merge requests that do not exist are ignored.
# to merge requests that do not exist are ignored.
#
#
# This filter supports cross-project references.
# This filter supports cross-project references.
class
MergeRequestReferenceFilter
<
ReferenceFilter
class
MergeRequestReferenceFilter
<
AbstractReferenceFilter
include
CrossProjectReference
def
self
.
object_class
MergeRequest
# Public: Find `!123` merge request references in text
#
# MergeRequestReferenceFilter.references_in(text) do |match, merge_request, project_ref|
# "<a href=...>##{merge_request}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, the Integer merge request ID, and an optional
# String of the external project reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
MergeRequest
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
:merge_request
].
to_i
,
$~
[
:project
]
end
end
def
self
.
referenced_by
(
node
)
{
merge_request:
LazyReference
.
new
(
MergeRequest
,
node
.
attr
(
"data-merge-request"
))
}
end
def
call
replace_text_nodes_matching
(
MergeRequest
.
reference_pattern
)
do
|
content
|
merge_request_link_filter
(
content
)
end
end
end
# Replace `!123` merge request references in text with links to the
def
find_object
(
project
,
id
)
# referenced merge request's details page.
project
.
merge_requests
.
find_by
(
iid:
id
)
#
# text - String text to replace references in.
#
# Returns a String with `!123` references replaced with links. All links
# have `gfm` and `gfm-merge_request` class names attached for styling.
def
merge_request_link_filter
(
text
)
self
.
class
.
references_in
(
text
)
do
|
match
,
id
,
project_ref
|
project
=
self
.
project_from_ref
(
project_ref
)
if
project
&&
merge_request
=
project
.
merge_requests
.
find_by
(
iid:
id
)
title
=
escape_once
(
"Merge Request:
#{
merge_request
.
title
}
"
)
klass
=
reference_class
(
:merge_request
)
data
=
data_attribute
(
project:
project
.
id
,
merge_request:
merge_request
.
id
)
url
=
url_for_merge_request
(
merge_request
,
project
)
%(<a href="#{url}" #{data}
title="#{title}"
class="#{klass}">#{match}</a>)
else
match
end
end
end
end
def
url_for_
merge_reques
t
(
mr
,
project
)
def
url_for_
objec
t
(
mr
,
project
)
h
=
Gitlab
::
Application
.
routes
.
url_helpers
h
=
Gitlab
::
Application
.
routes
.
url_helpers
h
.
namespace_project_merge_request_url
(
project
.
namespace
,
project
,
mr
,
h
.
namespace_project_merge_request_url
(
project
.
namespace
,
project
,
mr
,
only_path:
context
[
:only_path
])
only_path:
context
[
:only_path
])
...
...
lib/gitlab/markdown/snippet_reference_filter.rb
View file @
5098ad9d
...
@@ -6,65 +6,16 @@ module Gitlab
...
@@ -6,65 +6,16 @@ module Gitlab
# snippets that do not exist are ignored.
# snippets that do not exist are ignored.
#
#
# This filter supports cross-project references.
# This filter supports cross-project references.
class
SnippetReferenceFilter
<
ReferenceFilter
class
SnippetReferenceFilter
<
AbstractReferenceFilter
include
CrossProjectReference
def
self
.
object_class
Snippet
# Public: Find `$123` snippet references in text
#
# SnippetReferenceFilter.references_in(text) do |match, snippet|
# "<a href=...>$#{snippet}</a>"
# end
#
# text - String text to search.
#
# Yields the String match, the Integer snippet ID, and an optional String
# of the external project reference.
#
# Returns a String replaced with the return of the block.
def
self
.
references_in
(
text
)
text
.
gsub
(
Snippet
.
reference_pattern
)
do
|
match
|
yield
match
,
$~
[
:snippet
].
to_i
,
$~
[
:project
]
end
end
def
self
.
referenced_by
(
node
)
{
snippet:
LazyReference
.
new
(
Snippet
,
node
.
attr
(
"data-snippet"
))
}
end
def
call
replace_text_nodes_matching
(
Snippet
.
reference_pattern
)
do
|
content
|
snippet_link_filter
(
content
)
end
end
end
# Replace `$123` snippet references in text with links to the referenced
def
find_object
(
project
,
id
)
# snippets's details page.
project
.
snippets
.
find_by
(
id:
id
)
#
# text - String text to replace references in.
#
# Returns a String with `$123` references replaced with links. All links
# have `gfm` and `gfm-snippet` class names attached for styling.
def
snippet_link_filter
(
text
)
self
.
class
.
references_in
(
text
)
do
|
match
,
id
,
project_ref
|
project
=
self
.
project_from_ref
(
project_ref
)
if
project
&&
snippet
=
project
.
snippets
.
find_by
(
id:
id
)
title
=
escape_once
(
"Snippet:
#{
snippet
.
title
}
"
)
klass
=
reference_class
(
:snippet
)
data
=
data_attribute
(
project:
project
.
id
,
snippet:
snippet
.
id
)
url
=
url_for_snippet
(
snippet
,
project
)
%(<a href="#{url}" #{data}
title="#{title}"
class="#{klass}">#{match}</a>)
else
match
end
end
end
end
def
url_for_
snippe
t
(
snippet
,
project
)
def
url_for_
objec
t
(
snippet
,
project
)
h
=
Gitlab
::
Application
.
routes
.
url_helpers
h
=
Gitlab
::
Application
.
routes
.
url_helpers
h
.
namespace_project_snippet_url
(
project
.
namespace
,
project
,
snippet
,
h
.
namespace_project_snippet_url
(
project
.
namespace
,
project
,
snippet
,
only_path:
context
[
:only_path
])
only_path:
context
[
:only_path
])
...
...
lib/gitlab/markdown/user_reference_filter.rb
View file @
5098ad9d
...
@@ -85,13 +85,12 @@ module Gitlab
...
@@ -85,13 +85,12 @@ module Gitlab
def
link_to_all
def
link_to_all
project
=
context
[
:project
]
project
=
context
[
:project
]
url
=
urls
.
namespace_project_url
(
project
.
namespace
,
project
,
url
=
urls
.
namespace_project_url
(
project
.
namespace
,
project
,
only_path:
context
[
:only_path
])
only_path:
context
[
:only_path
])
data
=
data_attribute
(
project:
project
.
id
)
data
=
data_attribute
(
project:
project
.
id
)
text
=
User
.
reference_prefix
+
'all'
text
=
User
.
reference_prefix
+
'all'
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
link_tag
(
url
,
data
,
text
)
end
end
def
link_to_namespace
(
namespace
)
def
link_to_namespace
(
namespace
)
...
@@ -105,16 +104,20 @@ module Gitlab
...
@@ -105,16 +104,20 @@ module Gitlab
def
link_to_group
(
group
,
namespace
)
def
link_to_group
(
group
,
namespace
)
url
=
urls
.
group_url
(
group
,
only_path:
context
[
:only_path
])
url
=
urls
.
group_url
(
group
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
group:
namespace
.
id
)
data
=
data_attribute
(
group:
namespace
.
id
)
text
=
Group
.
reference_prefix
+
group
text
=
Group
.
reference_prefix
+
group
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
link_tag
(
url
,
data
,
text
)
end
end
def
link_to_user
(
user
,
namespace
)
def
link_to_user
(
user
,
namespace
)
url
=
urls
.
user_url
(
user
,
only_path:
context
[
:only_path
])
url
=
urls
.
user_url
(
user
,
only_path:
context
[
:only_path
])
data
=
data_attribute
(
user:
namespace
.
owner_id
)
data
=
data_attribute
(
user:
namespace
.
owner_id
)
text
=
User
.
reference_prefix
+
user
text
=
User
.
reference_prefix
+
user
link_tag
(
url
,
data
,
text
)
end
def
link_tag
(
url
,
data
,
text
)
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
%(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
end
end
end
end
...
...
lib/tasks/flay.rake
View file @
5098ad9d
desc
'Code duplication analyze via flay'
desc
'Code duplication analyze via flay'
task
:flay
do
task
:flay
do
output
=
%x(bundle exec flay --mass 3
0
app/ lib/gitlab/)
output
=
%x(bundle exec flay --mass 3
5
app/ lib/gitlab/)
if
output
.
include?
"Similar code found"
if
output
.
include?
"Similar code found"
puts
output
puts
output
...
...
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