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
iv
gitlab-ce
Commits
7bf0add0
Commit
7bf0add0
authored
Dec 26, 2013
by
Dmitriy Zaporozhets
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'refactor/notes' of /home/git/repositories/gitlab/gitlabhq
parents
4cd7a563
5f7db3e9
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
552 additions
and
720 deletions
+552
-720
app/assets/javascripts/notes.js
app/assets/javascripts/notes.js
+0
-587
app/assets/javascripts/notes.js.coffee
app/assets/javascripts/notes.js.coffee
+418
-0
app/assets/stylesheets/sections/notes.scss
app/assets/stylesheets/sections/notes.scss
+2
-2
app/controllers/projects/commit_controller.rb
app/controllers/projects/commit_controller.rb
+2
-2
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+2
-2
app/controllers/projects/merge_requests_controller.rb
app/controllers/projects/merge_requests_controller.rb
+3
-3
app/controllers/projects/notes_controller.rb
app/controllers/projects/notes_controller.rb
+50
-62
app/controllers/projects/snippets_controller.rb
app/controllers/projects/snippets_controller.rb
+2
-2
app/helpers/notes_helper.rb
app/helpers/notes_helper.rb
+10
-9
app/models/note.rb
app/models/note.rb
+46
-23
app/views/projects/notes/_form.html.haml
app/views/projects/notes/_form.html.haml
+1
-2
app/views/projects/notes/_note.html.haml
app/views/projects/notes/_note.html.haml
+1
-1
app/views/projects/notes/_notes.html.haml
app/views/projects/notes/_notes.html.haml
+1
-1
app/views/projects/notes/_notes_with_form.html.haml
app/views/projects/notes/_notes_with_form.html.haml
+3
-2
app/views/projects/notes/create.js.haml
app/views/projects/notes/create.js.haml
+0
-18
features/steps/project/project_merge_requests.rb
features/steps/project/project_merge_requests.rb
+10
-3
spec/features/notes_on_merge_requests_spec.rb
spec/features/notes_on_merge_requests_spec.rb
+1
-1
No files found.
app/assets/javascripts/notes.js
deleted
100644 → 0
View file @
4cd7a563
This diff is collapsed.
Click to expand it.
app/assets/javascripts/notes.js.coffee
0 → 100644
View file @
7bf0add0
This diff is collapsed.
Click to expand it.
app/assets/stylesheets/sections/notes.scss
View file @
7bf0add0
...
@@ -257,12 +257,12 @@ ul.notes {
...
@@ -257,12 +257,12 @@ ul.notes {
.file
,
.file
,
.discussion
{
.discussion
{
.new_note
{
.new_note
{
margin
:
8px
5px
8px
0
;
margin
:
0
;
border
:
none
;
}
}
}
}
.new_note
{
.new_note
{
display
:
none
;
display
:
none
;
.buttons
{
.buttons
{
float
:
left
;
float
:
left
;
margin-top
:
8px
;
margin-top
:
8px
;
...
...
app/controllers/projects/commit_controller.rb
View file @
7bf0add0
...
@@ -24,8 +24,8 @@ class Projects::CommitController < Projects::ApplicationController
...
@@ -24,8 +24,8 @@ class Projects::CommitController < Projects::ApplicationController
@line_notes
=
result
[
:line_notes
]
@line_notes
=
result
[
:line_notes
]
@branches
=
result
[
:branches
]
@branches
=
result
[
:branches
]
@notes_count
=
result
[
:notes_count
]
@notes_count
=
result
[
:notes_count
]
@
target_type
=
:commit
@
notes
=
project
.
notes
.
for_commit_id
(
@commit
.
id
).
not_inline
.
fresh
@
target_id
=
@commit
.
id
@
noteable
=
@commit
@comments_allowed
=
@reply_allowed
=
true
@comments_allowed
=
@reply_allowed
=
true
@comments_target
=
{
noteable_type:
'Commit'
,
@comments_target
=
{
noteable_type:
'Commit'
,
...
...
app/controllers/projects/issues_controller.rb
View file @
7bf0add0
...
@@ -49,8 +49,8 @@ class Projects::IssuesController < Projects::ApplicationController
...
@@ -49,8 +49,8 @@ class Projects::IssuesController < Projects::ApplicationController
def
show
def
show
@note
=
@project
.
notes
.
new
(
noteable:
@issue
)
@note
=
@project
.
notes
.
new
(
noteable:
@issue
)
@
target_type
=
:issue
@
notes
=
@issue
.
notes
.
inc_author
.
fresh
@
target_id
=
@issue
.
id
@
noteable
=
@issue
respond_with
(
@issue
)
respond_with
(
@issue
)
end
end
...
...
app/controllers/projects/merge_requests_controller.rb
View file @
7bf0add0
...
@@ -198,6 +198,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
...
@@ -198,6 +198,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def
define_show_vars
def
define_show_vars
# Build a note object for comment form
# Build a note object for comment form
@note
=
@project
.
notes
.
new
(
noteable:
@merge_request
)
@note
=
@project
.
notes
.
new
(
noteable:
@merge_request
)
@notes
=
@merge_request
.
mr_and_commit_notes
.
inc_author
.
fresh
@discussions
=
Note
.
discussions_from_notes
(
@notes
)
@noteable
=
@merge_request
# Get commits from repository
# Get commits from repository
# or from cache if already merged
# or from cache if already merged
...
@@ -205,9 +208,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
...
@@ -205,9 +208,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@allowed_to_merge
=
allowed_to_merge?
@allowed_to_merge
=
allowed_to_merge?
@show_merge_controls
=
@merge_request
.
opened?
&&
@commits
.
any?
&&
@allowed_to_merge
@show_merge_controls
=
@merge_request
.
opened?
&&
@commits
.
any?
&&
@allowed_to_merge
@target_type
=
:merge_request
@target_id
=
@merge_request
.
id
end
end
def
allowed_to_merge?
def
allowed_to_merge?
...
...
app/controllers/projects/notes_controller.rb
View file @
7bf0add0
...
@@ -2,71 +2,54 @@ class Projects::NotesController < Projects::ApplicationController
...
@@ -2,71 +2,54 @@ class Projects::NotesController < Projects::ApplicationController
# Authorize
# Authorize
before_filter
:authorize_read_note!
before_filter
:authorize_read_note!
before_filter
:authorize_write_note!
,
only:
[
:create
]
before_filter
:authorize_write_note!
,
only:
[
:create
]
before_filter
:authorize_admin_note!
,
only:
[
:update
,
:destroy
]
respond_to
:js
def
index
def
index
@notes
=
Notes
::
LoadContext
.
new
(
project
,
current_user
,
params
).
execute
@notes
=
Notes
::
LoadContext
.
new
(
project
,
current_user
,
params
).
execute
@target_type
=
params
[
:target_type
].
camelize
@target_id
=
params
[
:target_id
]
if
params
[
:target_type
]
==
"merge_request"
notes_json
=
{
notes:
[]
}
@discussions
=
discussions_from_notes
end
respond_to
do
|
format
|
@notes
.
each
do
|
note
|
format
.
html
{
redirect_to
:back
}
notes_json
[
:notes
]
<<
{
format
.
json
do
id:
note
.
id
,
render
json:
{
html:
note_to_html
(
note
)
html:
view_to_html_string
(
"projects/notes/_notes"
)
}
}
end
end
end
render
json:
notes_json
end
end
def
create
def
create
@note
=
Notes
::
CreateContext
.
new
(
project
,
current_user
,
params
).
execute
@note
=
Notes
::
CreateContext
.
new
(
project
,
current_user
,
params
).
execute
@target_type
=
params
[
:target_type
].
camelize
@target_id
=
params
[
:target_id
]
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
html
{
redirect_to
:back
}
format
.
json
{
render_note_json
(
@note
)
}
format
.
js
format
.
html
{
redirect_to
:back
}
end
end
end
end
def
destroy
def
update
@note
=
@project
.
notes
.
find
(
params
[
:id
])
note
.
update_attributes
(
params
[
:note
])
return
access_denied!
unless
can?
(
current_user
,
:admin_note
,
@note
)
note
.
reset_events_cache
@note
.
destroy
@note
.
reset_events_cache
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
js
{
render
nothing:
true
}
format
.
json
{
render_note_json
(
note
)
}
format
.
html
{
redirect_to
:back
}
end
end
end
end
def
update
def
destroy
@note
=
@project
.
notes
.
find
(
params
[
:id
])
note
.
destroy
return
access_denied!
unless
can?
(
current_user
,
:admin_note
,
@note
)
note
.
reset_events_cache
@note
.
update_attributes
(
params
[
:note
])
@note
.
reset_events_cache
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
js
do
format
.
js
{
render
nothing:
true
}
render
js:
{
success:
@note
.
valid?
,
id:
@note
.
id
,
note:
view_context
.
markdown
(
@note
.
note
)
}.
to_json
end
format
.
html
do
redirect_to
:back
end
end
end
end
end
def
delete_attachment
def
delete_attachment
@note
=
@project
.
notes
.
find
(
params
[
:id
])
note
.
remove_attachment!
@note
.
remove_attachment!
note
.
update_attribute
(
:attachment
,
nil
)
@note
.
update_attribute
(
:attachment
,
nil
)
respond_to
do
|
format
|
respond_to
do
|
format
|
format
.
js
{
render
nothing:
true
}
format
.
js
{
render
nothing:
true
}
...
@@ -77,35 +60,40 @@ class Projects::NotesController < Projects::ApplicationController
...
@@ -77,35 +60,40 @@ class Projects::NotesController < Projects::ApplicationController
render
text:
view_context
.
markdown
(
params
[
:note
])
render
text:
view_context
.
markdown
(
params
[
:note
])
end
end
pr
otected
pr
ivate
def
discussion_notes_for
(
note
)
def
note
@notes
.
select
do
|
other_note
|
@note
||=
@project
.
notes
.
find
(
params
[
:id
])
note
.
discussion_id
==
other_note
.
discussion_id
end
end
end
def
discussions_from_notes
def
note_to_html
(
note
)
discussion_ids
=
[]
render_to_string
(
discussions
=
[]
"projects/notes/_note"
,
layout:
false
,
formats:
[
:html
],
locals:
{
note:
note
}
)
end
@notes
.
each
do
|
note
|
def
note_to_discussion_html
(
note
)
next
if
discussion_ids
.
include?
(
note
.
discussion_id
)
render_to_string
(
"projects/notes/_diff_notes_with_reply"
,
# don't group notes for the main target
layout:
false
,
if
note_for_main_target?
(
note
)
formats:
[
:html
],
discussions
<<
[
note
]
locals:
{
notes:
[
note
]
}
else
)
discussions
<<
discussion_notes_for
(
note
)
end
discussion_ids
<<
note
.
discussion_id
end
end
discussions
def
render_note_json
(
note
)
render
json:
{
id:
note
.
id
,
discussion_id:
note
.
discussion_id
,
html:
note_to_html
(
note
),
discussion_html:
note_to_discussion_html
(
note
)
}
end
end
# Helps to distinguish e.g. commit notes in mr notes list
def
authorize_admin_note!
def
note_for_main_target?
(
note
)
return
access_denied!
unless
can?
(
current_user
,
:admin_note
,
note
)
(
@target_type
.
camelize
==
note
.
noteable_type
&&
!
note
.
for_diff_line?
)
end
end
end
end
app/controllers/projects/snippets_controller.rb
View file @
7bf0add0
...
@@ -48,8 +48,8 @@ class Projects::SnippetsController < Projects::ApplicationController
...
@@ -48,8 +48,8 @@ class Projects::SnippetsController < Projects::ApplicationController
def
show
def
show
@note
=
@project
.
notes
.
new
(
noteable:
@snippet
)
@note
=
@project
.
notes
.
new
(
noteable:
@snippet
)
@
target_type
=
:snippet
@
notes
=
@snippet
.
notes
.
fresh
@
target_id
=
@snippet
.
id
@
noteable
=
@snippet
end
end
def
destroy
def
destroy
...
...
app/helpers/notes_helper.rb
View file @
7bf0add0
module
NotesHelper
module
NotesHelper
# Helps to distinguish e.g. commit notes in mr notes list
# Helps to distinguish e.g. commit notes in mr notes list
def
note_for_main_target?
(
note
)
def
note_for_main_target?
(
note
)
(
@
target_type
.
cameliz
e
==
note
.
noteable_type
&&
!
note
.
for_diff_line?
)
(
@
noteable
.
class
.
nam
e
==
note
.
noteable_type
&&
!
note
.
for_diff_line?
)
end
end
def
note_target_fields
def
note_target_fields
...
@@ -21,14 +21,6 @@ module NotesHelper
...
@@ -21,14 +21,6 @@ module NotesHelper
end
end
end
end
def
loading_more_notes?
params
[
:loading_more
].
present?
end
def
loading_new_notes?
params
[
:loading_new
].
present?
end
def
note_timestamp
(
note
)
def
note_timestamp
(
note
)
# Shows the created at time and the updated at time if different
# Shows the created at time and the updated at time if different
ts
=
"
#{
time_ago_with_tooltip
(
note
.
created_at
,
'bottom'
,
'note_created_ago'
)
}
ago"
ts
=
"
#{
time_ago_with_tooltip
(
note
.
created_at
,
'bottom'
,
'note_created_ago'
)
}
ago"
...
@@ -41,4 +33,13 @@ module NotesHelper
...
@@ -41,4 +33,13 @@ module NotesHelper
end
end
ts
.
html_safe
ts
.
html_safe
end
end
def
noteable_json
(
noteable
)
{
id:
noteable
.
id
,
class:
noteable
.
class
.
name
,
resources:
noteable
.
class
.
table_name
,
project_id:
noteable
.
project
.
id
,
}.
to_json
end
end
end
app/models/note.rb
View file @
7bf0add0
...
@@ -56,29 +56,52 @@ class Note < ActiveRecord::Base
...
@@ -56,29 +56,52 @@ class Note < ActiveRecord::Base
serialize
:st_diff
serialize
:st_diff
before_create
:set_diff
,
if:
->
(
n
)
{
n
.
line_code
.
present?
}
before_create
:set_diff
,
if:
->
(
n
)
{
n
.
line_code
.
present?
}
def
self
.
create_status_change_note
(
noteable
,
project
,
author
,
status
,
source
)
class
<<
self
body
=
"_Status changed to
#{
status
}#{
' by '
+
source
.
gfm_reference
if
source
}
_"
def
create_status_change_note
(
noteable
,
project
,
author
,
status
,
source
)
body
=
"_Status changed to
#{
status
}#{
' by '
+
source
.
gfm_reference
if
source
}
_"
create
({
noteable:
noteable
,
create
({
project:
project
,
noteable:
noteable
,
author:
author
,
project:
project
,
note:
body
,
author:
author
,
system:
true
note:
body
,
},
without_protection:
true
)
system:
true
end
},
without_protection:
true
)
end
# +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note.
# Create a system Note associated with +noteable+ with a GFM back-reference to +mentioner+.
def
create_cross_reference_note
(
noteable
,
mentioner
,
author
,
project
)
create
({
noteable:
noteable
,
commit_id:
(
noteable
.
sha
if
noteable
.
respond_to?
:sha
),
project:
project
,
author:
author
,
note:
"_mentioned in
#{
mentioner
.
gfm_reference
}
_"
,
system:
true
},
without_protection:
true
)
end
# +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note.
def
discussions_from_notes
(
notes
)
# Create a system Note associated with +noteable+ with a GFM back-reference to +mentioner+.
discussion_ids
=
[]
def
self
.
create_cross_reference_note
(
noteable
,
mentioner
,
author
,
project
)
discussions
=
[]
create
({
noteable:
noteable
,
notes
.
each
do
|
note
|
commit_id:
(
noteable
.
sha
if
noteable
.
respond_to?
:sha
),
next
if
discussion_ids
.
include?
(
note
.
discussion_id
)
project:
project
,
author:
author
,
# don't group notes for the main target
note:
"_mentioned in
#{
mentioner
.
gfm_reference
}
_"
,
if
!
note
.
for_diff_line?
&&
note
.
noteable_type
==
"MergeRequest"
system:
true
discussions
<<
[
note
]
},
without_protection:
true
)
else
discussions
<<
notes
.
select
do
|
other_note
|
note
.
discussion_id
==
other_note
.
discussion_id
end
discussion_ids
<<
note
.
discussion_id
end
end
discussions
end
end
end
# Determine whether or not a cross-reference note already exists.
# Determine whether or not a cross-reference note already exists.
...
@@ -89,7 +112,7 @@ class Note < ActiveRecord::Base
...
@@ -89,7 +112,7 @@ class Note < ActiveRecord::Base
def
commit_author
def
commit_author
@commit_author
||=
@commit_author
||=
project
.
users
.
find_by_email
(
noteable
.
author_email
)
||
project
.
users
.
find_by_email
(
noteable
.
author_email
)
||
project
.
users
.
find_by_name
(
noteable
.
author_name
)
project
.
users
.
find_by_name
(
noteable
.
author_name
)
rescue
rescue
nil
nil
end
end
...
...
app/views/projects/notes/_form.html.haml
View file @
7bf0add0
=
form_for
[
@project
,
@note
],
remote:
true
,
html:
{
multipart:
true
,
id:
nil
,
class:
"new_note js-new-note-form common-note-form"
},
authenticity_token:
true
do
|
f
|
=
form_for
[
@project
,
@note
],
remote:
true
,
html:
{
:'data-type'
=>
'json'
,
multipart:
true
,
id:
nil
,
class:
"new_note js-new-note-form common-note-form"
},
authenticity_token:
true
do
|
f
|
=
note_target_fields
=
note_target_fields
=
f
.
hidden_field
:commit_id
=
f
.
hidden_field
:commit_id
=
f
.
hidden_field
:line_code
=
f
.
hidden_field
:line_code
...
...
app/views/projects/notes/_note.html.haml
View file @
7bf0add0
...
@@ -34,7 +34,7 @@
...
@@ -34,7 +34,7 @@
=
markdown
(
note
.
note
)
=
markdown
(
note
.
note
)
.note-edit-form
.note-edit-form
=
form_for
note
,
url:
project_note_path
(
@project
,
note
),
method: :put
,
remote:
true
do
|
f
|
=
form_for
note
,
url:
project_note_path
(
@project
,
note
),
method: :put
,
remote:
true
,
authenticity_token:
true
do
|
f
|
=
f
.
text_area
:note
,
class:
'note_text js-note-text js-gfm-input turn-on'
=
f
.
text_area
:note
,
class:
'note_text js-note-text js-gfm-input turn-on'
.form-actions
.form-actions
...
...
app/views/projects/notes/_notes.html.haml
View file @
7bf0add0
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
-
if
note_for_main_target?
(
note
)
-
if
note_for_main_target?
(
note
)
=
render
discussion_notes
=
render
discussion_notes
-
else
-
else
=
render
'discussion'
,
discussion_notes:
discussion_notes
=
render
'
projects/notes/
discussion'
,
discussion_notes:
discussion_notes
-
else
-
else
-
@notes
.
each
do
|
note
|
-
@notes
.
each
do
|
note
|
-
next
unless
note
.
author
-
next
unless
note
.
author
...
...
app/views/projects/notes/_notes_with_form.html.haml
View file @
7bf0add0
%ul
#notes-list
.notes
%ul
#notes-list
.notes.main-notes-list
=
render
"projects/notes/notes"
.js-notes-busy
.js-notes-busy
.js-main-target-form
.js-main-target-form
...
@@ -6,4 +7,4 @@
...
@@ -6,4 +7,4 @@
=
render
"projects/notes/form"
=
render
"projects/notes/form"
:javascript
:javascript
NoteList
.
init
(
"
#{
@target_id
}
"
,
"
#{
@target_type
}
"
,
"
#{
project_notes_path
(
@project
)
}
"
);
new
Notes
(
"
#{
project_notes_path
(
target_id:
@noteable
.
id
,
target_type:
@noteable
.
class
.
name
.
underscore
)
}
"
,
#{
@notes
.
map
(
&
:id
).
to_json
}
)
app/views/projects/notes/create.js.haml
deleted
100644 → 0
View file @
4cd7a563
-
if
@note
.
valid?
var noteHtml = "
#{
escape_javascript
(
render
@note
)
}
";
-
if
note_for_main_target?
(
@note
)
NoteList.appendNewNote(
#{
@note
.
id
}
, noteHtml);
-
else
:plain
var firstDiscussionNoteHtml = "
#{
escape_javascript
(
render
"projects/notes/diff_notes_with_reply"
,
notes:
[
@note
])
}
";
NoteList.appendNewDiscussionNote("
#{
@note
.
discussion_id
}
",
firstDiscussionNoteHtml,
noteHtml);
-
else
var errorsHtml = "
#{
escape_javascript
(
render
'projects/notes/form_errors'
,
note:
@note
)
}
";
-
if
note_for_main_target?
(
@note
)
NoteList.errorsOnForm(errorsHtml);
-
else
NoteList.errorsOnForm(errorsHtml, "
#{
@note
.
discussion_id
}
");
features/steps/project/project_merge_requests.rb
View file @
7bf0add0
...
@@ -115,19 +115,26 @@ class ProjectMergeRequests < Spinach::FeatureSteps
...
@@ -115,19 +115,26 @@ class ProjectMergeRequests < Spinach::FeatureSteps
And
'I leave a comment on the diff page'
do
And
'I leave a comment on the diff page'
do
init_diff_note
init_diff_note
within
(
'.js-
temp-notes-holder
'
)
do
within
(
'.js-
discussion-note-form
'
)
do
fill_in
"note_note"
,
with:
"One comment to rule them all"
fill_in
"note_note"
,
with:
"One comment to rule them all"
click_button
"Add Comment"
click_button
"Add Comment"
end
end
within
".note-text"
do
page
.
should
have_content
"One comment to rule them all"
end
end
end
And
'I leave a comment like "Line is wrong" on line 185 of the first file'
do
And
'I leave a comment like "Line is wrong" on line 185 of the first file'
do
init_diff_note
init_diff_note
within
(
".js-
temp-notes-holder
"
)
do
within
(
".js-
discussion-note-form
"
)
do
fill_in
"note_note"
,
with:
"Line is wrong"
fill_in
"note_note"
,
with:
"Line is wrong"
click_button
"Add Comment"
click_button
"Add Comment"
sleep
0.05
end
within
".note-text"
do
page
.
should
have_content
"Line is wrong"
end
end
end
end
...
...
spec/features/notes_on_merge_requests_spec.rb
View file @
7bf0add0
...
@@ -108,7 +108,7 @@ describe "On a merge request", js: true do
...
@@ -108,7 +108,7 @@ describe "On a merge request", js: true do
within
(
"#note_
#{
note
.
id
}
"
)
do
within
(
"#note_
#{
note
.
id
}
"
)
do
should
have_css
(
".note-last-update small"
)
should
have_css
(
".note-last-update small"
)
find
(
".note-last-update small"
).
text
.
should
match
(
/Edited
just now
/
)
find
(
".note-last-update small"
).
text
.
should
match
(
/Edited
less than a minute ago
/
)
end
end
end
end
end
end
...
...
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