Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Devrim Gunyel
core
Commits
86b49cb1
Commit
86b49cb1
authored
Apr 04, 2019
by
Piotr Gawron
Browse files
Merge branch 'merge' into 'master'
some bugfixes from 12.2.2 into master See merge request
!730
parents
1d81650b
8c8bd890
Changes
17
Hide whitespace changes
Inline
Side-by-side
CHANGELOG
View file @
86b49cb1
...
...
@@ -38,6 +38,21 @@ minerva (12.3.0~alpha.0) unstable; urgency=low
* Bug fix: "Unknown Catalysis" and "Unknown Inhibition" reaction end is
slightly separated from target phenotype (#664)
minerva (12.2.2) stable; urgency=medium
* Bug fix: downloading overlays didn'
t
work
from
admin
panel
when
project
with
different
id
than
default
map
was
accessed
*
Bug
fix
:
change
of
owner
of
the
data
overlay
in
admin
panel
incorrectly
ordered
overlays
(#
777
)
*
Bug
fix
:
chemical
search
didn
't use updated disease identifier, original
disease id from project upload was used instead (#779)
* Bug fix: user login with special characters (like '
@
') could cause
problems in admin panel (#780)
* Bug fix: removing project without full control in the system (but with
enough privileges to remove project) caused "Not enough privileges" error
(#778)
* Bug fix: export of custom properties (like synonyms) are properly encoded
in CellDesigner xml (#785)
minerva (12.2.1) stable; urgency=medium
* Bug fix: export of reaction colorsi in SBML is properly encoded (COPASI can
read colors properly) (#744)
...
...
annotation/src/main/java/lcsb/mapviewer/annotation/services/annotators/EntrezAnnotator.java
View file @
86b49cb1
...
...
@@ -9,6 +9,7 @@ import java.util.HashSet;
import
java.util.List
;
import
java.util.Set
;
import
org.apache.commons.text.StringEscapeUtils
;
import
org.apache.log4j.Logger
;
import
org.springframework.stereotype.Service
;
import
org.w3c.dom.Node
;
...
...
@@ -263,7 +264,7 @@ public class EntrezAnnotator extends ElementAnnotator implements IExternalServic
Node
node
=
list
.
item
(
i
);
if
(
node
.
getNodeType
()
==
Node
.
ELEMENT_NODE
)
{
if
(
node
.
getNodeName
().
equals
(
"Gene-ref_syn_E"
))
{
synonyms
.
add
(
node
.
getTextContent
());
synonyms
.
add
(
StringEscapeUtils
.
unescapeHtml4
(
node
.
getTextContent
())
)
;
}
}
}
...
...
annotation/src/test/java/lcsb/mapviewer/annotation/services/annotators/EntrezAnnotatorTest.java
View file @
86b49cb1
...
...
@@ -271,6 +271,23 @@ public class EntrezAnnotatorTest extends AnnotationTestFunctions {
}
}
@Test
public
void
testAnnotateElementWithEncodedSynonyms
()
throws
Exception
{
try
{
Species
proteinAlias
=
new
GenericProtein
(
"id"
);
proteinAlias
.
addMiriamData
(
new
MiriamData
(
MiriamType
.
ENTREZ
,
"834106"
));
entrezAnnotator
.
annotateElement
(
proteinAlias
);
for
(
String
synonym
:
proteinAlias
.
getSynonyms
())
{
assertFalse
(
"Invalid character found in synonym: "
+
synonym
,
synonym
.
contains
(
"&"
));
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
throw
e
;
}
}
@Test
public
void
testParseEntrezResponse
()
throws
Exception
{
WebPageDownloader
downloader
=
entrezAnnotator
.
getWebPageDownloader
();
...
...
converter-CellDesigner/src/main/java/lcsb/mapviewer/converter/model/celldesigner/annotation/RestAnnotationParser.java
View file @
86b49cb1
...
...
@@ -160,8 +160,9 @@ public class RestAnnotationParser {
return
""
;
}
}
else
if
(
value
instanceof
String
)
{
if
(!((
String
)
value
).
trim
().
isEmpty
()
||
forceFullInfo
)
{
return
type
.
getCommonName
()
+
": "
+
value
+
"\n"
;
String
string
=(
String
)
value
;
if
(!(
string
.
trim
().
isEmpty
())
||
forceFullInfo
)
{
return
type
.
getCommonName
()
+
": "
+
XmlParser
.
escapeXml
(
string
)
+
"\n"
;
}
else
{
return
""
;
}
...
...
@@ -178,7 +179,7 @@ public class RestAnnotationParser {
if
(
object
instanceof
MiriamData
)
{
result
+=
((
MiriamData
)
object
).
getResource
();
}
else
{
result
+=
object
;
result
+=
XmlParser
.
escapeXml
(
object
.
toString
())
;
}
}
...
...
converter-CellDesigner/src/test/java/lcsb/mapviewer/converter/model/celldesigner/CellDesignerXmlParserTest.java
View file @
86b49cb1
...
...
@@ -59,6 +59,8 @@ import lcsb.mapviewer.model.map.species.Species;
public
class
CellDesignerXmlParserTest
extends
CellDesignerTestFunctions
{
Logger
logger
=
Logger
.
getLogger
(
CellDesignerXmlParserTest
.
class
);
ModelComparator
modelComparator
=
new
ModelComparator
();
@Before
public
void
setUp
()
throws
Exception
{
}
...
...
@@ -447,8 +449,6 @@ public class CellDesignerXmlParserTest extends CellDesignerTestFunctions {
InputStream
is
=
new
ByteArrayInputStream
(
xmlString
.
getBytes
());
Model
model2
=
parser
.
createModel
(
new
ConverterParams
().
inputStream
(
is
).
sizeAutoAdjust
(
false
));
ModelComparator
modelComparator
=
new
ModelComparator
();
assertEquals
(
0
,
modelComparator
.
compare
(
model
,
model2
));
}
catch
(
Exception
e
)
{
...
...
@@ -1037,6 +1037,34 @@ public class CellDesignerXmlParserTest extends CellDesignerTestFunctions {
}
}
@Test
public
void
testSpeciesWithSpecialSynonym
()
throws
Exception
{
try
{
Model
model
=
new
ModelFullIndexed
(
null
);
model
.
setIdModel
(
"as"
);
model
.
setWidth
(
10
);
model
.
setHeight
(
10
);
Species
protein
=
new
GenericProtein
(
"id1"
);
protein
.
setWidth
(
10
);
protein
.
setHeight
(
10
);
protein
.
setName
(
"ROS"
);
protein
.
addSynonym
(
"&"
);
model
.
addElement
(
protein
);
CellDesignerXmlParser
parser
=
new
CellDesignerXmlParser
();
String
xmlString
=
parser
.
model2String
(
model
);
InputStream
is
=
new
ByteArrayInputStream
(
xmlString
.
getBytes
(
"UTF-8"
));
Model
model2
=
parser
.
createModel
(
new
ConverterParams
().
inputStream
(
is
));
assertEquals
(
0
,
modelComparator
.
compare
(
model
,
model2
));
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
throw
e
;
}
}
@Test
public
void
testReactionCoordsEqual
()
throws
Exception
{
try
{
...
...
frontend-js/src/main/js/gui/admin/EditProjectDialog.js
View file @
86b49cb1
...
...
@@ -436,7 +436,8 @@ EditProjectDialog.prototype._createOverlayTable = function () {
$
(
overlaysTable
).
on
(
"
click
"
,
"
[name='downloadSource']
"
,
function
()
{
var
button
=
this
;
return
self
.
getServerConnector
().
getOverlaySourceDownloadUrl
({
overlayId
:
$
(
button
).
attr
(
"
data
"
)
overlayId
:
$
(
button
).
attr
(
"
data
"
),
projectId
:
self
.
getProject
().
getProjectId
()
}).
then
(
function
(
url
)
{
return
self
.
downloadFile
(
url
);
}).
then
(
null
,
GuiConnector
.
alert
);
...
...
@@ -990,8 +991,32 @@ EditProjectDialog.prototype.saveOverlay = function (overlayId) {
overlay
.
setDescription
(
$
(
"
[name='description-
"
+
overlayId
+
"
']
"
,
self
.
getElement
())[
0
].
value
);
overlay
.
setPublicOverlay
(
$
(
"
[name='publicOverlay-
"
+
overlayId
+
"
']
"
,
self
.
getElement
())[
0
].
checked
);
overlay
.
setDefaultOverlay
(
$
(
"
[name='defaultOverlay-
"
+
overlayId
+
"
']
"
,
self
.
getElement
())[
0
].
checked
);
overlay
.
setCreator
(
$
(
"
[name='creator-
"
+
overlayId
+
"
']
"
,
self
.
getElement
())[
0
].
value
);
var
creator
=
$
(
"
[name='creator-
"
+
overlayId
+
"
']
"
,
self
.
getElement
())[
0
].
value
;
if
(
creator
===
""
)
{
creator
=
undefined
;
}
if
(
overlay
.
getCreator
()
!==
creator
)
{
//put it on the bottom of ordered list of data overlays for given user
var
order
=
1
;
for
(
var
key
in
self
.
_overlayById
)
{
if
(
self
.
_overlayById
.
hasOwnProperty
(
key
))
{
var
existingOverlay
=
self
.
_overlayById
[
key
];
if
(
existingOverlay
.
getCreator
()
===
creator
)
{
if
(
existingOverlay
.
getId
()
!==
overlayId
)
{
order
=
Math
.
max
(
order
,
self
.
_overlayById
[
key
].
getOrder
()
+
1
);
}
else
{
order
=
Math
.
max
(
order
,
self
.
_overlayById
[
key
].
getOrder
());
}
}
}
}
if
(
creator
===
undefined
)
{
creator
=
""
;
}
overlay
.
setCreator
(
creator
);
overlay
.
setOrder
(
order
);
}
return
self
.
getServerConnector
().
updateOverlay
(
overlay
);
};
...
...
frontend-js/src/main/js/gui/admin/UsersAdminPanel.js
View file @
86b49cb1
...
...
@@ -299,7 +299,7 @@ UsersAdminPanel.prototype.addUpdateListener = function (user, dataTableRow) {
}
}
var
listener
=
function
()
{
var
login
=
user
.
getLogin
().
replace
(
"
.
"
,
"
\\
.
"
);
var
login
=
$
.
escapeSelector
(
user
.
getLogin
()
);
self
.
userToTableRow
(
user
,
dataTableRow
);
var
row
=
$
(
$
(
"
[name='usersTable']
"
,
self
.
getElement
())[
0
]).
DataTable
().
row
(
"
#
"
+
login
);
if
(
row
.
length
>
0
)
{
...
...
frontend-js/src/test/js/gui/admin/UserAdminPanel-test.js
View file @
86b49cb1
...
...
@@ -102,6 +102,18 @@ describe('UsersAdminPanel', function () {
});
});
it
(
'
onUpdateUserListener
'
,
function
()
{
helper
.
loginAsAdmin
();
var
usersTab
=
createUserAdminPanel
();
return
usersTab
.
init
().
then
(
function
()
{
var
user
=
helper
.
createUser
();
user
.
setLogin
(
"
x@y.lu
"
);
var
data
=
[];
usersTab
.
addUpdateListener
(
user
,
data
);
user
.
callListeners
(
"
onreload
"
);
assert
.
ok
(
data
.
indexOf
(
"
x@y.lu
"
)
>=
0
);
return
usersTab
.
destroy
();
});
});
})
;
});
rest-api/src/main/java/lcsb/mapviewer/api/projects/chemicals/ChemicalRestImpl.java
View file @
86b49cb1
...
...
@@ -58,7 +58,7 @@ public class ChemicalRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
if
(
project
.
getDisease
()
==
null
)
{
throw
new
QueryException
(
"Project doesn't have disease associated to it"
);
}
...
...
@@ -175,7 +175,7 @@ public class ChemicalRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
if
(
project
.
getDisease
()
==
null
)
{
throw
new
QueryException
(
"Project doesn't have disease associated to it"
);
...
...
rest-api/src/main/java/lcsb/mapviewer/api/projects/drugs/DrugRestImpl.java
View file @
86b49cb1
...
...
@@ -53,7 +53,7 @@ public class DrugRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
Set
<
String
>
columnSet
=
createDrugColumnSet
(
columns
);
...
...
@@ -159,7 +159,7 @@ public class DrugRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
List
<
Model
>
models
=
getModels
(
projectId
,
"*"
,
token
);
...
...
rest-api/src/main/java/lcsb/mapviewer/api/projects/mirnas/MiRnaRestImpl.java
View file @
86b49cb1
...
...
@@ -53,7 +53,7 @@ public class MiRnaRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
Set
<
String
>
columnSet
=
createMiRnaColumnSet
(
columns
);
...
...
@@ -129,7 +129,7 @@ public class MiRnaRestImpl extends BaseRestImpl {
if
(
model
==
null
)
{
throw
new
QueryException
(
"Project with given id doesn't exist"
);
}
Project
project
=
model
.
getProject
(
);
Project
project
=
getProjectService
().
getProjectByProjectId
(
projectId
,
token
);
List
<
Model
>
models
=
getModels
(
projectId
,
"*"
,
token
);
...
...
rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java
View file @
86b49cb1
package
lcsb.mapviewer.api
;
import
static
org
.
mockito
.
ArgumentMatchers
.
any
;
import
static
org
.
mockito
.
ArgumentMatchers
.
anyString
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileInputStream
;
...
...
@@ -33,6 +36,7 @@ import org.apache.log4j.Logger;
import
org.junit.After
;
import
org.junit.Before
;
import
org.junit.runner.RunWith
;
import
org.mockito.Mockito
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.test.annotation.Rollback
;
...
...
@@ -47,6 +51,7 @@ import org.xml.sax.InputSource;
import
org.xml.sax.SAXException
;
import
lcsb.mapviewer.common.Configuration
;
import
lcsb.mapviewer.common.exception.InvalidStateException
;
import
lcsb.mapviewer.common.exception.InvalidXmlSchemaException
;
import
lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser
;
import
lcsb.mapviewer.converter.zip.ModelZipEntryFile
;
...
...
@@ -56,6 +61,8 @@ import lcsb.mapviewer.model.map.model.Model;
import
lcsb.mapviewer.model.map.model.ModelSubmodelConnection
;
import
lcsb.mapviewer.model.map.model.SubmodelType
;
import
lcsb.mapviewer.persist.DbUtils
;
import
lcsb.mapviewer.services.interfaces.IModelService
;
import
lcsb.mapviewer.services.interfaces.IProjectService
;
import
lcsb.mapviewer.services.interfaces.IUserService
;
@Transactional
...
...
@@ -309,4 +316,21 @@ public abstract class RestTestFunctions {
return
tmp
.
toString
();
}
protected
IProjectService
createProjectMockServiceForModel
(
Model
model
)
{
try
{
IProjectService
mockModelService
=
Mockito
.
mock
(
IProjectService
.
class
);
Mockito
.
when
(
mockModelService
.
getProjectByProjectId
(
anyString
(),
anyString
())).
thenReturn
(
model
.
getProject
());
return
mockModelService
;
}
catch
(
lcsb
.
mapviewer
.
services
.
SecurityException
e
)
{
throw
new
InvalidStateException
(
e
);
}
}
protected
IModelService
createModelMockServiceForModel
(
Model
model
)
throws
lcsb
.
mapviewer
.
services
.
SecurityException
{
IModelService
mockModelService
=
Mockito
.
mock
(
IModelService
.
class
);
Mockito
.
when
(
mockModelService
.
getLastModelByProjectId
(
anyString
(),
any
())).
thenReturn
(
model
);
return
mockModelService
;
}
}
rest-api/src/test/java/lcsb/mapviewer/api/projects/ProjectRestImplTest.java
View file @
86b49cb1
...
...
@@ -33,6 +33,8 @@ import lcsb.mapviewer.converter.zip.ZipEntryFile;
import
lcsb.mapviewer.model.Project
;
import
lcsb.mapviewer.model.map.MiriamType
;
import
lcsb.mapviewer.model.map.model.Model
;
import
lcsb.mapviewer.model.user.PrivilegeType
;
import
lcsb.mapviewer.model.user.User
;
import
lcsb.mapviewer.persist.dao.ProjectDao
;
import
lcsb.mapviewer.services.interfaces.IModelService
;
import
lcsb.mapviewer.services.interfaces.IProjectService
;
...
...
@@ -81,6 +83,24 @@ public class ProjectRestImplTest extends RestTestFunctions {
}
}
@Test
public
void
testRemoveProject
()
throws
Exception
{
try
{
String
projectId
=
"test"
;
Project
project
=
new
Project
();
project
.
setProjectId
(
projectId
);
projectDao
.
add
(
project
);
User
user
=
userService
.
getUserByToken
(
token
);
userService
.
setUserPrivilege
(
user
,
PrivilegeType
.
VIEW_PROJECT
,
1
,
project
.
getId
(),
adminToken
);
userService
.
setUserPrivilege
(
user
,
PrivilegeType
.
PROJECT_MANAGEMENT
,
1
);
// userService.setUserPrivilege(user, PrivilegeType.VIEW_PROJECT, 1);
_projectRestImpl
.
removeProject
(
token
,
projectId
,
null
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
throw
e
;
}
}
@Test
(
expected
=
ObjectNotFoundException
.
class
)
public
void
testGetInvalidMetaData
()
throws
Exception
{
ProjectRestImpl
projectRest
=
createMockProjectRest
(
null
);
...
...
rest-api/src/test/java/lcsb/mapviewer/api/projects/drugs/DrugRestImplTest.java
View file @
86b49cb1
package
lcsb.mapviewer.api.projects.drugs
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
mockito
.
Matchers
.
any
;
import
static
org
.
mockito
.
Matchers
.
anyString
;
import
static
org
.
mockito
.
Argument
Matchers
.
any
;
import
static
org
.
mockito
.
Argument
Matchers
.
anyString
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -16,8 +16,11 @@ import org.mockito.Mockito;
import
org.springframework.beans.factory.annotation.Autowired
;
import
lcsb.mapviewer.api.RestTestFunctions
;
import
lcsb.mapviewer.common.exception.InvalidStateException
;
import
lcsb.mapviewer.model.map.model.Model
;
import
lcsb.mapviewer.services.SecurityException
;
import
lcsb.mapviewer.services.interfaces.IModelService
;
import
lcsb.mapviewer.services.interfaces.IProjectService
;
public
class
DrugRestImplTest
extends
RestTestFunctions
{
Logger
logger
=
Logger
.
getLogger
(
DrugRestImplTest
.
class
);
...
...
@@ -53,7 +56,7 @@ public class DrugRestImplTest extends RestTestFunctions {
throw
e
;
}
}
@Test
public
void
testTargetWithEmptyMechanism
()
throws
Exception
{
try
{
...
...
@@ -69,9 +72,8 @@ public class DrugRestImplTest extends RestTestFunctions {
private
DrugRestImpl
createMockProjectRest
(
String
string
)
throws
Exception
{
Model
model
=
super
.
getModelForFile
(
string
,
true
);
IModelService
mockModelService
=
Mockito
.
mock
(
IModelService
.
class
);
Mockito
.
when
(
mockModelService
.
getLastModelByProjectId
(
anyString
(),
any
())).
thenReturn
(
model
);
_drugRestImpl
.
setModelService
(
mockModelService
);
_drugRestImpl
.
setModelService
(
createModelMockServiceForModel
(
model
));
_drugRestImpl
.
setProjectService
(
createProjectMockServiceForModel
(
model
));
return
_drugRestImpl
;
}
...
...
rest-api/src/test/java/lcsb/mapviewer/api/projects/mirnas/MiRnaRestImplTest.java
View file @
86b49cb1
package
lcsb.mapviewer.api.projects.mirnas
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
mockito
.
Matchers
.
any
;
import
static
org
.
mockito
.
Matchers
.
anyString
;
import
java.util.HashSet
;
import
java.util.List
;
...
...
@@ -14,12 +12,10 @@ import org.junit.After;
import
org.junit.AfterClass
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.mockito.Mockito
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
lcsb.mapviewer.api.RestTestFunctions
;
import
lcsb.mapviewer.model.map.model.Model
;
import
lcsb.mapviewer.services.interfaces.IModelService
;
public
class
MiRnaRestImplTest
extends
RestTestFunctions
{
Logger
logger
=
Logger
.
getLogger
(
MiRnaRestImplTest
.
class
);
...
...
@@ -58,9 +54,8 @@ public class MiRnaRestImplTest extends RestTestFunctions {
private
MiRnaRestImpl
createMockProjectRest
(
String
string
)
throws
Exception
{
Model
model
=
super
.
getModelForFile
(
string
,
true
);
IModelService
mockModelService
=
Mockito
.
mock
(
IModelService
.
class
);
Mockito
.
when
(
mockModelService
.
getLastModelByProjectId
(
anyString
(),
any
())).
thenReturn
(
model
);
_miRnaRestImpl
.
setModelService
(
mockModelService
);
_miRnaRestImpl
.
setModelService
(
createModelMockServiceForModel
(
model
));
_miRnaRestImpl
.
setProjectService
(
createProjectMockServiceForModel
(
model
));
return
_miRnaRestImpl
;
}
...
...
rest-api/src/test/resources/log4j.properties
View file @
86b49cb1
...
...
@@ -22,4 +22,4 @@ log4j.logger.lcsb.mapviewer.annotation.cache=info
log4j.logger.lcsb.mapviewer.persist
=
info
#There are plenty of useless warnings in jsbml library
log4j.logger.org.sbml.jsbml
=
error
log4j.logger.org.sbml.jsbml
=
error
\ No newline at end of file
service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java
View file @
86b49cb1
...
...
@@ -298,7 +298,6 @@ public class ProjectService implements IProjectService {
homeDir
=
null
;
}
}
removePrivilegesForProject
(
p
);
updateProjectStatus
(
p
,
ProjectStatus
.
REMOVING
,
0
,
new
CreateProjectParams
());
Thread
computations
=
new
Thread
(
new
Runnable
()
{
...
...
@@ -350,6 +349,7 @@ public class ProjectService implements IProjectService {
}
}
}
removePrivilegesForProject
(
project
);
projectDao
.
delete
(
project
);
if
(
async
)
{
projectDao
.
commit
();
...
...
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