Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
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
0
Merge Requests
0
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
Kirill Smelkov
Zope
Commits
0a68cd0d
Commit
0a68cd0d
authored
Oct 11, 2001
by
Evan Simpson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve error reporting.
parent
4e3ab31c
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
156 additions
and
52 deletions
+156
-52
lib/python/Products/PageTemplates/Expressions.py
lib/python/Products/PageTemplates/Expressions.py
+33
-7
lib/python/Products/PageTemplates/PageTemplate.py
lib/python/Products/PageTemplates/PageTemplate.py
+13
-2
lib/python/Products/PageTemplates/PageTemplateFile.py
lib/python/Products/PageTemplates/PageTemplateFile.py
+2
-1
lib/python/Products/PageTemplates/TALES.py
lib/python/Products/PageTemplates/TALES.py
+38
-11
lib/python/Products/PageTemplates/ZopePageTemplate.py
lib/python/Products/PageTemplates/ZopePageTemplate.py
+4
-2
lib/python/Products/PageTemplates/www/ptEdit.zpt
lib/python/Products/PageTemplates/www/ptEdit.zpt
+21
-3
lib/python/TAL/HTMLTALParser.py
lib/python/TAL/HTMLTALParser.py
+3
-0
lib/python/TAL/TALGenerator.py
lib/python/TAL/TALGenerator.py
+37
-26
lib/python/TAL/TALInterpreter.py
lib/python/TAL/TALInterpreter.py
+2
-0
lib/python/TAL/TALParser.py
lib/python/TAL/TALParser.py
+3
-0
No files found.
lib/python/Products/PageTemplates/Expressions.py
View file @
0a68cd0d
...
...
@@ -89,7 +89,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths.
"""
__version__
=
'$Revision: 1.2
0
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.2
1
$'
[
11
:
-
2
]
import
re
,
sys
from
TALES
import
Engine
,
CompilerError
,
_valid_name
,
NAME_RE
,
\
...
...
@@ -114,6 +114,7 @@ def installHandlers(engine):
reg
(
'string'
,
StringExpr
)
reg
(
'python'
,
PythonExpr
)
reg
(
'not'
,
NotExpr
)
reg
(
'defer'
,
DeferExpr
)
if
sys
.
modules
.
has_key
(
'Zope'
):
import
AccessControl
...
...
@@ -194,10 +195,14 @@ class PathExpr:
ob
=
econtext
.
contexts
else
:
ob
=
vars
[
base
]
if
isinstance
(
ob
,
DeferWrapper
):
ob
=
ob
()
if
path
:
ob
=
restrictedTraverse
(
ob
,
path
,
securityManager
)
exists
=
1
break
except
Undefined
,
e
:
ob
=
e
except
(
AttributeError
,
KeyError
,
TypeError
,
IndexError
,
'Unauthorized'
),
e
:
ob
=
Undefined
(
self
.
_s
,
sys
.
exc_info
())
...
...
@@ -214,10 +219,10 @@ class PathExpr:
return
self
.
_eval
(
econtext
,
getSecurityManager
())
def
__str__
(
self
):
return
'%s expression
"%s"'
%
(
self
.
_name
,
self
.
_s
)
return
'%s expression
%s'
%
(
self
.
_name
,
`self._s`
)
def
__repr__
(
self
):
return
'
<PathExpr %s:%s>'
%
(
self
.
_name
,
self
.
_s
)
return
'
%s:%s'
%
(
self
.
_name
,
`self._s`
)
_interp
=
re
.
compile
(
r'\
$(%(
n)s)|\
${(%(
n)s(?:/%(n)s)*)}'
%
{
'n'
:
NAME_RE
})
...
...
@@ -242,8 +247,7 @@ class StringExpr:
m
=
_interp
.
search
(
exp
)
if
'$'
in
exp
:
raise
CompilerError
,
(
'$ must be doubled or followed by a variable name '
'in string expression "%s"'
%
expr
)
'$ must be doubled or followed by a simple path'
)
parts
.
append
(
exp
)
expr
=
join
(
parts
,
''
)
self
.
_expr
=
expr
...
...
@@ -261,7 +265,7 @@ class StringExpr:
return
'string expression %s'
%
`self._s`
def
__repr__
(
self
):
return
'
<StringExpr %s>
'
%
`self._s`
return
'
string:%s
'
%
`self._s`
class
NotExpr
:
def
__init__
(
self
,
name
,
expr
,
compiler
):
...
...
@@ -272,7 +276,29 @@ class NotExpr:
return
not
econtext
.
evaluateBoolean
(
self
.
_c
)
def
__repr__
(
self
):
return
'<NotExpr %s>'
%
`self._s`
return
'not:%s'
%
`self._s`
class
DeferWrapper
:
def
__init__
(
self
,
expr
,
econtext
):
self
.
_expr
=
expr
self
.
_econtext
=
econtext
def
__str__
(
self
):
return
str
(
self
())
def
__call__
(
self
):
return
self
.
_expr
(
self
.
_econtext
)
class
DeferExpr
:
def
__init__
(
self
,
name
,
expr
,
compiler
):
self
.
_s
=
expr
=
lstrip
(
expr
)
self
.
_c
=
compiler
.
compile
(
expr
)
def
__call__
(
self
,
econtext
):
return
DeferWrapper
(
self
.
_c
,
econtext
)
def
__repr__
(
self
):
return
'defer:%s'
%
`self._s`
def
restrictedTraverse
(
self
,
path
,
securityManager
,
...
...
lib/python/Products/PageTemplates/PageTemplate.py
View file @
0a68cd0d
...
...
@@ -87,7 +87,7 @@
HTML- and XML-based template objects using TAL, TALES, and METAL.
"""
__version__
=
'$Revision: 1.1
4
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.1
5
$'
[
11
:
-
2
]
import
os
,
sys
,
traceback
,
pprint
from
TAL.TALParser
import
TALParser
...
...
@@ -111,6 +111,7 @@ class PageTemplate(Base):
content_type
=
'text/html'
expand
=
1
_v_errors
=
()
_v_warnings
=
()
_text
=
''
_error_start
=
'<!-- Page Template Diagnostics'
...
...
@@ -162,7 +163,16 @@ class PageTemplate(Base):
return
self
.
pt_render
(
extra_context
=
{
'options'
:
kwargs
})
def
pt_errors
(
self
):
return
self
.
_v_errors
err
=
self
.
_v_errors
if
err
:
return
err
try
:
self
.
pt_render
(
source
=
1
)
except
:
return
(
'Macro expansion failed'
,
'%s: %s'
%
sys
.
exc_info
()[:
2
])
def
pt_warnings
(
self
):
return
self
.
_v_warnings
def
write
(
self
,
text
):
assert
type
(
text
)
is
type
(
''
)
...
...
@@ -209,6 +219,7 @@ class PageTemplate(Base):
except
:
self
.
_v_errors
=
[
"Compilation failed"
,
"%s: %s"
%
sys
.
exc_info
()[:
2
]]
self
.
_v_warnings
=
parser
.
getWarnings
()
def
html
(
self
):
return
self
.
content_type
==
'text/html'
...
...
lib/python/Products/PageTemplates/PageTemplateFile.py
View file @
0a68cd0d
...
...
@@ -87,7 +87,7 @@
Zope object encapsulating a Page Template from the filesystem.
"""
__version__
=
'$Revision: 1.
3
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.
4
$'
[
11
:
-
2
]
import
os
,
AccessControl
,
Acquisition
,
sys
from
Globals
import
package_home
,
DevelopmentMode
...
...
@@ -157,6 +157,7 @@ class PageTemplateFile(Script, PageTemplate, Traversable):
# Execute the template in a new security context.
security
=
getSecurityManager
()
bound_names
[
'user'
]
=
security
.
getUser
()
security
.
addContext
(
self
)
try
:
return
self
.
pt_render
(
extra_context
=
bound_names
)
...
...
lib/python/Products/PageTemplates/TALES.py
View file @
0a68cd0d
...
...
@@ -87,7 +87,7 @@
An implementation of a generic TALES engine
"""
__version__
=
'$Revision: 1.2
1
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.2
2
$'
[
11
:
-
2
]
import
re
,
sys
,
ZTUtils
from
MultiMapping
import
MultiMapping
...
...
@@ -100,24 +100,41 @@ _valid_name = re.compile('%s$' % NAME_RE).match
class
TALESError
(
Exception
):
__allow_access_to_unprotected_subobjects__
=
1
def
__init__
(
self
,
expression
,
info
=
(
None
,
None
,
None
)):
def
__init__
(
self
,
expression
,
info
=
(
None
,
None
,
None
),
position
=
(
None
,
None
)):
self
.
type
,
self
.
value
,
self
.
traceback
=
info
self
.
expression
=
expression
self
.
setPosition
(
position
)
def
setPosition
(
self
,
position
):
self
.
lineno
=
position
[
0
]
self
.
offset
=
position
[
1
]
def
__str__
(
self
):
if
self
.
type
is
not
None
:
return
'%s on %s in "%s"'
%
(
self
.
type
,
self
.
value
,
self
.
expression
)
return
self
.
expression
if
self
.
type
is
None
:
s
=
self
.
expression
else
:
s
=
'%s on %s in %s'
%
(
self
.
type
,
self
.
value
,
`self.expression`
)
if
self
.
lineno
is
not
None
:
s
=
"%s, at line %d"
%
(
s
,
self
.
lineno
)
if
self
.
offset
is
not
None
:
s
=
"%s, column %d"
%
(
s
,
self
.
offset
+
1
)
return
s
def
__nonzero__
(
self
):
return
0
class
Undefined
(
TALESError
):
'''Exception raised on traversal of an undefined path'''
def
__str__
(
self
):
if
self
.
type
is
not
None
:
return
'"%s" not found in "%s"'
%
(
self
.
value
,
self
.
expression
)
return
self
.
expression
if
self
.
type
is
None
:
s
=
self
.
expression
else
:
s
=
'%s not found in %s'
%
(
self
.
value
,
`self.expression`
)
if
self
.
lineno
is
not
None
:
s
=
"%s, at line %d"
%
(
s
,
self
.
lineno
)
if
self
.
offset
is
not
None
:
s
=
"%s, column %d"
%
(
s
,
self
.
offset
+
1
)
return
s
class
RegistrationError
(
Exception
):
'''TALES Type Registration Error'''
...
...
@@ -221,6 +238,8 @@ class Engine:
kwcontexts
=
contexts
return
Context
(
self
,
kwcontexts
)
def
getCompilerError
(
self
):
return
CompilerError
class
Context
:
'''Expression Context
...
...
@@ -231,6 +250,7 @@ class Context:
_context_class
=
SafeMapping
_nocatch
=
TALESError
position
=
(
None
,
None
)
def
__init__
(
self
,
engine
,
contexts
):
self
.
_engine
=
engine
...
...
@@ -297,10 +317,14 @@ class Context:
if
hasattr
(
v
,
'traceback'
):
raise
v
,
None
,
v
.
traceback
raise
v
except
TALESError
,
err
:
err
.
setPosition
(
self
.
position
)
raise
err
,
None
,
sys
.
exc_info
()[
2
]
except
self
.
_nocatch
:
raise
except
:
raise
TALESError
,
(
`expression`
,
sys
.
exc_info
()),
sys
.
exc_info
()[
2
]
raise
TALESError
,
(
`expression`
,
sys
.
exc_info
(),
self
.
position
),
sys
.
exc_info
()[
2
]
else
:
return
v
...
...
@@ -330,6 +354,9 @@ class Context:
def
getDefault
(
self
):
return
Default
def
setPosition
(
self
,
position
):
self
.
position
=
position
class
SimpleExpr
:
'''Simple example of an expression type handler'''
def
__init__
(
self
,
name
,
expr
,
engine
):
...
...
lib/python/Products/PageTemplates/ZopePageTemplate.py
View file @
0a68cd0d
...
...
@@ -87,7 +87,7 @@
Zope object encapsulating a Page Template.
"""
__version__
=
'$Revision: 1.1
8
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.1
9
$'
[
11
:
-
2
]
import
os
,
AccessControl
,
Acquisition
,
sys
from
Globals
import
DTMLFile
,
MessageDialog
,
package_home
...
...
@@ -160,7 +160,7 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
security
.
declareProtected
(
'View management screens'
,
'pt_editForm'
,
'manage_main'
,
'read'
,
'ZScriptHTML_tryForm'
,
'PrincipiaSearchSource'
,
'document_src'
)
'document_src'
,
'source.html'
,
'source.xml'
)
security
.
declareProtected
(
'Change Page Templates'
,
'pt_editAction'
,
'pt_setTitle'
,
'pt_edit'
,
...
...
@@ -310,6 +310,8 @@ class ZopePageTemplate(Script, PageTemplate, Historical, Cacheable,
if
RESPONSE
is
not
None
:
RESPONSE
.
setHeader
(
'Content-Type'
,
self
.
content_type
)
if
REQUEST
.
get
(
'raw'
):
return
self
.
_text
return
self
.
read
()
def
__setstate__
(
self
,
state
):
...
...
lib/python/Products/PageTemplates/www/ptEdit.zpt
View file @
0a68cd0d
...
...
@@ -2,9 +2,9 @@
<h2 tal:define="manage_tabs_message options/manage_tabs_message | nothing"
tal:replace="structure here/manage_tabs">Tabs</h2>
<
form action="" method="post"
tal:define="body request/other/text | request/form/text | here/read"
tal:attributes="action request/URL1">
<
tal:block define="global body request/other/text | request/form/text
| here/read" />
<form action="" method="post"
tal:attributes="action request/URL1">
<input type="hidden" name=":default_method" value="pt_changePrefs">
<table width="100%" cellspacing="0" cellpadding="2" border="0">
<tr>
...
...
@@ -48,6 +48,24 @@
Expand macros when editing
</td>
</tr>
<tr tal:define="errors here/pt_errors" tal:condition="errors">
<tal:block define="global body python:here.document_src({'raw':1})"/>
<td align="left" valign="middle" class="form-label">Errors</td>
<td align="left" valign="middle" style="background-color: #FFDDDD"
colspan="3">
<pre tal:content="python:modules['string'].join(errors, '\n')">errors</pre>
</td>
</tr>
<tr tal:define="warnings here/pt_warnings" tal:condition="warnings">
<td align="left" valign="middle" class="form-label">Warnings</td>
<td align="left" valign="middle" style="background-color: #FFEEDD"
colspan="3">
<pre tal:content="python:modules['string'].join(warnings, '\n')">errors</pre>
</td>
</tr>
<tr>
<td align="left" valign="top" colspan="4">
<div style="width: 100%;">
...
...
lib/python/TAL/HTMLTALParser.py
View file @
0a68cd0d
...
...
@@ -188,6 +188,9 @@ class HTMLTALParser(HTMLParser):
def
getCode
(
self
):
return
self
.
gen
.
getCode
()
def
getWarnings
(
self
):
return
()
# Overriding HTMLParser methods
def
handle_starttag
(
self
,
tag
,
attrs
):
...
...
lib/python/TAL/TALGenerator.py
View file @
0a68cd0d
...
...
@@ -102,6 +102,7 @@ class TALGenerator:
from
DummyEngine
import
DummyEngine
expressionCompiler
=
DummyEngine
()
self
.
expressionCompiler
=
expressionCompiler
self
.
CompilerError
=
expressionCompiler
.
getCompilerError
()
self
.
program
=
[]
self
.
stack
=
[]
self
.
todoStack
=
[]
...
...
@@ -229,7 +230,11 @@ class TALGenerator:
return
self
.
todoStack
.
pop
()
def
compileExpression
(
self
,
expr
):
return
self
.
expressionCompiler
.
compile
(
expr
)
try
:
return
self
.
expressionCompiler
.
compile
(
expr
)
except
self
.
CompilerError
,
err
:
raise
TALError
(
'%s in expression %s'
%
(
err
.
args
[
0
],
`expr`
),
self
.
position
)
def
pushProgram
(
self
):
self
.
stack
.
append
(
self
.
program
)
...
...
@@ -284,12 +289,13 @@ class TALGenerator:
def
emitText
(
self
,
text
):
self
.
emitRawText
(
cgi
.
escape
(
text
))
def
emitDefines
(
self
,
defines
,
position
):
def
emitDefines
(
self
,
defines
):
for
part
in
splitParts
(
defines
):
m
=
re
.
match
(
r"(?s)\
s*(?:(glo
bal|local)\
s+)?(%s)
\s+(.*)\
Z
" % NAME_RE, part)
if not m:
raise TALError("
invalid
define
syntax
:
" + `part`, position)
raise TALError("
invalid
define
syntax
:
" + `part`,
self.position)
scope, name, expr = m.group(1, 2, 3)
scope = scope or "
local
"
cexpr = self.compileExpression(expr)
...
...
@@ -298,9 +304,9 @@ class TALGenerator:
else:
self.emit("
setGlobal
", name, cexpr)
def emitOnError(self, name, onError
, position
):
def emitOnError(self, name, onError):
block = self.popProgram()
key, expr = parseSubstitution(onError
, position
)
key, expr = parseSubstitution(onError)
cexpr = self.compileExpression(expr)
if key == "
text
":
self.emit("
insertText
", cexpr, [])
...
...
@@ -316,17 +322,18 @@ class TALGenerator:
program = self.popProgram()
self.emit("
condition
", cexpr, program)
def emitRepeat(self, arg
, position=(None, None)
):
def emitRepeat(self, arg):
m = re.match("
(
?
s
)
\
s
*
(
%
s
)
\
s
+
(.
*
)
\
Z
" % NAME_RE, arg)
if not m:
raise TALError("
invalid
repeat
syntax
:
" + `arg`, position)
raise TALError("
invalid
repeat
syntax
:
" + `arg`,
self.position)
name, expr = m.group(1, 2)
cexpr = self.compileExpression(expr)
program = self.popProgram()
self.emit("
loop
", name, cexpr, program)
def emitSubstitution(self, arg, attrDict={}
, position=(None, None)
):
key, expr = parseSubstitution(arg
, position
)
def emitSubstitution(self, arg, attrDict={}):
key, expr = parseSubstitution(arg)
cexpr = self.compileExpression(expr)
program = self.popProgram()
if key == "
text
":
...
...
@@ -335,14 +342,15 @@ class TALGenerator:
assert key == "
structure
"
self.emit("
insertStructure
", cexpr, attrDict, program)
def emitDefineMacro(self, macroName
, position=(None, None)
):
def emitDefineMacro(self, macroName):
program = self.popProgram()
macroName = string.strip(macroName)
if self.macros.has_key(macroName):
raise METALError("
duplicate
macro
definition
:
%
s
" % `macroName`,
position)
self.
position)
if not re.match('%s$' % NAME_RE, macroName):
raise METALError("
invalid
macro
name
:
%
s
" % `macroName`, position)
raise METALError("
invalid
macro
name
:
%
s
" % `macroName`,
self.position)
self.macros[macroName] = program
self.inMacroDef = self.inMacroDef - 1
self.emit("
defineMacro
", macroName, program)
...
...
@@ -353,21 +361,23 @@ class TALGenerator:
self.inMacroUse = 0
self.emit("
useMacro
", expr, cexpr, self.popSlots(), program)
def emitDefineSlot(self, slotName
, position=(None, None)
):
def emitDefineSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
if not re.match('%s$' % NAME_RE, slotName):
raise METALError("
invalid
slot
name
:
%
s
" % `slotName`, position)
raise METALError("
invalid
slot
name
:
%
s
" % `slotName`,
self.position)
self.emit("
defineSlot
", slotName, program)
def emitFillSlot(self, slotName
, position=(None, None)
):
def emitFillSlot(self, slotName):
program = self.popProgram()
slotName = string.strip(slotName)
if self.slots.has_key(slotName):
raise METALError("
duplicate
fill
-
slot
name
:
%
s
" % `slotName`,
position)
self.
position)
if not re.match('%s$' % NAME_RE, slotName):
raise METALError("
invalid
slot
name
:
%
s
" % `slotName`, position)
raise METALError("
invalid
slot
name
:
%
s
" % `slotName`,
self.position)
self.slots[slotName] = program
self.inMacroUse = 1
self.emit("
fillSlot
", slotName, program)
...
...
@@ -443,6 +453,7 @@ class TALGenerator:
self.emitEndElement(name, isend)
return
self.position = position
for key, value in taldict.items():
if key not in KNOWN_TAL_ATTRIBUTES:
raise TALError("
bad
TAL
attribute
:
" + `key`, position)
...
...
@@ -527,7 +538,7 @@ class TALGenerator:
self.pushProgram() # block
todo["
onError
"] = onError
if define:
self.emitDefines(define
, position
)
self.emitDefines(define)
todo["
define
"] = define
if condition:
self.pushProgram()
...
...
@@ -574,7 +585,7 @@ class TALGenerator:
self.emitEndTag(name)
return
position = todo.get("
position
", (None, None))
self.position =
position = todo.get("
position
", (None, None))
defineMacro = todo.get("
defineMacro
")
useMacro = todo.get("
useMacro
")
defineSlot = todo.get("
defineSlot
")
...
...
@@ -600,29 +611,29 @@ class TALGenerator:
(what, name, name), position)
if content:
self.emitSubstitution(content, {}
, position
)
self.emitSubstitution(content, {})
if optTag:
self.emitOptTag(name, optTag, isend)
elif not isend:
self.emitEndTag(name)
if replace:
self.emitSubstitution(replace, repldict
, position
)
self.emitSubstitution(replace, repldict)
if repeat:
self.emitRepeat(repeat
, position
)
self.emitRepeat(repeat)
if condition:
self.emitCondition(condition)
if onError:
self.emitOnError(name, onError
, position
)
self.emitOnError(name, onError)
if scope:
self.emit("
endScope
")
if defineSlot:
self.emitDefineSlot(defineSlot
, position
)
self.emitDefineSlot(defineSlot)
if fillSlot:
self.emitFillSlot(fillSlot
, position
)
self.emitFillSlot(fillSlot)
if useMacro:
self.emitUseMacro(useMacro)
if defineMacro:
self.emitDefineMacro(defineMacro
, position
)
self.emitDefineMacro(defineMacro)
def test():
t = TALGenerator()
...
...
lib/python/TAL/TALInterpreter.py
View file @
0a68cd0d
...
...
@@ -201,6 +201,7 @@ class TALInterpreter:
while
self
.
scopeLevel
>
scopeLevel
:
self
.
engine
.
endScope
()
self
.
scopeLevel
=
self
.
scopeLevel
-
1
self
.
engine
.
setPosition
(
self
.
position
)
def
restoreOutputState
(
self
,
state
):
(
dummy
,
self
.
col
,
self
.
stream
,
scopeLevel
,
level
)
=
state
...
...
@@ -279,6 +280,7 @@ class TALInterpreter:
def
do_setPosition
(
self
,
position
):
self
.
position
=
position
self
.
engine
.
setPosition
(
position
)
bytecode_handlers
[
"setPosition"
]
=
do_setPosition
def
do_startEndTag
(
self
,
stuff
):
...
...
lib/python/TAL/TALParser.py
View file @
0a68cd0d
...
...
@@ -107,6 +107,9 @@ class TALParser(XMLParser):
def
getCode
(
self
):
return
self
.
gen
.
getCode
()
def
getWarnings
(
self
):
return
()
def
StartNamespaceDeclHandler
(
self
,
prefix
,
uri
):
self
.
nsStack
.
append
(
self
.
nsDict
.
copy
())
self
.
nsDict
[
uri
]
=
prefix
...
...
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