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
41884e50
Commit
41884e50
authored
Jan 17, 2019
by
Filipa Lacerda
Committed by
Phil Hughes
Jan 17, 2019
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Moves index page of feature flags completely to Vue
parent
a0463bae
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
144 additions
and
187 deletions
+144
-187
ee/app/assets/javascripts/feature_flags/components/delete_feature_flag.vue
...ascripts/feature_flags/components/delete_feature_flag.vue
+2
-2
ee/app/assets/javascripts/feature_flags/components/feature_flags.vue
...ts/javascripts/feature_flags/components/feature_flags.vue
+62
-25
ee/app/assets/javascripts/feature_flags/index.js
ee/app/assets/javascripts/feature_flags/index.js
+2
-0
ee/app/views/projects/feature_flags/_configure_feature_flags_button.html.haml
...s/feature_flags/_configure_feature_flags_button.html.haml
+0
-3
ee/app/views/projects/feature_flags/_delete_feature_flag_modal.html.haml
...ojects/feature_flags/_delete_feature_flag_modal.html.haml
+0
-26
ee/app/views/projects/feature_flags/_empty_state.html.haml
ee/app/views/projects/feature_flags/_empty_state.html.haml
+0
-14
ee/app/views/projects/feature_flags/_new_feature_flag_button.html.haml
...projects/feature_flags/_new_feature_flag_button.html.haml
+0
-3
ee/app/views/projects/feature_flags/_table.html.haml
ee/app/views/projects/feature_flags/_table.html.haml
+0
-41
ee/app/views/projects/feature_flags/index.html.haml
ee/app/views/projects/feature_flags/index.html.haml
+6
-25
ee/spec/controllers/projects/feature_flags_controller_spec.rb
...pec/controllers/projects/feature_flags_controller_spec.rb
+3
-7
ee/spec/features/projects/feature_flags_spec.rb
ee/spec/features/projects/feature_flags_spec.rb
+4
-36
ee/spec/javascripts/projects/feature_flags/feature_flags_spec.js
.../javascripts/projects/feature_flags/feature_flags_spec.js
+63
-0
locale/gitlab.pot
locale/gitlab.pot
+2
-5
No files found.
ee/app/assets/javascripts/feature_flags/components/delete_feature_flag.vue
View file @
41884e50
...
...
@@ -11,7 +11,7 @@ export default {
Icon
,
},
directives
:
{
GlModal
:
GlModal
Directive
,
GlModalDirective
,
GlTooltip
:
GlTooltipDirective
,
},
props
:
{
...
...
@@ -64,7 +64,7 @@ export default {
<div
class=
"d-inline-block"
>
<gl-button
v-gl-tooltip.hover.bottom=
"__('Delete')"
v-gl-modal=
"modalId"
v-gl-modal
-directive
=
"modalId"
class=
"js-feature-flag-delete-button"
variant=
"danger"
>
...
...
ee/app/assets/javascripts/feature_flags/components/feature_flags.vue
View file @
41884e50
<
script
>
import
{
GlEmptyState
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
_
from
'
underscore
'
;
import
{
GlEmptyState
,
GlLoadingIcon
,
GlButton
}
from
'
@gitlab/ui
'
;
import
FeatureFlagsTable
from
'
./feature_flags_table.vue
'
;
import
store
from
'
../store
'
;
import
{
__
}
from
'
~/locale
'
;
...
...
@@ -20,6 +21,7 @@ export default {
TablePagination
,
GlEmptyState
,
GlLoadingIcon
,
GlButton
,
},
props
:
{
endpoint
:
{
...
...
@@ -38,6 +40,15 @@ export default {
type
:
String
,
required
:
true
,
},
canUserConfigure
:
{
type
:
Boolean
,
required
:
true
,
},
newFeatureFlagPath
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
data
()
{
return
{
...
...
@@ -97,6 +108,9 @@ export default {
},
];
},
hasNewPath
()
{
return
!
_
.
isEmpty
(
this
.
newFeatureFlagPath
);
},
},
created
()
{
this
.
setFeatureFlagsEndpoint
(
this
.
endpoint
);
...
...
@@ -136,6 +150,29 @@ export default {
</
script
>
<
template
>
<div>
<h3
class=
"page-title with-button"
>
{{
s__
(
'
FeatureFlags|Feature Flags
'
)
}}
<div
class=
"pull-right"
>
<button
v-if=
"canUserConfigure"
type=
"button"
class=
"js-ff-configure append-right-8 btn-inverted btn btn-primary"
data-toggle=
"modal"
data-target=
"#configure-feature-flags-modal"
>
{{
s__
(
'
FeatureFlags|Configure
'
)
}}
</button>
<gl-button
v-if=
"hasNewPath"
:href=
"newFeatureFlagPath"
variant=
"success"
class=
"js-ff-new"
>
{{
s__
(
'
FeatureFlags|New Feature Flag
'
)
}}
</gl-button
>
</div>
</h3>
<div
v-if=
"shouldRenderTabs"
class=
"top-area scrolling-tabs-container inner-page-scroll-tabs"
>
<navigation-tabs
:tabs=
"tabs"
scope=
"featureflags"
@
onChangeTab=
"onChangeTab"
/>
</div>
...
...
@@ -147,32 +184,32 @@ export default {
class=
"js-loading-state prepend-top-20"
/>
<template
v-else-if=
"shouldRenderErrorState"
>
<gl-empty-state
:title=
"s__(`FeatureFlags|There was an error fetching the feature flags.`)"
:description=
"s__(`FeatureFlags|Try again in a few moments or contact your support team.`)"
:svg-path=
"errorStateSvgPath"
/>
</
template
>
<gl-empty-state
v-else-if=
"shouldRenderErrorState"
:title=
"s__(`FeatureFlags|There was an error fetching the feature flags.`)"
:description=
"s__(`FeatureFlags|Try again in a few moments or contact your support team.`)"
:svg-path=
"errorStateSvgPath"
/>
<
template
v-else-if=
"shouldShowEmptyState"
>
<gl-empty-state
class=
"js-feature-flags-empty-state"
:title=
"s__(`FeatureFlags|Get started with feature flags`)"
:description=
"
s__(
`FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.`,
)
"
:svg-path=
"errorStateSvgPath"
:primary-button-link=
"featureFlagsHelpPagePath"
:primary-button-text=
"s__(`FeatureFlags|More Information`)"
/>
</
template
>
<gl-empty-state
v-else-if=
"shouldShowEmptyState"
class=
"js-feature-flags-empty-state"
:title=
"s__(`FeatureFlags|Get started with feature flags`)"
:description=
"
s__(
`FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.`,
)
"
:svg-path=
"errorStateSvgPath"
:primary-button-link=
"featureFlagsHelpPagePath"
:primary-button-text=
"s__(`FeatureFlags|More Information`)"
/>
<
template
v-else-if=
"shouldRenderTable"
>
<feature-flags-table
:csrf-token=
"csrfToken"
:feature-flags=
"featureFlags"
/>
</
template
>
<feature-flags-table
v-else-if=
"shouldRenderTable"
:csrf-token=
"csrfToken"
:feature-flags=
"featureFlags"
/>
<table-pagination
v-if=
"shouldRenderPagination"
:change=
"onChangePage"
:page-info=
"pageInfo"
/>
</div>
...
...
ee/app/assets/javascripts/feature_flags/index.js
View file @
41884e50
...
...
@@ -20,6 +20,8 @@ export default () =>
errorStateSvgPath
:
this
.
dataset
.
errorStateSvgPath
,
featureFlagsHelpPagePath
:
this
.
dataset
.
featureFlagsHelpPagePath
,
csrfToken
:
csrf
.
token
,
canUserConfigure
:
this
.
dataset
.
canUserAdminFeatureFlag
,
newFeatureFlagPath
:
this
.
dataset
.
newFeatureFlagPath
,
},
});
},
...
...
ee/app/views/projects/feature_flags/_configure_feature_flags_button.html.haml
deleted
100644 → 0
View file @
a0463bae
-
if
can?
(
current_user
,
:admin_feature_flag
,
@project
)
%button
.btn.btn-primary.btn-inverted.append-right-8
{
type:
'button'
,
data:
{
toggle:
'modal'
,
target:
'#configure-feature-flags-modal'
}
}
>
=
s_
(
'FeatureFlags|Configure'
)
ee/app/views/projects/feature_flags/_delete_feature_flag_modal.html.haml
deleted
100644 → 0
View file @
a0463bae
-
if
can?
(
current_user
,
:destroy_feature_flag
,
@project
)
.modal
{
id:
"delete-feature-flag-modal-#{feature_flag.id}"
,
tabindex:
-
1
,
role:
'dialog'
}
.modal-dialog
{
role:
'document'
}
.modal-content
.modal-header
%h4
.modal-title.d-flex.mw-100
-
truncated_feature_flag_name
=
capture
do
%span
.text-truncate.prepend-left-4.append-right-4
=
feature_flag
.
name
=
s_
(
'FeatureFlags|Delete %{feature_flag_name}?'
).
html_safe
%
{
feature_flag_name:
truncated_feature_flag_name
}
%button
.close
{
type:
'button'
,
data:
{
dismiss:
'modal'
},
aria:
{
label:
_
(
'Close'
)
}
}
%span
{
"aria-hidden"
:
true
}
×
.modal-body
%p
-
monospace_feature_flag_name
=
capture
do
%span
.text-monospace
=
feature_flag
.
name
=
s_
(
'FeatureFlags|Feature flag %{feature_flag_name} will be removed. Are you sure?'
).
html_safe
%
{
feature_flag_name:
monospace_feature_flag_name
}
.modal-footer
%button
{
type:
'button'
,
data:
{
dismiss:
'modal'
},
class:
'btn btn-default'
}
Cancel
=
button_to
'Delete'
,
project_feature_flag_path
(
@project
,
feature_flag
),
title:
'Delete'
,
method: :delete
,
class:
'btn btn-remove'
ee/app/views/projects/feature_flags/_empty_state.html.haml
deleted
100644 → 0
View file @
a0463bae
.row.empty-state
.col-12
.svg-content
=
image_tag
'illustrations/feature_flag.svg'
.col-12
.text-content
%h4
.text-center
=
s_
(
'FeatureFlags|Get started with feature flags'
)
%p
=
s_
(
'FeatureFlags|Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality.'
)
=
link_to
'More information'
,
help_page_path
(
"user/project/operations/feature_flags"
)
.text-center
=
render
'new_feature_flag_button'
=
render
'configure_feature_flags_button'
ee/app/views/projects/feature_flags/_new_feature_flag_button.html.haml
deleted
100644 → 0
View file @
a0463bae
-
if
can?
(
current_user
,
:create_feature_flag
,
@project
)
=
link_to
new_project_feature_flag_path
(
@project
),
class:
'btn btn-success'
do
=
s_
(
'FeatureFlags|New Feature Flag'
)
ee/app/views/projects/feature_flags/_table.html.haml
deleted
100644 → 0
View file @
a0463bae
.table-holder.border-top
.gl-responsive-table-row.table-row-header
{
role:
'row'
}
.table-section.section-10
{
role:
'columnheader'
}=
s_
(
'FeatureFlags|Status'
)
.table-section.section-50
{
role:
'columnheader'
}=
s_
(
'FeatureFlags|Feature flag'
)
-
@feature_flags
.
each
do
|
feature_flag
|
=
render
'delete_feature_flag_modal'
,
{
feature_flag:
feature_flag
}
.gl-responsive-table-row
{
role:
'row'
}
.table-section.section-10
{
role:
'gridcell'
}
.table-mobile-header
{
role:
"rowheader"
}=
s_
(
'FeatureFlags|Status'
)
.table-mobile-content
-
if
feature_flag
.
active?
%span
.badge.badge-success
=
s_
(
'FeatureFlags|Active'
)
-
else
%span
.badge.badge-danger
=
s_
(
'FeatureFlags|Inactive'
)
.table-section.section-50
{
role:
'gridcell'
}
.table-mobile-header
{
role:
"rowheader"
}=
s_
(
'FeatureFlags|Feature Flag'
)
.table-mobile-content.d-flex.flex-column
.text-monospace.text-truncate
=
feature_flag
.
name
.text-secondary.text-truncate
=
feature_flag
.
description
.table-section.section-40.table-button-footer
{
role:
'gridcell'
}
.table-action-buttons.btn-group
-
if
can?
(
current_user
,
:update_feature_flag
,
@project
)
=
link_to
edit_project_feature_flag_path
(
@project
,
feature_flag
),
class:
'btn btn-default has-tooltip'
,
type:
'button'
,
title:
_
(
'Edit'
)
do
=
sprite_icon
(
'pencil'
,
size:
16
)
-
if
can?
(
current_user
,
:destroy_feature_flag
,
@project
)
%button
.btn.btn-danger.has-tooltip
{
type:
'button'
,
data:
{
toggle:
'modal'
,
target:
"#delete-feature-flag-modal-#{feature_flag.id}"
},
title:
_
(
'Delete'
)
}
=
sprite_icon
(
'remove'
,
size:
16
)
=
paginate
@feature_flags
,
theme:
"gitlab"
ee/app/views/projects/feature_flags/index.html.haml
View file @
41884e50
-
page_title
_
(
'
Feature Flags'
)
-
page_title
s_
(
'FeatureFlags|
Feature Flags'
)
=
render
'configure_feature_flags_modal'
-
if
Feature
.
enabled?
(
:operations_feature_flag_index_tab
,
default_enabled:
true
)
%h3
.page-title.with-button
=
_
(
'Feature Flags'
)
.pull-right
=
render
'configure_feature_flags_button'
=
render
'new_feature_flag_button'
%div
#feature-flags-vue
{
data:
{
endpoint:
project_feature_flags_path
(
@project
,
format: :json
),
"error-state-svg-path"
=>
image_path
(
'illustrations/feature_flag.svg'
),
"feature-flags-help-page-path"
=>
help_page_path
(
"user/project/operations/feature_flags"
)
}
}
-
else
-
if
@feature_flags
.
empty?
=
render
'empty_state'
-
else
%h3
.page-title.with-button
=
_
(
'Feature Flags'
)
.pull-right
=
render
'configure_feature_flags_button'
=
render
'new_feature_flag_button'
=
render
'table'
#feature-flags-vue
{
data:
{
endpoint:
project_feature_flags_path
(
@project
,
format: :json
),
"error-state-svg-path"
=>
image_path
(
'illustrations/feature_flag.svg'
),
"feature-flags-help-page-path"
=>
help_page_path
(
"user/project/operations/feature_flags"
),
"can-user-admin-feature-flag"
=>
can?
(
current_user
,
:admin_feature_flag
,
@project
),
"new-feature-flag-path"
=>
can?
(
current_user
,
:create_feature_flag
,
@project
)
?
new_project_feature_flag_path
(
@project
):
nil
}
}
ee/spec/controllers/projects/feature_flags_controller_spec.rb
View file @
41884e50
...
...
@@ -27,10 +27,8 @@ describe Projects::FeatureFlagsController do
subject
end
it
'
shows an empty state with buttons
'
do
it
'
renders page
'
do
expect
(
response
).
to
be_ok
expect
(
response
).
to
render_template
(
'_configure_feature_flags_button'
)
expect
(
response
).
to
render_template
(
'_new_feature_flag_button'
)
end
end
...
...
@@ -41,10 +39,8 @@ describe Projects::FeatureFlagsController do
subject
end
it
'shows an list of feature flags with buttons'
do
expect
(
response
).
to
be_ok
expect
(
response
).
to
render_template
(
'_configure_feature_flags_button'
)
expect
(
response
).
to
render_template
(
'_new_feature_flag_button'
)
it
'renders page'
do
expect
(
response
).
to
have_gitlab_http_status
(
:ok
)
end
end
...
...
ee/spec/features/projects/feature_flags_spec.rb
View file @
41884e50
...
...
@@ -86,14 +86,6 @@ describe 'Feature Flags', :js do
end
it_behaves_like
'correct edit behavior'
context
'when operations_feature_flag_index_tab feature flag is disabled'
do
before
do
stub_feature_flags
(
operations_feature_flag_index_tab:
false
)
end
it_behaves_like
'correct edit behavior'
end
end
context
'when deleting a feature flag'
do
...
...
@@ -136,14 +128,6 @@ describe 'Feature Flags', :js do
end
it_behaves_like
'correct delete behavior'
context
'when operations_feature_flag_index_tab feature flag is disabled'
do
before
do
stub_feature_flags
(
operations_feature_flag_index_tab:
false
)
end
it_behaves_like
'correct delete behavior'
end
end
context
'when user sees empty index page'
do
...
...
@@ -160,14 +144,6 @@ describe 'Feature Flags', :js do
end
it_behaves_like
'correct empty index behavior'
context
'when operations_feature_flag_index_tab feature flag is disabled'
do
before
do
stub_feature_flags
(
operations_feature_flag_index_tab:
false
)
end
it_behaves_like
'correct empty index behavior'
end
end
context
'when user sees index page'
do
...
...
@@ -182,6 +158,8 @@ describe 'Feature Flags', :js do
it
'shows all feature flags'
do
expect
(
page
).
to
have_content
(
feature_flag_enabled
.
name
)
expect
(
page
).
to
have_content
(
feature_flag_disabled
.
name
)
expect
(
page
).
to
have_link
(
'New Feature Flag'
)
expect
(
page
).
to
have_button
(
'Configure'
)
end
end
...
...
@@ -222,12 +200,7 @@ describe 'Feature Flags', :js do
end
def
delete_feature_flag
(
name
,
confirm
=
true
)
delete_button
=
if
Feature
.
enabled?
(
:operations_feature_flag_index_tab
)
find
(
'.gl-responsive-table-row'
,
text:
name
).
find
(
'.js-feature-flag-delete-button'
)
else
find
(
'.gl-responsive-table-row'
,
text:
name
).
find
(
'.btn-danger[title="Delete"]'
)
end
delete_button
=
find
(
'.gl-responsive-table-row'
,
text:
name
).
find
(
'.js-feature-flag-delete-button'
)
delete_button
.
click
...
...
@@ -243,12 +216,7 @@ describe 'Feature Flags', :js do
def
edit_feature_flag
(
old_name
,
new_name
,
new_description
,
new_status
)
visit
(
project_feature_flags_path
(
project
))
edit_button
=
if
Feature
.
enabled?
(
:operations_feature_flag_index_tab
)
find
(
'.gl-responsive-table-row'
,
text:
old_name
).
find
(
'.js-feature-flag-edit-button'
)
else
find
(
'.gl-responsive-table-row'
,
text:
old_name
).
find
(
'.btn-default[title="Edit"]'
)
end
edit_button
=
find
(
'.gl-responsive-table-row'
,
text:
old_name
).
find
(
'.js-feature-flag-edit-button'
)
edit_button
.
click
...
...
ee/spec/javascripts/projects/feature_flags/feature_flags_spec.js
View file @
41884e50
...
...
@@ -12,6 +12,8 @@ describe('Feature Flags', () => {
csrfToken
:
'
testToken
'
,
errorStateSvgPath
:
'
/assets/illustrations/feature_flag.svg
'
,
featureFlagsHelpPagePath
:
'
/help/feature-flags
'
,
canUserConfigure
:
true
,
newFeatureFlagPath
:
'
feature-flags/new
'
,
};
let
store
;
...
...
@@ -30,6 +32,43 @@ describe('Feature Flags', () => {
mock
.
restore
();
});
describe
(
'
without permissions
'
,
()
=>
{
const
props
=
{
endpoint
:
'
feature_flags.json
'
,
csrfToken
:
'
testToken
'
,
errorStateSvgPath
:
'
/assets/illustrations/feature_flag.svg
'
,
featureFlagsHelpPagePath
:
'
/help/feature-flags
'
,
canUserConfigure
:
false
,
};
beforeEach
(
done
=>
{
mock
.
onGet
(
mockData
.
endpoint
).
reply
(
200
,
{
feature_flags
:
[],
count
:
{
all
:
0
,
enabled
:
0
,
disabled
:
0
,
},
});
component
=
mountComponentWithStore
(
FeatureFlagsComponent
,
{
store
,
props
,
});
setTimeout
(()
=>
{
done
();
},
0
);
});
it
(
'
does not render configure button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-configure
'
)).
toBeNull
();
});
it
(
'
does not render new feature flag button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-new
'
)).
toBeNull
();
});
});
describe
(
'
loading state
'
,
()
=>
{
it
(
'
renders a loading icon
'
,
done
=>
{
mock
.
onGet
(
mockData
.
endpoint
).
reply
(
200
,
{
...
...
@@ -84,6 +123,14 @@ describe('Feature Flags', () => {
it
(
'
should render the empty state
'
,
()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
.js-feature-flags-empty-state
'
)).
not
.
toBeNull
();
});
it
(
'
renders configure button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-configure
'
)).
not
.
toBeNull
();
});
it
(
'
renders new feature flag button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-new
'
)).
not
.
toBeNull
();
});
});
describe
(
'
with paginated feature flags
'
,
()
=>
{
...
...
@@ -131,6 +178,14 @@ describe('Feature Flags', () => {
);
});
it
(
'
renders configure button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-configure
'
)).
not
.
toBeNull
();
});
it
(
'
renders new feature flag button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-new
'
)).
not
.
toBeNull
();
});
describe
(
'
pagination
'
,
()
=>
{
it
(
'
should render pagination
'
,
()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
.gl-pagination li
'
).
length
).
toEqual
(
5
);
...
...
@@ -185,5 +240,13 @@ describe('Feature Flags', () => {
'
There was an error fetching the feature flags. Try again in a few moments or contact your support team.
'
,
);
});
it
(
'
renders configure button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-configure
'
)).
not
.
toBeNull
();
});
it
(
'
renders new feature flag button
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-ff-new
'
)).
not
.
toBeNull
();
});
});
});
locale/gitlab.pot
View file @
41884e50
...
...
@@ -3749,9 +3749,6 @@ msgstr ""
msgid "FeatureFlags|Create feature flag"
msgstr ""
msgid "FeatureFlags|Delete %{feature_flag_name}?"
msgstr ""
msgid "FeatureFlags|Delete %{name}?"
msgstr ""
...
...
@@ -3770,10 +3767,10 @@ msgstr ""
msgid "FeatureFlags|Feature Flag"
msgstr ""
msgid "FeatureFlags|Feature
flag
"
msgid "FeatureFlags|Feature
Flags
"
msgstr ""
msgid "FeatureFlags|Feature flag
%{feature_flag_name} will be removed. Are you sure?
"
msgid "FeatureFlags|Feature flag"
msgstr ""
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
...
...
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