Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linphone-desktop
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
Administrator
linphone-desktop
Commits
4ce00a71
Commit
4ce00a71
authored
Dec 30, 2016
by
Ronan Abhamon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(app): handle correctly sip addresses changes (on contact, from history, ...)
parent
25b69259
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
188 additions
and
81 deletions
+188
-81
ContactModel.cpp
tests/src/components/contact/ContactModel.cpp
+49
-2
ContactModel.hpp
tests/src/components/contact/ContactModel.hpp
+7
-12
VcardModel.cpp
tests/src/components/contact/VcardModel.cpp
+19
-19
ContactsListModel.cpp
tests/src/components/contacts/ContactsListModel.cpp
+23
-2
ContactsListModel.hpp
tests/src/components/contacts/ContactsListModel.hpp
+6
-0
SipAddressesModel.cpp
tests/src/components/sip-addresses/SipAddressesModel.cpp
+80
-45
SipAddressesModel.hpp
tests/src/components/sip-addresses/SipAddressesModel.hpp
+4
-1
No files found.
tests/src/components/contact/ContactModel.cpp
View file @
4ce00a71
#include <QSet>
#include "../../app/App.hpp"
#include "ContactModel.hpp"
...
...
@@ -11,7 +13,6 @@ ContactModel::ContactModel (shared_ptr<linphone::Friend> linphone_friend) {
m_vcard
=
make_shared
<
VcardModel
>
(
linphone_friend
->
getVcard
());
App
::
getInstance
()
->
getEngine
()
->
setObjectOwnership
(
m_vcard
.
get
(),
QQmlEngine
::
CppOwnership
);
QObject
::
connect
(
m_vcard
.
get
(),
&
VcardModel
::
vcardUpdated
,
this
,
&
ContactModel
::
contactUpdated
);
}
ContactModel
::
ContactModel
(
VcardModel
*
vcard
)
{
...
...
@@ -23,7 +24,53 @@ ContactModel::ContactModel (VcardModel *vcard) {
m_vcard
.
reset
(
vcard
);
engine
->
setObjectOwnership
(
vcard
,
QQmlEngine
::
CppOwnership
);
QObject
::
connect
(
vcard
,
&
VcardModel
::
vcardUpdated
,
this
,
&
ContactModel
::
contactUpdated
);
}
void
ContactModel
::
startEdit
()
{
m_linphone_friend
->
edit
();
m_old_sip_addresses
=
m_vcard
->
getSipAddresses
();
}
void
ContactModel
::
endEdit
()
{
m_linphone_friend
->
done
();
QVariantList
sip_addresses
=
m_vcard
->
getSipAddresses
();
QSet
<
QString
>
done
;
for
(
const
auto
&
variant_a
:
m_old_sip_addresses
)
{
next:
const
QString
&
sip_address
=
variant_a
.
toString
();
if
(
done
.
contains
(
sip_address
))
continue
;
done
.
insert
(
sip_address
);
// Check if old sip address exists in new set => No changes.
for
(
const
auto
&
variant_b
:
sip_addresses
)
{
if
(
sip_address
==
variant_b
.
toString
())
goto
next
;
}
emit
sipAddressRemoved
(
sip_address
);
}
m_old_sip_addresses
.
clear
();
for
(
const
auto
&
variant
:
sip_addresses
)
{
const
QString
&
sip_address
=
variant
.
toString
();
if
(
done
.
contains
(
sip_address
))
continue
;
done
.
insert
(
sip_address
);
emit
sipAddressAdded
(
sip_address
);
}
emit
contactUpdated
();
}
void
ContactModel
::
abortEdit
()
{
// TODO: call linphone friend abort function when available.
// m_linphone_friend->abort();
m_old_sip_addresses
.
clear
();
}
Presence
::
PresenceStatus
ContactModel
::
getPresenceStatus
()
const
{
...
...
tests/src/components/contact/ContactModel.hpp
View file @
4ce00a71
...
...
@@ -27,21 +27,14 @@ public:
}
public
slots
:
void
startEdit
()
{
m_linphone_friend
->
edit
();
}
void
endEdit
()
{
m_linphone_friend
->
done
();
}
void
abortEdit
()
{
// TODO: call linphone friend abort function.
// m_linphone_friend->abort();
}
void
startEdit
();
void
endEdit
();
void
abortEdit
();
signals:
void
contactUpdated
();
void
sipAddressAdded
(
const
QString
&
sip_address
);
void
sipAddressRemoved
(
const
QString
&
sip_address
);
private:
Presence
::
PresenceStatus
getPresenceStatus
()
const
;
...
...
@@ -51,6 +44,8 @@ private:
return
m_vcard
.
get
();
}
QVariantList
m_old_sip_addresses
;
std
::
shared_ptr
<
VcardModel
>
m_vcard
;
std
::
shared_ptr
<
linphone
::
Friend
>
m_linphone_friend
;
};
...
...
tests/src/components/contact/VcardModel.cpp
View file @
4ce00a71
...
...
@@ -156,7 +156,7 @@ inline shared_ptr<belcard::BelCardAddress> getOrCreateBelCardAddress (shared_ptr
if
(
addresses
.
empty
())
{
address
=
belcard
::
BelCardGeneric
::
create
<
belcard
::
BelCardAddress
>
();
if
(
!
belcard
->
addAddress
(
address
))
qWarning
()
<<
"Unable to create a new address."
;
qWarning
()
<<
"Unable to create a new address
on vcard
."
;
}
else
address
=
addresses
.
front
();
...
...
@@ -227,7 +227,7 @@ bool VcardModel::addSipAddress (const QString &sip_address) {
);
if
(
!
l_address
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to add invalid sip address: `%1`."
).
arg
(
sip_address
);
qWarning
()
<<
QStringLiteral
(
"Unable to add invalid sip address
on vcard
: `%1`."
).
arg
(
sip_address
);
return
false
;
}
...
...
@@ -236,10 +236,10 @@ bool VcardModel::addSipAddress (const QString &sip_address) {
shared_ptr
<
belcard
::
BelCardImpp
>
value
=
belcard
::
BelCardGeneric
::
create
<
belcard
::
BelCardImpp
>
();
value
->
setValue
(
l_address
->
asStringUriOnly
());
qInfo
()
<<
QStringLiteral
(
"Add new sip address: `%1`."
).
arg
(
sip_address
);
qInfo
()
<<
QStringLiteral
(
"Add new sip address
on vcard
: `%1`."
).
arg
(
sip_address
);
if
(
!
belcard
->
addImpp
(
value
))
{
qWarning
()
<<
QStringLiteral
(
"Unable to add sip address: `%1`."
).
arg
(
sip_address
);
qWarning
()
<<
QStringLiteral
(
"Unable to add sip address
on vcard
: `%1`."
).
arg
(
sip_address
);
return
false
;
}
...
...
@@ -253,17 +253,17 @@ void VcardModel::removeSipAddress (const QString &sip_address) {
shared_ptr
<
belcard
::
BelCardImpp
>
value
=
findBelCardValue
(
addresses
,
sip_address
);
if
(
!
value
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove sip address: `%1`."
).
arg
(
sip_address
);
qWarning
()
<<
QStringLiteral
(
"Unable to remove sip address
on vcard
: `%1`."
).
arg
(
sip_address
);
return
;
}
if
(
addresses
.
size
()
==
1
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove the only existing sip address: `%1`."
)
qWarning
()
<<
QStringLiteral
(
"Unable to remove the only existing sip address
on vcard
: `%1`."
)
.
arg
(
sip_address
);
return
;
}
qInfo
()
<<
QStringLiteral
(
"Remove sip address: `%1`."
).
arg
(
sip_address
);
qInfo
()
<<
QStringLiteral
(
"Remove sip address
on vcard
: `%1`."
).
arg
(
sip_address
);
belcard
->
removeImpp
(
value
);
emit
vcardUpdated
();
...
...
@@ -294,10 +294,10 @@ bool VcardModel::addCompany (const QString &company) {
shared_ptr
<
belcard
::
BelCardRole
>
value
=
belcard
::
BelCardGeneric
::
create
<
belcard
::
BelCardRole
>
();
value
->
setValue
(
::
Utils
::
qStringToLinphoneString
(
company
));
qInfo
()
<<
QStringLiteral
(
"Add new company: `%1`."
).
arg
(
company
);
qInfo
()
<<
QStringLiteral
(
"Add new company
on vcard
: `%1`."
).
arg
(
company
);
if
(
!
belcard
->
addRole
(
value
))
{
qWarning
()
<<
QStringLiteral
(
"Unable to add company: `%1`."
).
arg
(
company
);
qWarning
()
<<
QStringLiteral
(
"Unable to add company
on vcard
: `%1`."
).
arg
(
company
);
return
false
;
}
...
...
@@ -310,11 +310,11 @@ void VcardModel::removeCompany (const QString &company) {
shared_ptr
<
belcard
::
BelCardRole
>
value
=
findBelCardValue
(
belcard
->
getRoles
(),
company
);
if
(
!
value
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove company: `%1`."
).
arg
(
company
);
qWarning
()
<<
QStringLiteral
(
"Unable to remove company
on vcard
: `%1`."
).
arg
(
company
);
return
;
}
qInfo
()
<<
QStringLiteral
(
"Remove company: `%1`."
).
arg
(
company
);
qInfo
()
<<
QStringLiteral
(
"Remove company
on vcard
: `%1`."
).
arg
(
company
);
belcard
->
removeRole
(
value
);
emit
vcardUpdated
();
...
...
@@ -345,10 +345,10 @@ bool VcardModel::addEmail (const QString &email) {
shared_ptr
<
belcard
::
BelCardEmail
>
value
=
belcard
::
BelCardGeneric
::
create
<
belcard
::
BelCardEmail
>
();
value
->
setValue
(
::
Utils
::
qStringToLinphoneString
(
email
));
qInfo
()
<<
QStringLiteral
(
"Add new email: `%1`."
).
arg
(
email
);
qInfo
()
<<
QStringLiteral
(
"Add new email
on vcard
: `%1`."
).
arg
(
email
);
if
(
!
belcard
->
addEmail
(
value
))
{
qWarning
()
<<
QStringLiteral
(
"Unable to add email: `%1`."
).
arg
(
email
);
qWarning
()
<<
QStringLiteral
(
"Unable to add email
on vcard
: `%1`."
).
arg
(
email
);
return
false
;
}
...
...
@@ -361,11 +361,11 @@ void VcardModel::removeEmail (const QString &email) {
shared_ptr
<
belcard
::
BelCardEmail
>
value
=
findBelCardValue
(
belcard
->
getEmails
(),
email
);
if
(
!
value
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove email: `%1`."
).
arg
(
email
);
qWarning
()
<<
QStringLiteral
(
"Unable to remove email
on vcard
: `%1`."
).
arg
(
email
);
return
;
}
qInfo
()
<<
QStringLiteral
(
"Remove email: `%1`."
).
arg
(
email
);
qInfo
()
<<
QStringLiteral
(
"Remove email
on vcard
: `%1`."
).
arg
(
email
);
belcard
->
removeEmail
(
value
);
emit
vcardUpdated
();
...
...
@@ -396,10 +396,10 @@ bool VcardModel::addUrl (const QString &url) {
shared_ptr
<
belcard
::
BelCardURL
>
value
=
belcard
::
BelCardGeneric
::
create
<
belcard
::
BelCardURL
>
();
value
->
setValue
(
::
Utils
::
qStringToLinphoneString
(
url
));
qInfo
()
<<
QStringLiteral
(
"Add new url: `%1`."
).
arg
(
url
);
qInfo
()
<<
QStringLiteral
(
"Add new url
on vcard
: `%1`."
).
arg
(
url
);
if
(
!
belcard
->
addURL
(
value
))
{
qWarning
()
<<
QStringLiteral
(
"Unable to add url: `%1`."
).
arg
(
url
);
qWarning
()
<<
QStringLiteral
(
"Unable to add url
on vcard
: `%1`."
).
arg
(
url
);
return
false
;
}
...
...
@@ -412,11 +412,11 @@ void VcardModel::removeUrl (const QString &url) {
shared_ptr
<
belcard
::
BelCardURL
>
value
=
findBelCardValue
(
belcard
->
getURLs
(),
url
);
if
(
!
value
)
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove url: `%1`."
).
arg
(
url
);
qWarning
()
<<
QStringLiteral
(
"Unable to remove url
on vcard
: `%1`."
).
arg
(
url
);
return
;
}
qInfo
()
<<
QStringLiteral
(
"Remove url: `%1`."
).
arg
(
url
);
qInfo
()
<<
QStringLiteral
(
"Remove url
on vcard
: `%1`."
).
arg
(
url
);
belcard
->
removeURL
(
value
);
emit
vcardUpdated
();
...
...
tests/src/components/contacts/ContactsListModel.cpp
View file @
4ce00a71
...
...
@@ -23,7 +23,7 @@ ContactsListModel::ContactsListModel (QObject *parent) : QAbstractListModel(pare
contact
,
QQmlEngine
::
CppOwnership
);
m_list
<<
contact
;
addContact
(
contact
)
;
}
}
...
...
@@ -95,7 +95,7 @@ ContactModel *ContactsListModel::addContact (VcardModel *vcard) {
int
row
=
rowCount
();
beginInsertRows
(
QModelIndex
(),
row
,
row
);
m_list
<<
contact
;
addContact
(
contact
)
;
endInsertRows
();
emit
contactAdded
(
contact
);
...
...
@@ -110,3 +110,24 @@ void ContactsListModel::removeContact (ContactModel *contact) {
if
(
index
==
-
1
||
!
removeRow
(
index
))
qWarning
()
<<
"Unable to remove contact:"
<<
contact
;
}
void
ContactsListModel
::
addContact
(
ContactModel
*
contact
)
{
QObject
::
connect
(
contact
,
&
ContactModel
::
contactUpdated
,
this
,
[
this
,
contact
]()
{
emit
contactUpdated
(
contact
);
}
);
QObject
::
connect
(
contact
,
&
ContactModel
::
sipAddressAdded
,
this
,
[
this
,
contact
](
const
QString
&
sip_address
)
{
emit
sipAddressAdded
(
contact
,
sip_address
);
}
);
QObject
::
connect
(
contact
,
&
ContactModel
::
sipAddressRemoved
,
this
,
[
this
,
contact
](
const
QString
&
sip_address
)
{
emit
sipAddressRemoved
(
contact
,
sip_address
);
}
);
m_list
<<
contact
;
}
tests/src/components/contacts/ContactsListModel.hpp
View file @
4ce00a71
...
...
@@ -32,8 +32,14 @@ public slots:
signals:
void
contactAdded
(
ContactModel
*
contact
);
void
contactRemoved
(
const
ContactModel
*
contact
);
void
contactUpdated
(
ContactModel
*
contact
);
void
sipAddressAdded
(
ContactModel
*
contact
,
const
QString
&
sip_address
);
void
sipAddressRemoved
(
ContactModel
*
contact
,
const
QString
&
sip_address
);
private:
void
addContact
(
ContactModel
*
contact
);
QList
<
ContactModel
*>
m_list
;
std
::
shared_ptr
<
linphone
::
FriendList
>
m_linphone_friends
;
};
...
...
tests/src/components/sip-addresses/SipAddressesModel.cpp
View file @
4ce00a71
...
...
@@ -12,37 +12,39 @@
SipAddressesModel
::
SipAddressesModel
(
QObject
*
parent
)
:
QAbstractListModel
(
parent
)
{
fetchSipAddresses
();
ContactsListModel
*
contacts
=
CoreManager
::
getInstance
()
->
getContactsListModel
();
QObject
::
connect
(
contacts
,
&
ContactsListModel
::
contactAdded
,
this
,
&
SipAddressesModel
::
updateFromNewContact
);
QObject
::
connect
(
CoreManager
::
getInstance
()
->
getContactsListModel
(),
&
ContactsListModel
::
contactAdded
,
this
,
&
SipAddressesModel
::
updateFromContact
contacts
,
&
ContactsListModel
::
contactRemoved
,
this
,
[
this
](
const
ContactModel
*
contact
)
{
for
(
const
auto
&
sip_address
:
contact
->
getVcardModel
()
->
getSipAddresses
())
tryToRemoveSipAddress
(
sip_address
.
toString
());
}
);
QObject
::
connect
(
CoreManager
::
getInstance
()
->
getContactsListModel
(),
&
ContactsListModel
::
contactRemoved
,
this
,
[
this
](
const
ContactModel
*
contact
)
{
for
(
const
auto
&
sip_address
:
contact
->
getVcardModel
()
->
getSipAddresses
())
{
auto
it
=
m_sip_addresses
.
find
(
sip_address
.
toString
());
if
(
it
==
m_sip_addresses
.
end
())
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove contact from sip address: `%1`."
).
arg
(
sip_address
.
toString
());
continue
;
}
if
(
it
->
remove
(
"contact"
)
==
0
)
qWarning
()
<<
QStringLiteral
(
"`contact` field is empty on sip address: `%1`."
).
arg
(
sip_address
.
toString
());
int
row
=
m_refs
.
indexOf
(
&
(
*
it
));
Q_ASSERT
(
row
!=
-
1
);
// History exists, signal changes.
if
(
it
->
contains
(
"timestamp"
))
{
emit
dataChanged
(
index
(
row
,
0
),
index
(
row
,
0
));
continue
;
}
// Remove sip address if no history.
removeRow
(
row
);
contacts
,
&
ContactsListModel
::
sipAddressAdded
,
this
,
[
this
](
ContactModel
*
contact
,
const
QString
&
sip_address
)
{
// TODO: Avoid the limitation of one contact by sip address.
ContactModel
*
mapped_contact
=
mapSipAddressToContact
(
sip_address
);
if
(
mapped_contact
)
{
qWarning
()
<<
"Unable to map sip address"
<<
sip_address
<<
"to"
<<
contact
<<
"- already used by"
<<
mapped_contact
;
return
;
}
updateFromNewContactSipAddress
(
contact
,
sip_address
);
}
);
QObject
::
connect
(
contacts
,
&
ContactsListModel
::
sipAddressRemoved
,
this
,
[
this
](
ContactModel
*
contact
,
const
QString
&
sip_address
)
{
ContactModel
*
mapped_contact
=
mapSipAddressToContact
(
sip_address
);
if
(
contact
!=
mapped_contact
)
{
qWarning
()
<<
"Unable to remove sip address"
<<
sip_address
<<
"of"
<<
contact
<<
"- already used by"
<<
mapped_contact
;
return
;
}
tryToRemoveSipAddress
(
sip_address
);
}
);
}
...
...
@@ -137,32 +139,65 @@ bool SipAddressesModel::removeRows (int row, int count, const QModelIndex &paren
return
true
;
}
void
SipAddressesModel
::
updateFromContact
(
ContactModel
*
contact
)
{
for
(
const
auto
&
sip_address
:
contact
->
getVcardModel
()
->
getSipAddresses
())
{
const
QString
&
sip_address_str
=
sip_address
.
toString
(
);
auto
it
=
m_sip_addresses
.
find
(
sip_address_str
);
void
SipAddressesModel
::
updateFrom
New
Contact
(
ContactModel
*
contact
)
{
for
(
const
auto
&
sip_address
:
contact
->
getVcardModel
()
->
getSipAddresses
())
updateFromNewContactSipAddress
(
contact
,
sip_address
.
toString
()
);
}
// New sip address from contact = new entry.
if
(
it
==
m_sip_addresses
.
end
())
{
QVariantMap
map
;
map
[
"sipAddress"
]
=
sip_address
;
map
[
"contact"
]
=
QVariant
::
fromValue
(
contact
);
void
SipAddressesModel
::
updateFromNewContactSipAddress
(
ContactModel
*
contact
,
const
QString
&
sip_address
)
{
auto
it
=
m_sip_addresses
.
find
(
sip_address
);
m_sip_addresses
[
sip_address_str
]
=
map
;
m_refs
<<
&
m_sip_addresses
[
sip_address_str
];
// New sip address.
if
(
it
==
m_sip_addresses
.
end
())
{
QVariantMap
map
;
map
[
"sipAddress"
]
=
sip_address
;
map
[
"contact"
]
=
QVariant
::
fromValue
(
contact
);
int
row
=
m_refs
.
count
()
-
1
;
emit
dataChanged
(
index
(
row
,
0
),
index
(
row
,
0
));
continue
;
}
int
row
=
m_refs
.
count
();
beginInsertRows
(
QModelIndex
(),
row
,
row
);
qInfo
()
<<
QStringLiteral
(
"Add sip address: `%1`."
).
arg
(
sip_address
);
m_sip_addresses
[
sip_address
]
=
map
;
m_refs
<<
&
m_sip_addresses
[
sip_address
];
endInsertRows
();
// Sip address exists, update contact.
(
*
it
)[
"contact"
]
=
QVariant
::
fromValue
(
contact
);
emit
dataChanged
(
index
(
row
,
0
),
index
(
row
,
0
));
return
;
}
// Sip address exists, update contact.
(
*
it
)[
"contact"
]
=
QVariant
::
fromValue
(
contact
);
int
row
=
m_refs
.
indexOf
(
&
(
*
it
));
Q_ASSERT
(
row
!=
-
1
);
int
row
=
m_refs
.
indexOf
(
&
(
*
it
));
Q_ASSERT
(
row
!=
-
1
);
emit
dataChanged
(
index
(
row
,
0
),
index
(
row
,
0
));
}
void
SipAddressesModel
::
tryToRemoveSipAddress
(
const
QString
&
sip_address
)
{
auto
it
=
m_sip_addresses
.
find
(
sip_address
);
if
(
it
==
m_sip_addresses
.
end
())
{
qWarning
()
<<
QStringLiteral
(
"Unable to remove unavailable sip address: `%1`."
).
arg
(
sip_address
);
return
;
}
if
(
it
->
remove
(
"contact"
)
==
0
)
qWarning
()
<<
QStringLiteral
(
"`contact` field is empty on sip address: `%1`."
).
arg
(
sip_address
);
int
row
=
m_refs
.
indexOf
(
&
(
*
it
));
Q_ASSERT
(
row
!=
-
1
);
// History exists, signal changes.
if
(
it
->
contains
(
"timestamp"
))
{
emit
dataChanged
(
index
(
row
,
0
),
index
(
row
,
0
));
return
;
}
// Remove sip address if no history.
removeRow
(
row
);
}
void
SipAddressesModel
::
fetchSipAddresses
()
{
...
...
@@ -213,5 +248,5 @@ void SipAddressesModel::fetchSipAddresses () {
// Get sip addresses from contacts.
for
(
auto
&
contact
:
CoreManager
::
getInstance
()
->
getContactsListModel
()
->
m_list
)
updateFromContact
(
contact
);
updateFrom
New
Contact
(
contact
);
}
tests/src/components/sip-addresses/SipAddressesModel.hpp
View file @
4ce00a71
...
...
@@ -27,7 +27,10 @@ private:
bool
removeRow
(
int
row
,
const
QModelIndex
&
parent
=
QModelIndex
());
bool
removeRows
(
int
row
,
int
count
,
const
QModelIndex
&
parent
=
QModelIndex
())
override
;
void
updateFromContact
(
ContactModel
*
contact
);
void
updateFromNewContact
(
ContactModel
*
contact
);
void
updateFromNewContactSipAddress
(
ContactModel
*
contact
,
const
QString
&
sip_address
);
void
tryToRemoveSipAddress
(
const
QString
&
sip_address
);
void
fetchSipAddresses
();
QHash
<
QString
,
QVariantMap
>
m_sip_addresses
;
...
...
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