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
6a551a1e
Commit
6a551a1e
authored
Nov 14, 2018
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-11-14
parents
06ef748d
2059e9f5
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
143 additions
and
88 deletions
+143
-88
app/assets/javascripts/diffs/components/diff_file.vue
app/assets/javascripts/diffs/components/diff_file.vue
+15
-3
app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
...ascripts/filtered_search/filtered_search_visual_tokens.js
+5
-4
app/assets/javascripts/pipelines/components/graph/action_component.vue
...vascripts/pipelines/components/graph/action_component.vue
+8
-16
app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
...scripts/pipelines/components/graph/job_group_dropdown.vue
+3
-10
app/assets/javascripts/pipelines/components/graph/job_item.vue
...ssets/javascripts/pipelines/components/graph/job_item.vue
+7
-13
app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
...pts/pipelines/components/graph/stage_column_component.vue
+2
-8
app/assets/javascripts/pipelines/components/pipelines_table_row.vue
.../javascripts/pipelines/components/pipelines_table_row.vue
+2
-1
app/assets/javascripts/pipelines/components/stage.vue
app/assets/javascripts/pipelines/components/stage.vue
+3
-5
changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml
...ogs/unreleased/53636-fix-rendering-of-any-user-filter.yml
+5
-0
doc/user/project/repository/index.md
doc/user/project/repository/index.md
+24
-10
spec/javascripts/diffs/components/diff_file_spec.js
spec/javascripts/diffs/components/diff_file_spec.js
+22
-0
spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
...pts/filtered_search/filtered_search_visual_tokens_spec.js
+44
-13
spec/javascripts/pipelines/graph/job_item_spec.js
spec/javascripts/pipelines/graph/job_item_spec.js
+3
-5
No files found.
app/assets/javascripts/diffs/components/diff_file.vue
View file @
6a551a1e
...
...
@@ -32,6 +32,7 @@ export default {
computed
:
{
...
mapState
(
'
diffs
'
,
[
'
currentDiffFileId
'
]),
...
mapGetters
([
'
isNotesFetched
'
]),
...
mapGetters
(
'
diffs
'
,
[
'
getDiffFileDiscussions
'
]),
isCollapsed
()
{
return
this
.
file
.
collapsed
||
false
;
},
...
...
@@ -57,12 +58,23 @@ export default {
showLoadingIcon
()
{
return
this
.
isLoadingCollapsedDiff
||
(
!
this
.
file
.
renderIt
&&
!
this
.
isCollapsed
);
},
hasDiffLines
()
{
const
{
highlightedDiffLines
,
parallelDiffLines
}
=
this
.
file
;
return
highlightedDiffLines
&&
parallelDiffLines
&&
parallelDiffLines
.
length
>
0
;
},
},
watch
:
{
'
file.collapsed
'
:
function
fileCollapsedWatch
(
newVal
,
oldVal
)
{
if
(
!
newVal
&&
oldVal
&&
!
this
.
hasDiffLines
)
{
this
.
handleLoadCollapsedDiff
();
}
},
},
methods
:
{
...
mapActions
(
'
diffs
'
,
[
'
loadCollapsedDiff
'
,
'
assignDiscussionsToDiff
'
]),
handleToggle
()
{
const
{
highlightedDiffLines
,
parallelDiffLines
}
=
this
.
file
;
if
(
!
highlightedDiffLines
&&
parallelDiffLines
!==
undefined
&&
!
parallelDiffLines
.
length
)
{
if
(
!
this
.
hasDiffLines
)
{
this
.
handleLoadCollapsedDiff
();
}
else
{
this
.
file
.
collapsed
=
!
this
.
file
.
collapsed
;
...
...
@@ -81,7 +93,7 @@ export default {
.
then
(()
=>
{
requestIdleCallback
(
()
=>
{
this
.
assignDiscussionsToDiff
();
this
.
assignDiscussionsToDiff
(
this
.
getDiffFileDiscussions
(
this
.
file
)
);
},
{
timeout
:
1000
},
);
...
...
app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
View file @
6a551a1e
...
...
@@ -135,10 +135,6 @@ export default class FilteredSearchVisualTokens {
}
static
updateUserTokenAppearance
(
tokenValueContainer
,
tokenValueElement
,
tokenValue
)
{
if
(
tokenValue
===
'
none
'
)
{
return
Promise
.
resolve
();
}
const
username
=
tokenValue
.
replace
(
/^@/
,
''
);
return
(
UsersCache
.
retrieve
(
username
)
...
...
@@ -184,7 +180,12 @@ export default class FilteredSearchVisualTokens {
const
tokenValueElement
=
tokenValueContainer
.
querySelector
(
'
.value
'
);
tokenValueElement
.
innerText
=
tokenValue
;
if
(
tokenValue
===
'
none
'
||
tokenValue
===
'
any
'
)
{
return
;
}
const
tokenType
=
tokenName
.
toLowerCase
();
if
(
tokenType
===
'
label
'
)
{
FilteredSearchVisualTokens
.
updateLabelTokenColor
(
tokenValueContainer
,
tokenValue
);
}
else
if
(
tokenType
===
'
author
'
||
tokenType
===
'
assignee
'
)
{
...
...
app/assets/javascripts/pipelines/components/graph/action_component.vue
View file @
6a551a1e
<
script
>
import
$
from
'
jquery
'
;
import
{
GlTooltipDirective
,
GlButton
}
from
'
@gitlab-org/gitlab-ui
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
dasherize
}
from
'
~/lib/utils/text_utility
'
;
import
{
__
}
from
'
~/locale
'
;
import
createFlash
from
'
~/flash
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
/**
...
...
@@ -20,23 +19,20 @@ import Icon from '~/vue_shared/components/icon.vue';
export
default
{
components
:
{
Icon
,
GlButton
,
},
directives
:
{
tooltip
,
GlTooltip
:
GlTooltipDirective
,
},
props
:
{
tooltipText
:
{
type
:
String
,
required
:
true
,
},
link
:
{
type
:
String
,
required
:
true
,
},
actionIcon
:
{
type
:
String
,
required
:
true
,
...
...
@@ -47,7 +43,6 @@ export default {
isDisabled
:
false
,
};
},
computed
:
{
cssClass
()
{
const
actionIconDash
=
dasherize
(
this
.
actionIcon
);
...
...
@@ -62,8 +57,7 @@ export default {
*
*/
onClickAction
()
{
$
(
this
.
$el
).
tooltip
(
'
hide
'
);
this
.
$root
.
$emit
(
'
bv::hide::tooltip
'
,
`js-ci-action-
${
this
.
link
}
`
);
this
.
isDisabled
=
true
;
axios
...
...
@@ -82,18 +76,16 @@ export default {
};
</
script
>
<
template
>
<button
v-tooltip
<gl-button
:id=
"`js-ci-action-$
{link}`"
v-gl-tooltip="{ boundary: 'viewport' }"
:title="tooltipText"
:class="cssClass"
:disabled="isDisabled"
type=
"button"
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
data-container=
"body"
data-boundary=
"viewport"
@click="onClickAction"
>
<icon
:name=
"actionIcon"
/>
</button>
</
gl-
button>
</
template
>
app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue
View file @
6a551a1e
<
script
>
import
$
from
'
jquery
'
;
import
{
GlTooltipDirective
}
from
'
@gitlab-org/gitlab-ui
'
;
import
CiIcon
from
'
~/vue_shared/components/ci_icon.vue
'
;
import
JobItem
from
'
./job_item.vue
'
;
import
tooltip
from
'
../../../vue_shared/directives/tooltip
'
;
/**
* Renders the dropdown for the pipeline graph.
...
...
@@ -12,32 +12,27 @@ import tooltip from '../../../vue_shared/directives/tooltip';
*/
export
default
{
directives
:
{
tooltip
,
GlTooltip
:
GlTooltipDirective
,
},
components
:
{
JobItem
,
CiIcon
,
},
props
:
{
group
:
{
type
:
Object
,
required
:
true
,
},
},
computed
:
{
tooltipText
()
{
const
{
name
,
status
}
=
this
.
group
;
return
`
${
name
}
-
${
status
.
label
}
`
;
},
},
mounted
()
{
this
.
stopDropdownClickPropagation
();
},
methods
:
{
/**
* When the user right clicks or cmd/ctrl + click in the group name or the action icon
...
...
@@ -65,12 +60,10 @@ export default {
<
template
>
<div
class=
"ci-job-dropdown-container dropdown dropright"
>
<button
v-
tooltip
v-
gl-tooltip.hover=
"
{ boundary: 'viewport' }"
:title="tooltipText"
type="button"
data-toggle="dropdown"
data-container=
"body"
data-boundary=
"viewport"
data-display="static"
class="dropdown-menu-toggle build-content"
>
...
...
app/assets/javascripts/pipelines/components/graph/job_item.vue
View file @
6a551a1e
<
script
>
import
ActionComponent
from
'
./action_component.vue
'
;
import
JobNameComponent
from
'
./job_name_component.vue
'
;
import
tooltip
from
'
../../../vue_shared/directives/tooltip
'
;
import
{
GlTooltipDirective
,
GlLink
}
from
'
@gitlab-org/gitlab-ui
'
;
import
{
sprintf
}
from
'
~/locale
'
;
import
delayedJobMixin
from
'
~/jobs/mixins/delayed_job_mixin
'
;
...
...
@@ -34,9 +34,10 @@ export default {
components
:
{
ActionComponent
,
JobNameComponent
,
GlLink
,
},
directives
:
{
tooltip
,
GlTooltip
:
GlTooltipDirective
,
},
mixins
:
[
delayedJobMixin
],
props
:
{
...
...
@@ -55,7 +56,6 @@ export default {
default
:
Infinity
,
},
},
computed
:
{
status
()
{
return
this
.
job
&&
this
.
job
.
status
?
this
.
job
.
status
:
{};
...
...
@@ -88,7 +88,6 @@ export default {
tooltipBoundary
()
{
return
this
.
dropdownLength
<
5
?
'
viewport
'
:
null
;
},
/**
* Verifies if the provided job has an action path
*
...
...
@@ -98,7 +97,6 @@ export default {
return
this
.
job
.
status
&&
this
.
job
.
status
.
action
&&
this
.
job
.
status
.
action
.
path
;
},
},
methods
:
{
pipelineActionRequestComplete
()
{
this
.
$emit
(
'
pipelineActionRequestComplete
'
);
...
...
@@ -108,30 +106,26 @@ export default {
</
script
>
<
template
>
<div
class=
"ci-job-component"
>
<
a
<
gl-link
v-if=
"status.has_details"
v-
tooltip
v-
gl-tooltip=
"
{ boundary: tooltipBoundary }"
:href="status.details_path"
:title="tooltipText"
:class="cssClassJobName"
:data-boundary=
"tooltipBoundary"
data-container=
"body"
class="js-pipeline-graph-job-link"
>
<job-name-component
:name=
"job.name"
:status=
"job.status"
/>
</
a
>
</
gl-link
>
<div
v-else
v-tooltip
v-
gl-
tooltip
:title=
"tooltipText"
:class=
"cssClassJobName"
class=
"js-job-component-tooltip non-details-job-component"
data-container=
"body"
>
<job-name-component
...
...
app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
View file @
6a551a1e
...
...
@@ -13,18 +13,15 @@ export default {
type
:
String
,
required
:
true
,
},
groups
:
{
type
:
Array
,
required
:
true
,
},
isFirstColumn
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
stageConnectorClass
:
{
type
:
String
,
required
:
false
,
...
...
@@ -36,16 +33,13 @@ export default {
required
:
true
,
},
},
methods
:
{
groupId
(
group
)
{
return
`ci-badge-
${
_
.
escape
(
group
.
name
)}
`
;
},
buildConnnectorClass
(
index
)
{
return
index
===
0
&&
(
!
this
.
isFirstColumn
||
this
.
hasTriggeredBy
)
?
'
left-connector
'
:
''
;
},
pipelineActionRequestComplete
()
{
this
.
$emit
(
'
refreshPipelineGraph
'
);
},
...
...
@@ -55,7 +49,8 @@ export default {
<
template
>
<li
:class=
"stageConnectorClass"
class=
"stage-column"
>
class=
"stage-column"
>
<div
class=
"stage-name"
>
{{
title
}}
</div>
...
...
@@ -83,7 +78,6 @@ export default {
:group=
"group"
@
pipelineActionRequestComplete=
"pipelineActionRequestComplete"
/>
</li>
</ul>
</div>
...
...
app/assets/javascripts/pipelines/components/pipelines_table_row.vue
View file @
6a551a1e
...
...
@@ -308,7 +308,8 @@ export default {
<div
v-for=
"(stage, index) in pipeline.details.stages"
:key=
"index"
class=
"stage-container dropdown js-mini-pipeline-graph"
>
class=
"stage-container dropdown js-mini-pipeline-graph"
>
<pipeline-stage
:type=
"$options.pipelinesTable"
:stage=
"stage"
...
...
app/assets/javascripts/pipelines/components/stage.vue
View file @
6a551a1e
...
...
@@ -13,14 +13,13 @@
*/
import
$
from
'
jquery
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab-org/gitlab-ui
'
;
import
{
GlLoadingIcon
,
GlTooltipDirective
}
from
'
@gitlab-org/gitlab-ui
'
;
import
{
__
}
from
'
../../locale
'
;
import
Flash
from
'
../../flash
'
;
import
axios
from
'
../../lib/utils/axios_utils
'
;
import
eventHub
from
'
../event_hub
'
;
import
Icon
from
'
../../vue_shared/components/icon.vue
'
;
import
JobItem
from
'
./graph/job_item.vue
'
;
import
tooltip
from
'
../../vue_shared/directives/tooltip
'
;
import
{
PIPELINES_TABLE
}
from
'
../constants
'
;
export
default
{
...
...
@@ -31,7 +30,7 @@ export default {
},
directives
:
{
tooltip
,
GlTooltip
:
GlTooltipDirective
,
},
props
:
{
...
...
@@ -159,11 +158,10 @@ export default {
<button
id=
"stageDropdown"
ref=
"dropdown"
v-
tooltip
v-
gl-tooltip
.
hover
:class=
"triggerButtonClass"
:title=
"stage.title"
class=
"mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
data-placement=
"top"
data-toggle=
"dropdown"
data-display=
"static"
type=
"button"
...
...
changelogs/unreleased/53636-fix-rendering-of-any-user-filter.yml
0 → 100644
View file @
6a551a1e
---
title
:
Fix rendering of filter bar tokens for special values
merge_request
:
22865
author
:
Heinrich Lee Yu
type
:
fixed
doc/user/project/repository/index.md
View file @
6a551a1e
...
...
@@ -53,17 +53,35 @@ To get started with the command line, please read through the
Use GitLab's
[
file finder
](
../../../workflow/file_finder.md
)
to search for files in a repository.
### Supported markup languages and extensions
GitLab supports a number of markup languages (sometimes called
[
lightweight
markup languages
](
https://en.wikipedia.org/wiki/Lightweight_markup_language
)
)
that you can use for the content of your files in a repository. They are mostly
used for documentation purposes.
Just pick the right extension for your files and GitLab will render them
according to the markup language.
| Markup language | Extensions |
| --------------- | ---------- |
| Plain text |
`txt`
|
|
[
Markdown
](
../../markdown.md
)
|
`mdown`
,
`mkd`
,
`mkdn`
,
`md`
,
`markdown`
|
|
[
reStructuredText
](
http://docutils.sourceforge.net/rst.html
)
|
`rst`
|
|
[
Asciidoc
](
https://asciidoctor.org/docs/what-is-asciidoc/
)
|
`adoc`
,
`ad`
,
`asciidoc`
|
|
[
Textile
](
https://txstyle.org/
)
|
`textile`
|
|
[
rdoc
](
http://rdoc.sourceforge.net/doc/index.html
)
|
`rdoc`
|
|
[
Orgmode
](
https://orgmode.org/
)
|
`org`
|
|
[
creole
](
http://www.wikicreole.org/
)
|
`creole`
|
|
[
Mediawiki
](
https://www.mediawiki.org/wiki/MediaWiki
)
|
`wiki`
,
`mediawiki`
|
### Repository README and index files
When a
`README`
or
`index`
file is present in a repository, its contents will be
automatically pre-rendered by GitLab without opening it.
They can either be plain text or have an extension of a supported markup language:
-
Asciidoc:
`README.adoc`
or
`index.adoc`
-
Markdown:
`README.md`
or
`index.md`
-
reStructuredText:
`README.rst`
or
`index.rst`
-
Text:
`README.txt`
or
`index.txt`
They can either be plain text or have an extension of a
[
supported markup language
](
#supported-markup-languages-and-extensions
)
:
Some things to note about precedence:
...
...
@@ -75,10 +93,6 @@ Some things to note about precedence:
precedence over
`README.md`
, and
`README.rst`
will take precedence over
`README`
.
NOTE:
**Note:**
`index`
files without an extension will not automatically pre-render. You'll
have to explicitly open them to see their contents.
### Jupyter Notebook files
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508) in GitLab 9.1
...
...
spec/javascripts/diffs/components/diff_file_spec.js
View file @
6a551a1e
...
...
@@ -107,4 +107,26 @@ describe('DiffFile', () => {
});
});
});
describe
(
'
watch collapsed
'
,
()
=>
{
it
(
'
calls handleLoadCollapsedDiff if collapsed changed & file has no lines
'
,
done
=>
{
spyOn
(
vm
,
'
handleLoadCollapsedDiff
'
);
vm
.
file
.
highlightedDiffLines
=
undefined
;
vm
.
file
.
parallelDiffLines
=
[];
vm
.
file
.
collapsed
=
true
;
vm
.
$nextTick
()
.
then
(()
=>
{
vm
.
file
.
collapsed
=
false
;
return
vm
.
$nextTick
();
})
.
then
(()
=>
{
expect
(
vm
.
handleLoadCollapsedDiff
).
toHaveBeenCalled
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
});
spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
View file @
6a551a1e
...
...
@@ -754,6 +754,50 @@ describe('Filtered Search Visual Tokens', () => {
expect
(
updateLabelTokenColorSpy
.
calls
.
count
()).
toBe
(
0
);
expect
(
updateUserTokenAppearanceSpy
.
calls
.
count
()).
toBe
(
0
);
});
it
(
'
does not update user token appearance for `none` filter
'
,
()
=>
{
const
{
tokenNameElement
}
=
findElements
(
authorToken
);
const
tokenName
=
tokenNameElement
.
innerText
;
const
tokenValue
=
'
none
'
;
subject
.
renderVisualTokenValue
(
authorToken
,
tokenName
,
tokenValue
);
expect
(
updateUserTokenAppearanceSpy
.
calls
.
count
()).
toBe
(
0
);
});
it
(
'
does not update user token appearance for `any` filter
'
,
()
=>
{
const
{
tokenNameElement
}
=
findElements
(
authorToken
);
const
tokenName
=
tokenNameElement
.
innerText
;
const
tokenValue
=
'
any
'
;
subject
.
renderVisualTokenValue
(
authorToken
,
tokenName
,
tokenValue
);
expect
(
updateUserTokenAppearanceSpy
.
calls
.
count
()).
toBe
(
0
);
});
it
(
'
does not update label token color for `none` filter
'
,
()
=>
{
const
{
tokenNameElement
}
=
findElements
(
bugLabelToken
);
const
tokenName
=
tokenNameElement
.
innerText
;
const
tokenValue
=
'
none
'
;
subject
.
renderVisualTokenValue
(
bugLabelToken
,
tokenName
,
tokenValue
);
expect
(
updateLabelTokenColorSpy
.
calls
.
count
()).
toBe
(
0
);
});
it
(
'
does not update label token color for `any` filter
'
,
()
=>
{
const
{
tokenNameElement
}
=
findElements
(
bugLabelToken
);
const
tokenName
=
tokenNameElement
.
innerText
;
const
tokenValue
=
'
any
'
;
subject
.
renderVisualTokenValue
(
bugLabelToken
,
tokenName
,
tokenValue
);
expect
(
updateLabelTokenColorSpy
.
calls
.
count
()).
toBe
(
0
);
});
});
describe
(
'
updateUserTokenAppearance
'
,
()
=>
{
...
...
@@ -763,19 +807,6 @@ describe('Filtered Search Visual Tokens', () => {
spyOn
(
UsersCache
,
'
retrieve
'
).
and
.
callFake
(
username
=>
usersCacheSpy
(
username
));
});
it
(
'
ignores special value "none"
'
,
done
=>
{
usersCacheSpy
=
username
=>
{
expect
(
username
).
toBe
(
'
none
'
);
done
.
fail
(
'
Should not resolve "none"!
'
);
};
const
{
tokenValueContainer
,
tokenValueElement
}
=
findElements
(
authorToken
);
subject
.
updateUserTokenAppearance
(
tokenValueContainer
,
tokenValueElement
,
'
none
'
)
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'
ignores error if UsersCache throws
'
,
done
=>
{
spyOn
(
window
,
'
Flash
'
);
const
dummyError
=
new
Error
(
'
Earth rotated backwards
'
);
...
...
spec/javascripts/pipelines/graph/job_item_spec.js
View file @
6a551a1e
...
...
@@ -140,14 +140,12 @@ describe('pipeline graph job item', () => {
});
describe
(
'
tooltip placement
'
,
()
=>
{
const
tooltipBoundary
=
'
a[data-boundary="viewport"]
'
;
it
(
'
does not set tooltip boundary by default
'
,
()
=>
{
component
=
mountComponent
(
JobComponent
,
{
job
:
mockJob
,
});
expect
(
component
.
$el
.
querySelector
(
tooltipBoundary
)
).
toBeNull
();
expect
(
component
.
tooltipBoundary
).
toBeNull
();
});
it
(
'
sets tooltip boundary to viewport for small dropdowns
'
,
()
=>
{
...
...
@@ -156,7 +154,7 @@ describe('pipeline graph job item', () => {
dropdownLength
:
1
,
});
expect
(
component
.
$el
.
querySelector
(
tooltipBoundary
)).
not
.
toBeNull
(
);
expect
(
component
.
tooltipBoundary
).
toEqual
(
'
viewport
'
);
});
it
(
'
does not set tooltip boundary for large lists
'
,
()
=>
{
...
...
@@ -165,7 +163,7 @@ describe('pipeline graph job item', () => {
dropdownLength
:
7
,
});
expect
(
component
.
$el
.
querySelector
(
tooltipBoundary
)
).
toBeNull
();
expect
(
component
.
tooltipBoundary
).
toBeNull
();
});
});
...
...
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