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
8ca10a83
Commit
8ca10a83
authored
Sep 28, 2012
by
Calen Pennington
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Separate unit page from vertical implementation for editing
parent
82a28f06
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
192 additions
and
231 deletions
+192
-231
cms/djangoapps/contentstore/views.py
+38
-34
cms/static/coffee/src/models/module.coffee
+0
-6
cms/static/coffee/src/views/module_edit.coffee
+25
-31
cms/static/js/base.js
+9
-70
cms/static/sass/_base.scss
+63
-69
cms/templates/component.html
+12
-0
cms/templates/unit.html
+38
-5
cms/urls.py
+1
-1
common/static/coffee/src/xmodule.coffee
+3
-1
common/templates/xmodule_edit.html
+3
-14
No files found.
cms/djangoapps/contentstore/views.py
View file @
8ca10a83
...
...
@@ -43,6 +43,9 @@ from cache_toolbox.core import set_cached_content, get_cached_content, del_cache
log
=
logging
.
getLogger
(
__name__
)
COMPONENT_TYPES
=
[
'customtag'
,
'discussion'
,
'html'
,
'problem'
,
'video'
]
# ==== Public views ==================================================
@ensure_csrf_cookie
...
...
@@ -142,43 +145,54 @@ def edit_unit(request, location):
else
:
lms_link
=
None
<<<<<<<
HEAD
return
render_to_response
(
'unit.html'
,
{
'module'
:
item
,
'editable_preview'
:
get_module_previews
(
request
,
item
)[
0
],
})
=======
component_templates
=
defaultdict
(
list
)
>>>>>>>
Separate
unit
page
from
vertical
implementation
for
editing
templates
=
modulestore
()
.
get_items
(
Location
(
'i4x'
,
'edx'
,
'templates'
))
for
template
in
templates
:
if
template
.
location
.
category
in
COMPONENT_TYPES
:
component_templates
[
template
.
location
.
category
]
.
append
((
template
.
display_name
,
template
.
location
.
url
(),
))
components
=
[
component
.
location
.
url
()
for
component
in
item
.
get_children
()
]
@login_required
def
delete_unit
(
request
,
location
):
pass
return
render_to_response
(
'unit.html'
,
{
'unit_name'
:
item
.
display_name
,
'components'
:
components
,
'component_templates'
:
component_templates
,
})
@login_required
def
new_item
(
request
):
"""
Display a page where the user can create a new item from a template
Expects a GET request with the parameter 'parent_location', which is the element to add
the newly created item to as a child.
parent_location: A Location URL
"""
def
preview_component
(
request
,
location
):
# TODO (vshnayder): change name from id to location in coffee+html as well.
if
not
has_access
(
request
.
user
,
location
):
raise
Http404
# TODO (vshnayder): better error
parent_location
=
request
.
GET
[
'parent_location'
]
if
not
has_access
(
request
.
user
,
parent_location
):
raise
Http404
component
=
modulestore
()
.
get_item
(
location
)
parent
=
modulestore
()
.
get_item
(
parent_location
)
templates
=
modulestore
()
.
get_items
(
Location
(
'i4x'
,
'edx'
,
'templates'
))
return
render_to_response
(
'component.html'
,
{
'preview'
:
get_module_previews
(
request
,
component
)[
0
],
'editor'
:
wrap_xmodule
(
component
.
get_html
,
component
,
'xmodule_edit.html'
)(),
})
templates
.
sort
(
key
=
attrgetter
(
'location.category'
,
'display_name'
))
return
render_to_response
(
'new_item.html'
,
{
'parent_name'
:
parent
.
display_name
,
'parent_location'
:
parent
.
location
.
url
(),
'templates'
:
groupby
(
templates
,
attrgetter
(
'location.category'
)),
})
@login_required
def
delete_unit
(
request
,
location
):
pass
def
user_author_string
(
user
):
...
...
@@ -321,20 +335,10 @@ def load_preview_module(request, preview_id, descriptor, instance_state, shared_
error_msg
=
exc_info_to_str
(
sys
.
exc_info
())
)
.
xmodule_constructor
(
system
)(
None
,
None
)
module
.
get_html
=
wrap_xmodule
(
module
.
get_html
,
module
,
"xmodule_edit.html"
,
{
'location'
:
descriptor
.
location
.
url
(),
'editor_content'
:
descriptor
.
get_html
(),
'editor_type'
:
descriptor
.
js_module_name
,
'editor_class'
:
descriptor
.
__class__
.
__name__
,
# TODO (cpennington): Make descriptors know if they have data that can be editng
'editable_data'
:
descriptor
.
definition
.
get
(
'data'
),
'editable_class'
:
'editable'
if
descriptor
.
definition
.
get
(
'data'
)
else
''
,
}
"xmodule_display.html"
,
)
module
.
get_html
=
replace_static_urls
(
module
.
get_html
,
...
...
cms/static/coffee/src/models/module.coffee
View file @
8ca10a83
...
...
@@ -4,9 +4,3 @@ class CMS.Models.Module extends Backbone.Model
data
:
''
children
:
''
metadata
:
{}
initialize
:
(
attributes
)
->
@
module
=
attributes
.
module
@
unset
(
'module'
)
delete
attributes
.
module
super
(
attributes
)
cms/static/coffee/src/views/module_edit.coffee
View file @
8ca10a83
class
CMS
.
Views
.
ModuleEdit
extends
Backbone
.
View
tagName
:
'
div
'
className
:
'
xmodule_edi
t'
tagName
:
'
li
'
className
:
'
componen
t'
initialize
:
->
@
module
=
@
options
.
module
@
module
.
onUpdate
(
@
save
)
events
:
"click .component-editor .cancel-button"
:
'clickCancelButton'
"click .component-editor .save-button"
:
'clickSaveButton'
"click .component-actions .edit-button"
:
'clickEditButton'
@
setEvents
()
$component_editor
:
->
@
$el
.
find
(
'.component-editor'
)
setEvents
:
->
id
=
@
$el
.
data
(
'id'
)
initialize
:
->
@
module
=
@
options
.
module
@
render
()
@
events
=
{}
@
events
[
"click .component-editor[data-id=
#{
id
}
] .cancel-button"
]
=
'clickCancelButton'
@
events
[
"click .component-editor[data-id=
#{
id
}
] .save-button"
]
=
'clickSaveButton'
@
events
[
"click .component-actions[data-id=
#{
id
}
] .edit-button"
]
=
'clickEditButton'
$component_editor
:
=>
@
$el
.
find
(
'.component-editor'
)
@
delegateEvents
()
loadModules
:
->
@
module
=
XModule
.
loadModule
(
@
$el
.
find
(
'.xmodule_edit'
))
XModule
.
loadModule
(
@
$el
.
find
(
'.xmodule_display'
))
metadata
:
->
# cdodge: package up metadata which is separated into a number of input fields
...
...
@@ -32,30 +30,26 @@ class CMS.Views.ModuleEdit extends Backbone.View
# build up a object to pass back to the server on the subsequent POST
_metadata
[
$
(
el
).
data
(
"metadata-name"
)]
=
el
.
value
for
el
in
$
(
'[data-metadata-name]'
,
$metadata
)
_metadata
save
:
(
data
)
=>
@
model
.
unset
(
'preview'
)
@
model
.
set
(
data
)
@
model
.
save
().
done
(
(
resp
)
=>
alert
(
"Your changes have been saved."
)
$preview
=
$
(
resp
.
preview
)
@
$el
.
replaceWith
(
$preview
)
@
setElement
(
$preview
)
@
module
.
constructor
(
@
$el
)
XModule
.
loadModules
(
@
$el
)
return
_metadata
).
fail
(
->
alert
(
"There was an error saving your changes. Please try again."
)
render
:
->
@
$el
.
load
(
"/preview_component/
#{
@
model
.
id
}
"
,
=>
@
loadModules
()
@
delegateEvents
()
)
clickSaveButton
:
(
event
)
=>
event
.
preventDefault
()
data
=
@
module
.
save
()
data
.
metadata
=
@
metadata
()
@
model
.
save
(
data
).
done
(
=>
alert
(
"Your changes have been saved."
)
@
save
(
data
)
@
render
()
@
$el
.
removeClass
(
'editing'
)
).
fail
(
->
alert
(
"There was an error saving your changes. Please try again."
)
)
clickCancelButton
:
(
event
)
->
event
.
preventDefault
()
...
...
cms/static/js/base.js
View file @
8ca10a83
...
...
@@ -10,32 +10,23 @@ $(document).ready(function() {
$modal
=
$
(
'.history-modal'
);
$modalCover
=
$
(
'.modal-cover'
);
$newComponentItem
=
$
(
'.new-component-item'
);
$newComponentStep1
=
$
(
'.new-component-step-1'
);
$newComponentStep2
=
$
(
'.new-component-step-2'
);
$newComponentChooser
=
$
(
'.new-component'
);
$newComponentButton
=
$
(
'.new-component-button'
);
$
(
document
).
bind
(
'XModule.loaded.edit'
,
function
(
e
,
element
,
module
)
{
var
previewType
=
$
(
element
).
data
(
'preview-type'
);
var
moduleType
=
$
(
element
).
data
(
'type'
);
$
(
'li.component'
).
each
(
function
(
idx
,
element
)
{
new
CMS
.
Views
.
ModuleEdit
({
el
:
element
,
module
:
module
,
model
:
new
CMS
.
Models
.
Module
({
id
:
$
(
element
).
data
(
'id'
),
type
:
moduleType
==
'None'
?
null
:
moduleType
,
previewType
:
previewType
==
'None'
?
null
:
previewType
,
})
});
});
XModule
.
loadModules
()
$
(
'.expand-collapse-icon'
).
bind
(
'click'
,
toggleSubmodules
);
$
(
'.visibility-options'
).
bind
(
'change'
,
setVisibility
);
$newComponentButton
.
bind
(
'click'
,
showNewComponentForm
);
$newComponentStep1
.
find
(
'.new-component-type a'
).
bind
(
'click'
,
showNewComponentProperties
);
$newComponentStep2
.
find
(
'.save-button'
).
bind
(
'click'
,
saveNewComponent
);
$newComponentStep2
.
find
(
'.cancel-button'
).
bind
(
'click'
,
cancelNewComponent
);
$newComponentChooser
.
find
(
'.new-component-type a'
).
bind
(
'click'
,
showComponentTemplates
);
$
(
'.unit-history ol a'
).
bind
(
'click'
,
showHistoryModal
);
$modal
.
bind
(
'click'
,
hideHistoryModal
);
...
...
@@ -64,70 +55,18 @@ function closeComponentEditor(e) {
}
function
showNewComponentForm
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
$newComponentItem
.
addClass
(
'adding'
);
$
(
this
).
slideUp
(
150
);
$newComponent
Step1
.
slideDown
(
150
);
$newComponent
Chooser
.
slideDown
(
150
);
}
function
show
NewComponentProperti
es
(
e
)
{
function
show
ComponentTemplat
es
(
e
)
{
e
.
preventDefault
();
var
displayName
;
var
componentSource
;
var
selectionRange
;
var
$renderedComponent
;
switch
(
$
(
this
).
attr
(
'data-type'
))
{
case
'video'
:
displayName
=
'Video'
;
componentSource
=
'<video youtube="1.50:___,1.25:___,1.0:___,0.75:___"/>'
;
selectionRange
=
[
21
,
24
];
$renderedComponent
=
$
(
'<div class="rendered-component"><div class="video-unit"><img src="images/video-module.png"></div></div>'
);
break
;
case
'textbook'
:
displayName
=
'Textbook'
;
componentSource
=
'<customtag page="___"><impl>book</impl></customtag>'
;
selectionRange
=
[
17
,
20
];
$renderedComponent
=
$
(
'<div class="rendered-component"><p><span class="textbook-icon"></span>More information given in the text.</p></div>'
);
break
;
case
'slide'
:
displayName
=
'Slide'
;
componentSource
=
'<customtag page="___"><customtag lecnum="___"><impl>slides</impl></customtag>'
;
selectionRange
=
[
17
,
20
];
$renderedComponent
=
$
(
'<div class="rendered-component"><p><span class="slides-icon"></span>Lecture Slides Handout [Clean] [Annotated]</p></div>'
);
break
;
case
'discussion'
:
displayName
=
'Discussion'
;
componentSource
=
'<discussion for="___" id="___" discussion_category="___"/>'
;
selectionRange
=
[
17
,
20
];
$renderedComponent
=
$
(
'<div class="rendered-component"><div class="discussion-unit"><img src="images/discussion-module.png"></div></div>'
);
break
;
case
'problem'
:
displayName
=
'Problem'
;
componentSource
=
'<problem>___</problem>'
;
selectionRange
=
[
9
,
12
];
$renderedComponent
=
$
(
'<div class="rendered-component"></div>'
);
break
;
case
'freeform'
:
displayName
=
'Freeform HTML'
;
componentSource
=
''
;
selectionRange
=
[
0
,
0
];
$renderedComponent
=
$
(
'<div class="rendered-component"></div>'
);
break
;
}
$newComponentItem
.
prepend
(
$renderedComponent
);
$renderedComponent
.
slideDown
(
250
);
$newComponentStep2
.
find
(
'h5'
).
html
(
'Edit '
+
displayName
+
' Component'
);
$newComponentStep2
.
find
(
'textarea'
).
html
(
componentSource
);
setTimeout
(
function
()
{
$newComponentStep2
.
find
(
'textarea'
).
focus
().
get
(
0
).
setSelectionRange
(
selectionRange
[
0
],
selectionRange
[
1
]);
},
10
);
$newComponentStep1
.
slideUp
(
250
);
$newComponentStep2
.
slideDown
(
250
);
var
type
=
$
(
this
).
data
(
'type'
);
$newComponentChooser
.
slideUp
(
250
);
$
(
'.new-component-'
+
type
).
slideDown
(
250
);
}
function
cancelNewComponent
(
e
)
{
...
...
cms/static/sass/_base.scss
View file @
8ca10a83
...
...
@@ -268,7 +268,7 @@ input.courseware-unit-search-input {
header
{
height
:
67px
;
.item-actions
{
.item-actions
{
margin-top
:
11px
;
margin-right
:
12px
;
...
...
@@ -359,7 +359,7 @@ input.courseware-unit-search-input {
}
}
.
item
-actions
{
.
component
-actions
{
float
:
right
;
.edit-button
,
...
...
@@ -499,8 +499,8 @@ input.courseware-unit-search-input {
text-transform
:
uppercase
;
}
.
edit-pane
{
.
xmodule_edit.editable
{
.
components
{
.
component
{
position
:
relative
;
border
:
1px
solid
transparent
;
z-index
:
10
;
...
...
@@ -577,82 +577,76 @@ input.courseware-unit-search-input {
opacity
:
0
;
-webkit-transition
:
opacity
.15s
;
}
}
.new-component-item
{
padding
:
0
;
border
:
1px
solid
#8891a1
;
border-radius
:
3px
;
background
:
-webkit-linear-gradient
(
top
,
rgba
(
255
,
255
,
255
,
.3
)
,
rgba
(
255
,
255
,
255
,
0
))
#d1dae3
;
box-shadow
:
0
1px
0
rgba
(
255
,
255
,
255
,
.2
)
inset
;
-webkit-transition
:
background-color
.15s
,
border-color
.15s
;
&
.new-component-item
{
padding
:
0
;
border
:
1px
solid
#8891a1
;
border-radius
:
3px
;
background
:
-webkit-linear-gradient
(
top
,
rgba
(
255
,
255
,
255
,
.3
)
,
rgba
(
255
,
255
,
255
,
0
))
#d1dae3
;
box-shadow
:
0
1px
0
rgba
(
255
,
255
,
255
,
.2
)
inset
;
-webkit-transition
:
background-color
.15s
,
border-color
.15s
;
&
.adding
{
background-color
:
$blue
;
border-color
:
#437fbf
;
}
&
.adding
{
background-color
:
$blue
;
border-color
:
#437fbf
;
}
.new-component-button
{
display
:
block
;
padding
:
20px
;
text-align
:
center
;
color
:
#6d788b
;
}
.new-component-button
{
display
:
block
;
padding
:
20px
;
text-align
:
center
;
color
:
#6d788b
;
}
h5
{
margin-bottom
:
8px
;
color
:
#fff
;
font-weight
:
700
;
}
h5
{
margin-bottom
:
8px
;
color
:
#fff
;
font-weight
:
70
0
;
}
.rendered-component
{
display
:
none
;
background
:
#fff
;
border-radius
:
3px
3px
0
0
;
}
.rendered-component
{
display
:
none
;
background
:
#fff
;
border-radius
:
3px
3px
0
0
;
}
.new-component-type
{
@include
clearfix
;
a
{
position
:
relative
;
float
:
left
;
width
:
100px
;
height
:
100px
;
margin-right
:
10px
;
border-radius
:
8px
;
font-size
:
13px
;
line-height
:
14px
;
color
:
#fff
;
text-align
:
center
;
box-shadow
:
0
1px
1px
rgba
(
0
,
0
,
0
,
.4
)
,
0
1px
0
rgba
(
255
,
255
,
255
,
.4
)
inset
;
-webkit-transition
:
background-color
.15s
;
.new-component-type
{
@include
clearfix
;
a
{
position
:
relative
;
float
:
left
;
width
:
100px
;
height
:
100px
;
margin-right
:
10px
;
border-radius
:
8px
;
font-size
:
13px
;
line-height
:
14px
;
color
:
#fff
;
text-align
:
center
;
box-shadow
:
0
1px
1px
rgba
(
0
,
0
,
0
,
.4
)
,
0
1px
0
rgba
(
255
,
255
,
255
,
.4
)
inset
;
-webkit-transition
:
background-color
.15s
;
&
:hover
{
background-color
:
rgba
(
255
,
255
,
255
,
.2
);
}
.name
{
position
:
absolute
;
bottom
:
5px
;
left
:
0
;
width
:
100%
;
padding
:
10px
;
box-sizing
:
border-box
;
}
&
:hover
{
background-color
:
rgba
(
255
,
255
,
255
,
.2
);
}
}
.new-component-step-1
,
.new-component-step-2
{
display
:
none
;
padding
:
20px
;
.name
{
position
:
absolute
;
bottom
:
5px
;
left
:
0
;
width
:
100%
;
padding
:
10px
;
box-sizing
:
border-box
;
}
}
}
}
.video-unit
img
,
.discussion-unit
img
{
width
:
100%
;
.new-component
,
.new-component-templates
{
display
:
none
;
padding
:
20px
;
}
}
}
...
...
cms/templates/component.html
0 → 100644
View file @
8ca10a83
${preview}
<div
class=
"component-actions"
>
<a
href=
"#"
class=
"edit-button"
><span
class=
"edit-icon white"
></span>
Edit
</a>
<a
href=
"#"
class=
"delete-button"
><span
class=
"delete-icon white"
></span>
Delete
</a>
</div>
<a
href=
"#"
class=
"drag-handle"
></a>
<div
class=
"component-editor"
>
${editor}
<a
href=
"#"
class=
"save-button"
>
Save
</a>
<a
href=
"#"
class=
"cancel-button"
>
Cancel
</a>
</div>
cms/templates/unit.html
View file @
8ca10a83
...
...
@@ -9,19 +9,52 @@
<ul>
<li><a
href=
"#"
>
Week 2
</a></li>
<li><a
href=
"#"
>
Linearity and Superposition
</a></li>
<li><span
class=
"current-page"
>
S3V2: Properties of Linearity
</span></li>
<li><span
class=
"current-page"
>
${unit_name}
</span></li>
</ul>
</nav>
<section
class=
'edit-pane'
>
${editable_preview}
</section>
<ol
class=
"components"
>
% for id in components:
<li
class=
"component"
data-id=
"${id}"
/>
% endfor
<li
class=
"new-component-item"
>
<a
href=
"#"
class=
"new-component-button"
>
<span
class=
"plus-icon"
></span>
New Component
</a>
<div
class=
"new-component"
>
<h5>
Select Component Type
</h5>
<ul
class=
"new-component-type"
>
% for type in sorted(component_templates.keys()):
<li>
<a
href=
"#"
data-type=
"${type}"
>
<span
class=
"large-template-icon large-${type}-icon"
></span>
<span
class=
"name"
>
${type}
</span>
</a>
</li>
% endfor
</ul>
</div>
% for type, templates in sorted(component_templates.items()):
<div
class=
"new-component-templates new-component-${type}"
>
<ul
class=
"new-component-template"
>
% for name, location in templates:
<li>
<a
href=
"#"
data-location=
"${location}"
>
<span
class=
"name"
>
${name}
</span>
</a>
</li>
% endfor
</ul>
</div>
% endfor
</li>
</ol>
</article>
<div
class=
"sidebar"
>
<div
class=
"unit-properties window"
>
<h4>
Properties
</h4>
<div
class=
"window-contents"
>
<div
class=
"row"
><label>
Display Name:
</label><input
type=
"text"
value=
"${
module.display
_name}"
/></div>
<div
class=
"row"
><label>
Display Name:
</label><input
type=
"text"
value=
"${
unit
_name}"
/></div>
<div
class=
"row"
>
<label>
State:
</label>
<div
class=
"visibility-options"
>
...
...
cms/urls.py
View file @
8ca10a83
...
...
@@ -9,9 +9,9 @@ import django.contrib.auth.views
urlpatterns
=
(
''
,
url
(
r'^$'
,
'contentstore.views.index'
,
name
=
'index'
),
url
(
r'^new_item$'
,
'contentstore.views.new_item'
,
name
=
'new_item'
),
url
(
r'^edit/(?P<location>.*?)$'
,
'contentstore.views.edit_unit'
,
name
=
'edit_unit'
),
url
(
r'^delete/(?P<location>.*?)$'
,
'contentstore.views.delete_unit'
,
name
=
'delete_unit'
),
url
(
r'^preview_component/(?P<location>.*?)$'
,
'contentstore.views.preview_component'
,
name
=
'preview_component'
),
url
(
r'^save_item$'
,
'contentstore.views.save_item'
,
name
=
'save_item'
),
url
(
r'^clone_item$'
,
'contentstore.views.clone_item'
,
name
=
'clone_item'
),
url
(
r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)$'
,
...
...
common/static/coffee/src/xmodule.coffee
View file @
8ca10a83
...
...
@@ -17,6 +17,8 @@
if
$
(
element
).
hasClass
(
'xmodule_display'
)
$
(
document
).
trigger
(
'XModule.loaded.display'
,
[
element
,
module
])
return
module
catch
error
console
.
error
"Unable to load
#{
moduleType
}
:
#{
error
.
message
}
"
if
console
...
...
@@ -33,7 +35,7 @@
else
modules
=
$
(
selector
)
modules
.
each
(
idx
,
element
)
->
XModule
.
loadModule
element
modules
.
each
((
idx
,
element
)
->
XModule
.
loadModule
element
)
class
@
XModule
.
Descriptor
...
...
common/templates/xmodule_edit.html
View file @
8ca10a83
<div
class=
"xmodule_edit xmodule_${editor_class} ${editable_class}"
data-type=
"${editor_type}"
data-id=
"${location}"
>
<
%
include
file=
"xmodule_display.html"
/>
% if editable_data:
<div
class=
"component-actions"
data-id=
"${location}"
>
<a
href=
"#"
class=
"edit-button"
><span
class=
"edit-icon white"
></span>
Edit
</a>
<a
href=
"#"
class=
"delete-button"
><span
class=
"delete-icon white"
></span>
Delete
</a>
</div>
<div
class=
"component-editor"
data-id=
"${location}"
>
${editor_content}
<a
href=
"#"
class=
"save-button"
>
Save
</a>
<a
href=
"#"
class=
"cancel-button"
>
Cancel
</a>
</div>
% endif
</div>
<section
class=
"xmodule_edit xmodule_${class_}"
data-type=
"${module_name}"
>
${content}
</section>
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