Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
450dfe17
Commit
450dfe17
authored
Apr 09, 2014
by
Valera Rozuvan
Committed by
Valera Rozuvan
Apr 23, 2014
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
TESTS: Add tests for fix of end-time in the video player.
BLD-662
parent
dc373f22
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
290 additions
and
92 deletions
+290
-92
common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
+1
-1
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
+27
-11
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
+5
-0
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
+2
-1
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
+105
-35
common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
+13
-2
lms/djangoapps/courseware/features/common.py
+1
-1
lms/djangoapps/courseware/features/video.feature
+112
-30
lms/djangoapps/courseware/features/video.py
+24
-11
No files found.
common/lib/xmodule/xmodule/js/spec/video/html5_video_spec.js
View file @
450dfe17
...
@@ -117,7 +117,7 @@
...
@@ -117,7 +117,7 @@
runs
(
function
()
{
runs
(
function
()
{
expect
(
state
.
videoPlayer
.
player
.
getPlayerState
())
expect
(
state
.
videoPlayer
.
player
.
getPlayerState
())
.
toBe
(
STATUS
.
PLAY
ING
);
.
toBe
(
STATUS
.
BUFFER
ING
);
});
});
});
});
...
...
common/lib/xmodule/xmodule/js/spec/video/video_player_spec.js
View file @
450dfe17
...
@@ -366,19 +366,22 @@ function (VideoPlayer) {
...
@@ -366,19 +366,22 @@ function (VideoPlayer) {
});
});
it
(
'Slider event causes log update'
,
function
()
{
it
(
'Slider event causes log update'
,
function
()
{
runs
(
function
()
{
runs
(
function
()
{
var
currentTime
=
state
.
videoPlayer
.
currentTime
;
spyOn
(
state
.
videoPlayer
,
'log'
);
spyOn
(
state
.
videoPlayer
,
'log'
);
state
.
videoProgressSlider
.
onSlide
(
state
.
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
2
}
jQuery
.
Event
(
'slide'
),
{
value
:
2
}
);
);
});
waitsFor
(
function
()
{
return
state
.
videoPlayer
.
currentTime
>=
2
;
},
'currentTime is less than 2 seconds'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
state
.
videoPlayer
.
log
).
toHaveBeenCalledWith
(
expect
(
state
.
videoPlayer
.
log
).
toHaveBeenCalledWith
(
'seek_video'
,
'seek_video'
,
{
{
old_time
:
currentTime
,
old_time
:
jasmine
.
any
(
Number
)
,
new_time
:
2
,
new_time
:
2
,
type
:
'onSlideSeek'
type
:
'onSlideSeek'
}
}
...
@@ -388,25 +391,37 @@ function (VideoPlayer) {
...
@@ -388,25 +391,37 @@ function (VideoPlayer) {
it
(
'seek the player'
,
function
()
{
it
(
'seek the player'
,
function
()
{
runs
(
function
()
{
runs
(
function
()
{
spyOn
(
state
.
videoPlayer
.
player
,
'seekTo'
);
spyOn
(
state
.
videoPlayer
.
player
,
'seekTo'
)
.
andCallThrough
()
;
state
.
videoProgressSlider
.
onSlide
(
state
.
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
6
0
}
jQuery
.
Event
(
'slide'
),
{
value
:
3
0
}
);
);
});
waitsFor
(
function
()
{
return
state
.
videoPlayer
.
currentTime
>=
30
;
},
'currentTime is less than 30 seconds'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
state
.
videoPlayer
.
player
.
seekTo
)
expect
(
state
.
videoPlayer
.
player
.
seekTo
)
.
toHaveBeenCalledWith
(
6
0
,
true
);
.
toHaveBeenCalledWith
(
3
0
,
true
);
});
});
});
});
it
(
'call updatePlayTime on player'
,
function
()
{
it
(
'call updatePlayTime on player'
,
function
()
{
runs
(
function
()
{
runs
(
function
()
{
spyOn
(
state
.
videoPlayer
,
'updatePlayTime'
);
spyOn
(
state
.
videoPlayer
,
'updatePlayTime'
)
.
andCallThrough
()
;
state
.
videoProgressSlider
.
onSlide
(
state
.
videoProgressSlider
.
onSlide
(
jQuery
.
Event
(
'slide'
),
{
value
:
6
0
}
jQuery
.
Event
(
'slide'
),
{
value
:
3
0
}
);
);
});
waitsFor
(
function
()
{
return
state
.
videoPlayer
.
currentTime
>=
30
;
},
'currentTime is less than 30 seconds'
,
WAIT_TIMEOUT
);
runs
(
function
()
{
expect
(
state
.
videoPlayer
.
updatePlayTime
)
expect
(
state
.
videoPlayer
.
updatePlayTime
)
.
toHaveBeenCalledWith
(
60
);
.
toHaveBeenCalledWith
(
jasmine
.
any
(
Number
)
);
});
});
});
});
...
@@ -1070,6 +1085,7 @@ function (VideoPlayer) {
...
@@ -1070,6 +1085,7 @@ function (VideoPlayer) {
youtubeId
:
jasmine
.
createSpy
().
andReturn
(
'videoId'
),
youtubeId
:
jasmine
.
createSpy
().
andReturn
(
'videoId'
),
isFlashMode
:
jasmine
.
createSpy
().
andReturn
(
false
),
isFlashMode
:
jasmine
.
createSpy
().
andReturn
(
false
),
isHtml5Mode
:
jasmine
.
createSpy
().
andReturn
(
true
),
isHtml5Mode
:
jasmine
.
createSpy
().
andReturn
(
true
),
isYoutubeType
:
jasmine
.
createSpy
().
andReturn
(
true
),
setPlayerMode
:
jasmine
.
createSpy
(),
setPlayerMode
:
jasmine
.
createSpy
(),
videoPlayer
:
{
videoPlayer
:
{
currentTime
:
60
,
currentTime
:
60
,
...
@@ -1104,12 +1120,12 @@ function (VideoPlayer) {
...
@@ -1104,12 +1120,12 @@ function (VideoPlayer) {
});
});
it
(
'in HTML5 mode'
,
function
()
{
it
(
'in HTML5 mode'
,
function
()
{
state
.
isYoutubeType
.
andReturn
(
false
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
VideoPlayer
.
prototype
.
setPlaybackRate
.
call
(
state
,
'0.75'
);
expect
(
state
.
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
expect
(
state
.
videoPlayer
.
player
.
setPlaybackRate
).
toHaveBeenCalledWith
(
'0.75'
);
});
});
it
(
'Youtube video in FF, with new speed equal 1.0'
,
function
()
{
it
(
'Youtube video in FF, with new speed equal 1.0'
,
function
()
{
state
.
videoType
=
'youtube'
;
state
.
browserIsFirefox
=
true
;
state
.
browserIsFirefox
=
true
;
state
.
videoPlayer
.
isPlaying
.
andReturn
(
false
);
state
.
videoPlayer
.
isPlaying
.
andReturn
(
false
);
...
...
common/lib/xmodule/xmodule/js/src/video/01_initialize.js
View file @
450dfe17
...
@@ -68,6 +68,7 @@ function (VideoPlayer, VideoStorage) {
...
@@ -68,6 +68,7 @@ function (VideoPlayer, VideoStorage) {
initialize
:
initialize
,
initialize
:
initialize
,
isHtml5Mode
:
isHtml5Mode
,
isHtml5Mode
:
isHtml5Mode
,
isFlashMode
:
isFlashMode
,
isFlashMode
:
isFlashMode
,
isYoutubeType
:
isYoutubeType
,
parseSpeed
:
parseSpeed
,
parseSpeed
:
parseSpeed
,
parseVideoSources
:
parseVideoSources
,
parseVideoSources
:
parseVideoSources
,
parseYoutubeStreams
:
parseYoutubeStreams
,
parseYoutubeStreams
:
parseYoutubeStreams
,
...
@@ -847,6 +848,10 @@ function (VideoPlayer, VideoStorage) {
...
@@ -847,6 +848,10 @@ function (VideoPlayer, VideoStorage) {
return
this
.
getPlayerMode
()
===
'html5'
;
return
this
.
getPlayerMode
()
===
'html5'
;
}
}
function
isYoutubeType
()
{
return
this
.
videoType
===
'youtube'
;
}
function
speedToString
(
speed
)
{
function
speedToString
(
speed
)
{
return
parseFloat
(
speed
).
toFixed
(
2
).
replace
(
/
\.
00$/
,
'.0'
);
return
parseFloat
(
speed
).
toFixed
(
2
).
replace
(
/
\.
00$/
,
'.0'
);
}
}
...
...
common/lib/xmodule/xmodule/js/src/video/02_html5_video.js
View file @
450dfe17
...
@@ -275,12 +275,13 @@ function () {
...
@@ -275,12 +275,13 @@ function () {
// Register the 'play' event.
// Register the 'play' event.
this
.
video
.
addEventListener
(
'play'
,
function
()
{
this
.
video
.
addEventListener
(
'play'
,
function
()
{
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PLAY
ING
;
_this
.
playerState
=
HTML5Video
.
PlayerState
.
BUFFER
ING
;
_this
.
callStateChangeCallback
();
_this
.
callStateChangeCallback
();
},
false
);
},
false
);
this
.
video
.
addEventListener
(
'playing'
,
function
()
{
this
.
video
.
addEventListener
(
'playing'
,
function
()
{
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PLAYING
;
_this
.
playerState
=
HTML5Video
.
PlayerState
.
PLAYING
;
_this
.
callStateChangeCallback
();
},
false
);
},
false
);
// Register the 'pause' event.
// Register the 'pause' event.
...
...
common/lib/xmodule/xmodule/js/src/video/03_video_player.js
View file @
450dfe17
This diff is collapsed.
Click to expand it.
common/lib/xmodule/xmodule/js/src/video/06_video_progress_slider.js
View file @
450dfe17
...
@@ -177,15 +177,26 @@ function () {
...
@@ -177,15 +177,26 @@ function () {
}
}
function
onSlide
(
event
,
ui
)
{
function
onSlide
(
event
,
ui
)
{
var
time
=
ui
.
value
,
duration
=
this
.
videoPlayer
.
duration
();
this
.
videoProgressSlider
.
frozen
=
true
;
this
.
videoProgressSlider
.
frozen
=
true
;
// Remember the seek to value so that we don't repeat ourselves on the
// Remember the seek to value so that we don't repeat ourselves on the
// 'stop' slider event.
// 'stop' slider event.
this
.
videoProgressSlider
.
lastSeekValue
=
ui
.
value
;
this
.
videoProgressSlider
.
lastSeekValue
=
time
;
this
.
trigger
(
'videoControl.updateVcrVidTime'
,
{
time
:
time
,
duration
:
duration
}
);
this
.
trigger
(
this
.
trigger
(
'videoPlayer.onSlideSeek'
,
'videoPlayer.onSlideSeek'
,
{
'type'
:
'onSlideSeek'
,
'time'
:
ui
.
valu
e
}
{
'type'
:
'onSlideSeek'
,
'time'
:
tim
e
}
);
);
// ARIA
// ARIA
...
...
lms/djangoapps/courseware/features/common.py
View file @
450dfe17
...
@@ -35,7 +35,7 @@ def configure_screenshots_for_all_steps(_step, action):
...
@@ -35,7 +35,7 @@ def configure_screenshots_for_all_steps(_step, action):
else
:
else
:
raise
ValueError
(
'Parameter `action` should be one of "enable" or "disable".'
)
raise
ValueError
(
'Parameter `action` should be one of "enable" or "disable".'
)
@world.absorb
def
capture_screenshot_before_after
(
func
):
def
capture_screenshot_before_after
(
func
):
"""
"""
A decorator that will take a screenshot before and after the applied
A decorator that will take a screenshot before and after the applied
...
...
lms/djangoapps/courseware/features/video.feature
View file @
450dfe17
...
@@ -2,18 +2,6 @@
...
@@ -2,18 +2,6 @@
Feature
:
LMS.Video component
Feature
:
LMS.Video component
As a student, I want to view course videos in LMS
As a student, I want to view course videos in LMS
# 1 Disabled 4/8/14 after intermittent failures in master
#Scenario: Video component stores position correctly when page is reloaded
# Given the course has a Video component in "Youtube" mode
# When the video has rendered in "Youtube" mode
# And I click video button "play"
# And I click video button "pause"
# Then I seek video to "10" seconds
# And I click video button "play"
# And I click video button "pause"
# And I reload the page with video
# Then I see video slider at "10" seconds
# 1
# 1
Scenario
:
Video component is fully rendered in the LMS in HTML5 mode
Scenario
:
Video component is fully rendered in the LMS in HTML5 mode
Given
the course has a Video component in
"HTML5"
mode
Given
the course has a Video component in
"HTML5"
mode
...
@@ -267,7 +255,106 @@ Feature: LMS.Video component
...
@@ -267,7 +255,106 @@ Feature: LMS.Video component
Then
the video has rendered in
"HTML5"
mode
Then
the video has rendered in
"HTML5"
mode
And
the video does not show the captions
And
the video does not show the captions
# 20 Disabled 4/8/14 after intermittent failures in master
# 20
Scenario
:
Start time works for Youtube video
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
start_time
|
|
00:00:10
|
And
I click video button
"play"
Then I see video slider at "0
:
10"
position
# 21
Scenario
:
End time works for Youtube video
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
end_time
|
|
00:00:02
|
And
I click video button
"play"
And
I wait
"5"
seconds
Then I see video slider at "0
:
02"
position
# 22
Scenario
:
Youtube video with end-time at 1
:
00 and the video starts playing at 0
:
58
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
end_time
|
|
00:01:00
|
And
I wait for video controls appear
And I seek video to "0
:
58"
position
And
I click video button
"play"
And
I wait
"5"
seconds
Then I see video slider at "1
:
00"
position
# 23
Scenario
:
Start time and end time work together for Youtube video
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
start_time
|
end_time
|
|
00:00:10
|
00:00:12
|
And
I click video button
"play"
Then I see video slider at "0
:
10"
position
And
I wait
"5"
seconds
Then I see video slider at "0
:
12"
position
# 24
Scenario
:
Youtube video after pausing at end time video plays to the end from end time
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
start_time
|
end_time
|
|
00:01:51
|
00:01:52
|
And
I click video button
"play"
And
I wait
"5"
seconds
# The end time is 00:01:52.
Then I see video slider at "1
:
52"
position
And
I click video button
"play"
And
I wait
"8"
seconds
# The default video length is 00:01:55.
Then I see video slider at "1
:
55"
position
# 25
Scenario
:
Youtube video with end-time at 0
:
32 and start-time at 0
:
30, the video starts playing from 0
:
28
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
start_time
|
end_time
|
|
00:00:30
|
00:00:32
|
And
I wait for video controls appear
And I seek video to "0
:
28"
position
And
I click video button
"play"
And
I wait
"8"
seconds
Then I see video slider at "0
:
32"
position
# 26
Scenario
:
Youtube video with end-time at 1
:
00, the video starts playing from 1
:
52
Given
I am registered for the course
"test_course"
And it has a video in "Youtube" mode
:
|
end_time
|
|
00:01:00
|
And
I wait for video controls appear
And I seek video to "1
:
52"
position
And
I click video button
"play"
And
I wait
"5"
seconds
# Video stops at the end.
Then I see video slider at "1
:
55"
position
# 27
@skip_firefox
Scenario
:
Quality button appears on play
Given
the course has a Video component in
"Youtube"
mode
Then
I see video button
"quality"
is hidden
And
I click video button
"play"
Then
I see video button
"quality"
is visible
# 28
@skip_firefox
Scenario
:
Quality button works correctly
Given
the course has a Video component in
"Youtube"
mode
And
I click video button
"play"
And
I see video button
"quality"
is inactive
And
I click video button
"quality"
Then
I see video button
"quality"
is active
# 29 Disabled 4/8/14 after intermittent failures in master
#Scenario: Transcripts are available on different speeds of Flash mode
#Scenario: Transcripts are available on different speeds of Flash mode
# Given I am registered for the course "test_course"
# Given I am registered for the course "test_course"
# And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
# And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
...
@@ -282,7 +369,7 @@ Feature: LMS.Video component
...
@@ -282,7 +369,7 @@ Feature: LMS.Video component
# Then I select the "1.25" speed
# Then I select the "1.25" speed
# And I see "Hi, welcome to Edx." text in the captions
# And I see "Hi, welcome to Edx." text in the captions
#
21
Disabled 4/8/14 after intermittent failures in master
#
30
Disabled 4/8/14 after intermittent failures in master
#Scenario: Elapsed time calculates correctly on different speeds of Flash mode
#Scenario: Elapsed time calculates correctly on different speeds of Flash mode
# Given I am registered for the course "test_course"
# Given I am registered for the course "test_course"
# And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
# And I have a "subs_OEoXaMPEzfM.srt.sjson" transcript file in assets
...
@@ -298,19 +385,14 @@ Feature: LMS.Video component
...
@@ -298,19 +385,14 @@ Feature: LMS.Video component
# And I click video button "pause"
# And I click video button "pause"
# And I click on caption line "2", video module shows elapsed time "4"
# And I click on caption line "2", video module shows elapsed time "4"
# 27
# 31 Disabled 4/8/14 after intermittent failures in master
@skip_firefox
#Scenario: Video component stores position correctly when page is reloaded
Scenario
:
Quality button appears on play
# Given the course has a Video component in "Youtube" mode
Given
the course has a Video component in
"Youtube"
mode
# When the video has rendered in "Youtube" mode
Then
I see video button
"quality"
is hidden
# And I click video button "play"
And
I click video button
"play"
# And I click video button "pause"
Then
I see video button
"quality"
is visible
# Then I seek video to "0:10" position
# And I click video button "play"
# 28
# And I click video button "pause"
@skip_firefox
# And I reload the page with video
Scenario
:
Quality button works correctly
# Then I see video slider at "0:10" position
Given
the course has a Video component in
"Youtube"
mode
And
I click video button
"play"
And
I see video button
"quality"
is inactive
And
I click video button
"quality"
Then
I see video button
"quality"
is active
lms/djangoapps/courseware/features/video.py
View file @
450dfe17
...
@@ -482,6 +482,7 @@ def check_captions(_step):
...
@@ -482,6 +482,7 @@ def check_captions(_step):
@step
(
'I select language with code "([^"]*)"$'
)
@step
(
'I select language with code "([^"]*)"$'
)
def
select_language
(
_step
,
code
):
def
select_language
(
_step
,
code
):
world
.
wait_for_visible
(
'.video-controls'
)
# Make sure that all ajax requests that affects the language menu are finished.
# Make sure that all ajax requests that affects the language menu are finished.
# For example, request to get new translation etc.
# For example, request to get new translation etc.
world
.
wait_for_ajax_complete
()
world
.
wait_for_ajax_complete
()
...
@@ -506,16 +507,19 @@ def select_language(_step, code):
...
@@ -506,16 +507,19 @@ def select_language(_step, code):
@step
(
'I click video button "([^"]*)"$'
)
@step
(
'I click video button "([^"]*)"$'
)
def
click_button
(
_step
,
button
):
def
click_button
(
_step
,
button
):
world
.
css_click
(
VIDEO_BUTTONS
[
button
])
world
.
css_click
(
VIDEO_BUTTONS
[
button
])
world
.
wait_for_ajax_complete
()
if
button
==
"play"
:
# Needs to wait for video buffrization
@step
(
'I see video slider at "([^"]*)" seconds$'
)
def
start_playing_video_from_n_seconds
(
_step
,
position
):
world
.
wait_for
(
world
.
wait_for
(
func
=
lambda
_
:
elapsed_time
()
>
0
,
func
=
lambda
_
:
world
.
css_has_class
(
'.video'
,
'is-playing'
)
and
world
.
is_css_present
(
VIDEO_BUTTONS
[
'pause'
])
,
timeout
=
30
timeout
=
30
)
)
world
.
wait_for_ajax_complete
()
@step
(
'I see video slider at "([^"]*)" position$'
)
def
start_playing_video_from_n_seconds
(
_step
,
time_str
):
position
=
parse_time_str
(
time_str
)
actual_position
=
elapsed_time
()
actual_position
=
elapsed_time
()
assert_equal
(
actual_position
,
int
(
position
),
"Current position is {}, but should be {}"
.
format
(
actual_position
,
position
))
assert_equal
(
actual_position
,
int
(
position
),
"Current position is {}, but should be {}"
.
format
(
actual_position
,
position
))
...
@@ -530,12 +534,21 @@ def i_see_duration(_step, position):
...
@@ -530,12 +534,21 @@ def i_see_duration(_step, position):
assert
duration
()
==
parse_time_str
(
position
)
assert
duration
()
==
parse_time_str
(
position
)
@step
(
'I seek video to "([^"]*)" seconds$'
)
@step
(
'I wait for video controls appear$'
)
def
seek_video_to_n_seconds
(
_step
,
seconds
):
def
controls_appear
(
_step
):
time
=
float
(
seconds
.
strip
())
world
.
wait_for_visible
(
'.video-controls'
)
jsCode
=
"$('.video').data('video-player-state').videoPlayer.onSlideSeek({{time: {0:f}}})"
.
format
(
time
)
@step
(
'I seek video to "([^"]*)" position$'
)
def
seek_video_to_n_seconds
(
_step
,
time_str
):
time
=
parse_time_str
(
time_str
)
jsCode
=
"$('.video').data('video-player-state').videoPlayer.onSlideSeek({{time: {0}}})"
.
format
(
time
)
world
.
browser
.
execute_script
(
jsCode
)
world
.
browser
.
execute_script
(
jsCode
)
_step
.
given
(
'I see video slider at "{}" seconds'
.
format
(
seconds
))
world
.
wait_for
(
func
=
lambda
_
:
world
.
retry_on_exception
(
lambda
:
elapsed_time
()
==
time
and
not
world
.
css_has_class
(
'.video'
,
'is-buffering'
)),
timeout
=
30
)
_step
.
given
(
'I see video slider at "{0}" position'
.
format
(
time_str
))
@step
(
'I have a "([^"]*)" transcript file in assets$'
)
@step
(
'I have a "([^"]*)" transcript file in assets$'
)
...
...
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