Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
2658f557
Commit
2658f557
authored
Oct 02, 2020
by
Chad Woolley
Committed by
Mayra Cabrera
Oct 02, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Changelog for new 'mounts' entry to SSE config file
Document addition of 'mounts' entry
parent
c2bf084d
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
361 additions
and
42 deletions
+361
-42
app/assets/javascripts/static_site_editor/index.js
app/assets/javascripts/static_site_editor/index.js
+10
-1
app/controllers/projects/static_site_editor_controller.rb
app/controllers/projects/static_site_editor_controller.rb
+12
-1
changelogs/unreleased/241166-sse-config-mounts.yml
changelogs/unreleased/241166-sse-config-mounts.yml
+5
-0
lib/gitlab/static_site_editor/config/file_config/entry/global.rb
...lab/static_site_editor/config/file_config/entry/global.rb
+3
-0
lib/gitlab/static_site_editor/config/file_config/entry/mount.rb
...tlab/static_site_editor/config/file_config/entry/mount.rb
+39
-0
lib/gitlab/static_site_editor/config/file_config/entry/mounts.rb
...lab/static_site_editor/config/file_config/entry/mounts.rb
+33
-0
lib/gitlab/static_site_editor/config/generated_config.rb
lib/gitlab/static_site_editor/config/generated_config.rb
+1
-1
spec/controllers/projects/static_site_editor_controller_spec.rb
...ontrollers/projects/static_site_editor_controller_spec.rb
+61
-25
spec/features/static_site_editor_spec.rb
spec/features/static_site_editor_spec.rb
+13
-4
spec/lib/gitlab/static_site_editor/config/file_config/entry/global_spec.rb
...tatic_site_editor/config/file_config/entry/global_spec.rb
+22
-2
spec/lib/gitlab/static_site_editor/config/file_config/entry/mount_spec.rb
...static_site_editor/config/file_config/entry/mount_spec.rb
+101
-0
spec/lib/gitlab/static_site_editor/config/file_config/entry/mounts_spec.rb
...tatic_site_editor/config/file_config/entry/mounts_spec.rb
+53
-0
spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb
...gitlab/static_site_editor/config/generated_config_spec.rb
+8
-8
No files found.
app/assets/javascripts/static_site_editor/index.js
View file @
2658f557
...
...
@@ -12,10 +12,19 @@ const initStaticSiteEditor = el => {
namespace
,
project
,
mergeRequestsIllustrationPath
,
// NOTE: The following variables are not yet used, but are supported by the config file,
// so we are adding them here as a convenience for future use.
// eslint-disable-next-line no-unused-vars
staticSiteGenerator
,
// eslint-disable-next-line no-unused-vars
imageUploadPath
,
mounts
,
}
=
el
.
dataset
;
// NOTE that the object in 'mounts' is a JSON string from the data attribute, so it must be parsed into an object.
// eslint-disable-next-line no-unused-vars
const
mountsObject
=
JSON
.
parse
(
mounts
);
const
{
current_username
:
username
}
=
window
.
gon
;
const
returnUrl
=
el
.
dataset
.
returnUrl
||
null
;
const
router
=
createRouter
(
baseUrl
);
const
apolloProvider
=
createApolloProvider
({
isSupportedContent
:
parseBoolean
(
isSupportedContent
),
...
...
app/controllers/projects/static_site_editor_controller.rb
View file @
2658f557
...
...
@@ -25,7 +25,7 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
).
execute
if
service_response
.
success?
@data
=
ser
vice_response
.
payload
@data
=
ser
ialize_necessary_payload_values_to_json
(
service_response
.
payload
)
else
# TODO: For now, if the service returns any error, the user is redirected
# to the root project page with the error message displayed as an alert.
...
...
@@ -38,6 +38,17 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
private
def
serialize_necessary_payload_values_to_json
(
payload
)
# This will convert booleans, Array-like and Hash-like objects to JSON
payload
.
transform_values
do
|
value
|
if
value
.
is_a?
(
String
)
||
value
.
is_a?
(
Integer
)
value
else
value
.
to_json
end
end
end
def
assign_ref_and_path
@ref
,
@path
=
extract_ref
(
params
.
fetch
(
:id
))
...
...
changelogs/unreleased/241166-sse-config-mounts.yml
0 → 100644
View file @
2658f557
---
title
:
Introduce 'mounts' entry support for '.gitlab/static-site-editor.yml' config file.
merge_request
:
43485
author
:
type
:
added
lib/gitlab/static_site_editor/config/file_config/entry/global.rb
View file @
2658f557
...
...
@@ -15,6 +15,7 @@ module Gitlab
ALLOWED_KEYS
=
%i[
image_upload_path
mounts
static_site_generator
]
.
freeze
...
...
@@ -26,6 +27,8 @@ module Gitlab
entry
:image_upload_path
,
Entry
::
ImageUploadPath
,
description:
'Configuration of the Static Site Editor image upload path.'
entry
:mounts
,
Entry
::
Mounts
,
description:
'Configuration of the Static Site Editor mounts.'
entry
:static_site_generator
,
Entry
::
StaticSiteGenerator
,
description:
'Configuration of the Static Site Editor static site generator.'
end
...
...
lib/gitlab/static_site_editor/config/file_config/entry/mount.rb
0 → 100644
View file @
2658f557
# frozen_string_literal: true
module
Gitlab
module
StaticSiteEditor
module
Config
class
FileConfig
module
Entry
##
# Entry that represents the mappings of mounted source directories to target paths
#
class
Mount
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Validatable
include
::
Gitlab
::
Config
::
Entry
::
Attributable
ALLOWED_KEYS
=
%i[source target]
.
freeze
attributes
ALLOWED_KEYS
validations
do
validates
:config
,
allowed_keys:
ALLOWED_KEYS
validates
:source
,
type:
String
,
presence:
true
validates
:target
,
type:
String
,
presence:
true
,
allow_blank:
true
end
def
self
.
default
# NOTE: This is the default for middleman projects. Ideally, this would be determined
# based on the defaults for whatever `static_site_generator` is configured.
{
source:
'source'
,
target:
''
}
end
end
end
end
end
end
end
lib/gitlab/static_site_editor/config/file_config/entry/mounts.rb
0 → 100644
View file @
2658f557
# frozen_string_literal: true
module
Gitlab
module
StaticSiteEditor
module
Config
class
FileConfig
module
Entry
##
# Entry that represents the mappings of mounted source directories to target paths
#
class
Mounts
<
::
Gitlab
::
Config
::
Entry
::
Node
include
::
Gitlab
::
Config
::
Entry
::
Configurable
include
::
Gitlab
::
Config
::
Entry
::
Validatable
entry
:mount
,
Entry
::
Mount
,
description:
'Configuration of a Static Site Editor mount.'
validations
do
validates
:config
,
type:
Array
,
presence:
true
end
def
skip_config_hash_validation?
true
end
def
self
.
default
[
Entry
::
Mount
.
default
]
end
end
end
end
end
end
end
lib/gitlab/static_site_editor/config/generated_config.rb
View file @
2658f557
...
...
@@ -21,7 +21,7 @@ module Gitlab
project:
project
.
path
,
namespace:
project
.
namespace
.
full_path
,
return_url:
sanitize_url
(
return_url
),
is_supported_content:
supported_content?
.
to_s
,
is_supported_content:
supported_content?
,
base_url:
Gitlab
::
Routing
.
url_helpers
.
project_show_sse_path
(
project
,
full_path
),
merge_requests_illustration_path:
merge_requests_illustration_path
}
...
...
spec/controllers/projects/static_site_editor_controller_spec.rb
View file @
2658f557
...
...
@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec
.
describe
Projects
::
StaticSiteEditorController
do
let_it_be
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
let_it_be
(
:user
)
{
create
(
:user
)
}
let
(
:data
)
{
instance_double
(
Hash
)
}
let
(
:data
)
{
{
key:
'value'
}
}
describe
'GET show'
do
render_views
...
...
@@ -52,42 +52,78 @@ RSpec.describe Projects::StaticSiteEditorController do
end
end
%w[developer maintainer]
.
each
do
|
role
|
context
"as
#{
role
}
"
do
before_all
do
project
.
add_role
(
user
,
role
)
context
"as developer"
do
before
do
project
.
add_role
(
user
,
'developer'
)
sign_in
(
user
)
get
:show
,
params:
default_params
end
it
'renders the edit page'
do
expect
(
response
).
to
render_template
(
:show
)
end
it
'assigns ref and path variables'
do
expect
(
assigns
(
:ref
)).
to
eq
(
'master'
)
expect
(
assigns
(
:path
)).
to
eq
(
'README.md'
)
end
context
'when combination of ref and path is incorrect'
do
let
(
:default_params
)
{
super
().
merge
(
id:
'unknown'
)
}
it
'responds with 404 page'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
end
context
'when invalid config file'
do
let
(
:service_response
)
{
ServiceResponse
.
error
(
message:
'invalid'
)
}
before
do
sign_in
(
user
)
get
:show
,
params:
default_params
it
'redirects to project page and flashes error message'
do
expect
(
response
).
to
redirect_to
(
project_path
(
project
)
)
expect
(
response
).
to
set_flash
[
:alert
].
to
(
'invalid'
)
end
end
it
'renders the edit page'
do
expect
(
response
).
to
render_template
(
:show
)
context
'with a service response payload containing multiple data types'
do
let
(
:data
)
do
{
a_string:
'string'
,
an_array:
[
{
foo:
'bar'
}
],
an_integer:
123
,
a_hash:
{
a_deeper_hash:
{
foo:
'bar'
}
},
a_boolean:
true
}
end
it
'assigns a required variables'
do
expect
(
assigns
(
:data
)).
to
eq
(
data
)
expect
(
assigns
(
:ref
)).
to
eq
(
'master'
)
expect
(
assigns
(
:path
)).
to
eq
(
'README.md
'
)
let
(
:assigns_data
)
{
assigns
(
:data
)
}
it
'leaves data values which are strings as strings'
do
expect
(
assigns
_data
[
:a_string
]).
to
eq
(
'string
'
)
end
context
'when combination of ref and path is incorrect'
do
let
(
:default_params
)
{
super
().
merge
(
id:
'unknown'
)
}
it
'leaves data values which are integers as integers'
do
expect
(
assigns_data
[
:an_integer
]).
to
eq
(
123
)
end
it
'responds with 404 page'
do
expect
(
response
).
to
have_gitlab_http_status
(
:not_found
)
end
it
'serializes data values which are booleans to JSON'
do
expect
(
assigns_data
[
:a_boolean
]).
to
eq
(
'true'
)
end
context
'when invalid config file'
do
let
(
:service_response
)
{
ServiceResponse
.
error
(
message:
'invalid'
)
}
it
'serializes data values which are arrays to JSON'
do
expect
(
assigns_data
[
:an_array
]).
to
eq
(
'[{"foo":"bar"}]'
)
end
it
'redirects to project page and flashes error message'
do
expect
(
response
).
to
redirect_to
(
project_path
(
project
))
expect
(
response
).
to
set_flash
[
:alert
].
to
(
'invalid'
)
end
it
'serializes data values which are hashes to JSON'
do
expect
(
assigns_data
[
:a_hash
]).
to
eq
(
'{"a_deeper_hash":{"foo":"bar"}}'
)
end
end
end
...
...
spec/features/static_site_editor_spec.rb
View file @
2658f557
...
...
@@ -21,7 +21,7 @@ RSpec.describe 'Static Site Editor' do
visit
sse_path
end
it
'renders S
tatic Site Editor
page with all generated config values and default config file values'
do
it
'renders S
SE
page with all generated config values and default config file values'
do
node
=
page
.
find
(
'#static-site-editor'
)
# assert generated config values are present
...
...
@@ -29,23 +29,30 @@ RSpec.describe 'Static Site Editor' do
expect
(
node
[
'data-branch'
]).
to
eq
(
'master'
)
expect
(
node
[
'data-commit-id'
]).
to
match
(
/\A[0-9a-f]{40}\z/
)
expect
(
node
[
'data-is-supported-content'
]).
to
eq
(
'true'
)
expect
(
node
[
'data-merge-requests-illustration-path'
]).
to
match
(
%r{/assets/illustrations/merge_requests-.*
\.
svg}
)
expect
(
node
[
'data-merge-requests-illustration-path'
])
.
to
match
(
%r{/assets/illustrations/merge_requests-.*
\.
svg}
)
expect
(
node
[
'data-namespace'
]).
to
eq
(
project
.
namespace
.
full_path
)
expect
(
node
[
'data-project'
]).
to
eq
(
project
.
path
)
expect
(
node
[
'data-project-id'
]).
to
eq
(
project
.
id
.
to_s
)
# assert default config file values are present
expect
(
node
[
'data-image-upload-path'
]).
to
eq
(
'source/images'
)
expect
(
node
[
'data-mounts'
]).
to
eq
(
'[{"source":"source","target":""}]'
)
expect
(
node
[
'data-static-site-generator'
]).
to
eq
(
'middleman'
)
end
end
context
"when a config file is present"
do
let
(
:config_file_yml
)
do
<<
-
EOS
<<
~
YAML
image_upload_path: custom-image-upload-path
mounts:
- source: source1
target: ""
- source: source2
target: target2
static_site_generator: middleman
EOS
YAML
end
before
do
...
...
@@ -60,7 +67,9 @@ RSpec.describe 'Static Site Editor' do
node
=
page
.
find
(
'#static-site-editor'
)
# assert user-specified config file values are present
expected_mounts
=
'[{"source":"source1","target":""},{"source":"source2","target":"target2"}]'
expect
(
node
[
'data-image-upload-path'
]).
to
eq
(
'custom-image-upload-path'
)
expect
(
node
[
'data-mounts'
]).
to
eq
(
expected_mounts
)
expect
(
node
[
'data-static-site-generator'
]).
to
eq
(
'middleman'
)
end
end
...
...
spec/lib/gitlab/static_site_editor/config/file_config/entry/global_spec.rb
View file @
2658f557
...
...
@@ -4,9 +4,19 @@ require 'spec_helper'
RSpec
.
describe
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
Global
do
let
(
:global
)
{
described_class
.
new
(
hash
)
}
let
(
:default_static_site_generator_value
)
{
'middleman'
}
let
(
:default_image_upload_path_value
)
{
'source/images'
}
let
(
:default_mounts_value
)
do
[
{
source:
'source'
,
target:
''
}
]
end
let
(
:default_static_site_generator_value
)
{
'middleman'
}
shared_examples_for
'valid default configuration'
do
describe
'#compose!'
do
before
do
...
...
@@ -18,7 +28,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
end
it
'creates node object for each entry'
do
expect
(
global
.
descendants
.
count
).
to
eq
2
expect
(
global
.
descendants
.
count
).
to
eq
3
end
it
'creates node object using valid class'
do
...
...
@@ -67,6 +77,12 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
end
end
describe
'#mounts_value'
do
it
'returns correct values'
do
expect
(
global
.
mounts_value
).
to
eq
(
default_mounts_value
)
end
end
describe
'#static_site_generator_value'
do
it
'returns correct values'
do
expect
(
global
.
static_site_generator_value
).
to
eq
(
default_static_site_generator_value
)
...
...
@@ -84,6 +100,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
it
'contains the expected node names'
do
expected_node_names
=
%i[
image_upload_path
mounts
static_site_generator
]
expect
(
described_class
.
nodes
.
keys
).
to
match_array
(
expected_node_names
)
...
...
@@ -96,6 +113,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
let
(
:expected_node_object_classes
)
do
[
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
ImageUploadPath
,
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
Mounts
,
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
StaticSiteGenerator
]
end
...
...
@@ -103,6 +121,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
let
(
:hash
)
do
{
image_upload_path:
default_image_upload_path_value
,
mounts:
default_mounts_value
,
static_site_generator:
default_static_site_generator_value
}
end
...
...
@@ -114,6 +133,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::FileConfig::Entry::Global do
context
'when value is an empty hash'
do
let
(
:expected_node_object_classes
)
do
[
Gitlab
::
Config
::
Entry
::
Unspecified
,
Gitlab
::
Config
::
Entry
::
Unspecified
,
Gitlab
::
Config
::
Entry
::
Unspecified
]
...
...
spec/lib/gitlab/static_site_editor/config/file_config/entry/mount_spec.rb
0 → 100644
View file @
2658f557
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
Mount
do
subject
(
:entry
)
{
described_class
.
new
(
config
)
}
describe
'validations'
do
context
'with a valid config'
do
context
'and target is a non-empty string'
do
let
(
:config
)
do
{
source:
'source'
,
target:
'sub-site'
}
end
it
{
is_expected
.
to
be_valid
}
describe
'#value'
do
it
'returns mount configuration'
do
expect
(
entry
.
value
).
to
eq
config
end
end
end
context
'and target is an empty string'
do
let
(
:config
)
do
{
source:
'source'
,
target:
''
}
end
it
{
is_expected
.
to
be_valid
}
describe
'#value'
do
it
'returns mount configuration'
do
expect
(
entry
.
value
).
to
eq
config
end
end
end
end
context
'with an invalid config'
do
context
'when source is not a string'
do
let
(
:config
)
{
{
source:
123
,
target:
'target'
}
}
it
{
is_expected
.
not_to
be_valid
}
it
'reports error'
do
expect
(
entry
.
errors
)
.
to
include
'mount source should be a string'
end
end
context
'when source is not present'
do
let
(
:config
)
{
{
target:
'target'
}
}
it
{
is_expected
.
not_to
be_valid
}
it
'reports error'
do
expect
(
entry
.
errors
)
.
to
include
"mount source can't be blank"
end
end
context
'when target is not a string'
do
let
(
:config
)
{
{
source:
'source'
,
target:
123
}
}
it
{
is_expected
.
not_to
be_valid
}
it
'reports error'
do
expect
(
entry
.
errors
)
.
to
include
'mount target should be a string'
end
end
context
'when there is an unknown key present'
do
let
(
:config
)
{
{
test:
100
}
}
it
{
is_expected
.
not_to
be_valid
}
it
'reports error'
do
expect
(
entry
.
errors
)
.
to
include
'mount config contains unknown keys: test'
end
end
end
end
describe
'.default'
do
it
'returns default mount'
do
expect
(
described_class
.
default
)
.
to
eq
({
source:
'source'
,
target:
''
})
end
end
end
spec/lib/gitlab/static_site_editor/config/file_config/entry/mounts_spec.rb
0 → 100644
View file @
2658f557
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
StaticSiteEditor
::
Config
::
FileConfig
::
Entry
::
Mounts
do
subject
(
:entry
)
{
described_class
.
new
(
config
)
}
describe
'validations'
do
context
'with a valid config'
do
let
(
:config
)
do
[
{
source:
'source'
,
target:
''
},
{
source:
'sub-site/source'
,
target:
'sub-site'
}
]
end
it
{
is_expected
.
to
be_valid
}
describe
'#value'
do
it
'returns mounts configuration'
do
expect
(
entry
.
value
).
to
eq
config
end
end
end
context
'with an invalid config'
do
let
(
:config
)
{
{
not_an_array:
true
}
}
it
{
is_expected
.
not_to
be_valid
}
it
'reports errors about wrong type'
do
expect
(
entry
.
errors
)
.
to
include
'mounts config should be a array'
end
end
end
describe
'.default'
do
it
'returns default mounts'
do
expect
(
described_class
.
default
)
.
to
eq
([{
source:
'source'
,
target:
''
}])
end
end
end
spec/lib/gitlab/static_site_editor/config/generated_config_spec.rb
View file @
2658f557
...
...
@@ -29,7 +29,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
project:
'project'
,
project_id:
project
.
id
,
return_url:
'http://example.com'
,
is_supported_content:
'true'
,
is_supported_content:
true
,
base_url:
'/namespace/project/-/sse/master%2FREADME.md'
,
merge_requests_illustration_path:
%r{illustrations/merge_requests}
})
...
...
@@ -65,7 +65,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
stub_feature_flags
(
sse_erb_support:
project
)
end
it
{
is_expected
.
to
include
(
is_supported_content:
'true'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
true
)
}
end
context
'when feature flag is disabled'
do
...
...
@@ -75,7 +75,7 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
stub_feature_flags
(
sse_erb_support:
false
)
end
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
end
...
...
@@ -88,31 +88,31 @@ RSpec.describe Gitlab::StaticSiteEditor::Config::GeneratedConfig do
context
'when branch is not master'
do
let
(
:ref
)
{
'my-branch'
}
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
context
'when file does not have a markdown extension'
do
let
(
:path
)
{
'README.txt'
}
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
context
'when file does not have an extension'
do
let
(
:path
)
{
'README'
}
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
context
'when file does not exist'
do
let
(
:path
)
{
'UNKNOWN.md'
}
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
context
'when repository is empty'
do
let
(
:repository
)
{
create
(
:project_empty_repo
).
repository
}
it
{
is_expected
.
to
include
(
is_supported_content:
'false'
)
}
it
{
is_expected
.
to
include
(
is_supported_content:
false
)
}
end
context
'when return_url is not a valid URL'
do
...
...
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