Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Donald Haase
Cockatrice
Commits
d3114faf
Commit
d3114faf
authored
Nov 18, 2009
by
Max-Wilhelm Bruker
Browse files
decklist transfer code
parent
c5bf72b1
Changes
9
Hide whitespace changes
Inline
Side-by-side
cockatrice/cockatrice.pro
View file @
d3114faf
...
...
@@ -33,7 +33,6 @@ HEADERS += src/counter.h \
src
/
zoneviewlayout
.
h
\
src
/
carddatabasemodel
.
h
\
src
/
window_deckeditor
.
h
\
src
/
decklist
.
h
\
src
/
setsmodel
.
h
\
src
/
window_sets
.
h
\
src
/
abstractgraphicsitem
.
h
\
...
...
@@ -45,6 +44,7 @@ HEADERS += src/counter.h \
src
/
tab_chatchannel
.
h
\
src
/
tab_game
.
h
\
src
/
tab_supervisor
.
h
\
..
/
common
/
decklist
.
h
\
..
/
common
/
protocol
.
h
\
..
/
common
/
protocol_items
.
h
\
..
/
common
/
protocol_datastructures
.
h
...
...
@@ -76,7 +76,6 @@ SOURCES += src/counter.cpp \
src
/
zoneviewlayout
.
cpp
\
src
/
carddatabasemodel
.
cpp
\
src
/
window_deckeditor
.
cpp
\
src
/
decklist
.
cpp
\
src
/
setsmodel
.
cpp
\
src
/
window_sets
.
cpp
\
src
/
abstractgraphicsitem
.
cpp
\
...
...
@@ -88,6 +87,7 @@ SOURCES += src/counter.cpp \
src
/
tab_chatchannel
.
cpp
\
src
/
tab_game
.
cpp
\
src
/
tab_supervisor
.
cpp
\
..
/
common
/
decklist
.
cpp
\
..
/
common
/
protocol
.
cpp
\
..
/
common
/
protocol_items
.
cpp
...
...
common/decklist.cpp
View file @
d3114faf
...
...
@@ -4,8 +4,8 @@
#include
<QXmlStreamReader>
#include
<QXmlStreamWriter>
#include
<QVariant>
#include
<QDebug>
#include
"decklist.h"
#include
"carddatabase.h"
AbstractDecklistNode
::
AbstractDecklistNode
(
InnerDecklistNode
*
_parent
)
:
parent
(
_parent
),
currentItem
(
0
)
...
...
@@ -185,7 +185,7 @@ bool DeckList::readElement(QXmlStreamReader *xml)
if
(
currentZone
)
{
if
(
currentZone
->
readElement
(
xml
))
currentZone
=
0
;
return
tru
e
;
return
fals
e
;
}
if
(
xml
->
isEndElement
())
{
...
...
@@ -193,17 +193,17 @@ bool DeckList::readElement(QXmlStreamReader *xml)
name
=
currentElementText
;
else
if
(
xml
->
name
()
==
"comments"
)
comments
=
currentElementText
;
else
return
false
;
else
if
(
xml
->
name
()
==
"cockatrice_deck"
)
{
qDebug
()
<<
"deck finished!"
;
return
true
;
}
currentElementText
.
clear
();
}
else
if
(
xml
->
isStartElement
()
&&
(
xml
->
name
()
==
"zone"
))
currentZone
=
new
InnerDecklistNode
(
xml
->
attributes
().
value
(
"name"
).
toString
(),
root
);
else
if
(
xml
->
isCharacters
()
&&
!
xml
->
isWhitespace
())
currentElementText
=
xml
->
text
().
toString
();
else
return
false
;
return
tru
e
;
return
fals
e
;
}
void
DeckList
::
writeElement
(
QXmlStreamWriter
*
xml
)
...
...
@@ -219,25 +219,29 @@ void DeckList::writeElement(QXmlStreamWriter *xml)
xml
->
writeEndElement
();
// cockatrice_deck
}
bool
DeckList
::
loadFrom
File_Native
(
QIODevice
*
device
)
bool
DeckList
::
loadFrom
Xml
(
QXmlStreamReader
*
xml
)
{
QXmlStreamReader
xml
(
device
);
while
(
!
xml
.
atEnd
())
{
xml
.
readNext
();
if
(
xml
.
isStartElement
())
{
if
(
xml
.
name
()
!=
"cockatrice_deck"
)
while
(
!
xml
->
atEnd
())
{
xml
->
readNext
();
if
(
xml
->
isStartElement
())
{
if
(
xml
->
name
()
!=
"cockatrice_deck"
)
return
false
;
while
(
!
xml
.
atEnd
())
{
xml
.
readNext
();
if
(
!
readElement
(
&
xml
))
if
(
xml
.
isEndElement
()
&&
(
xml
.
name
()
==
"cockatrice_deck"
))
return
true
;
while
(
!
xml
->
atEnd
())
{
xml
->
readNext
();
if
(
readElement
(
xml
))
return
true
;
}
}
}
return
false
;
}
bool
DeckList
::
loadFromFile_Native
(
QIODevice
*
device
)
{
QXmlStreamReader
xml
(
device
);
return
loadFromXml
(
&
xml
);
}
bool
DeckList
::
saveToFile_Native
(
QIODevice
*
device
)
{
QXmlStreamWriter
xml
(
device
);
...
...
common/decklist.h
View file @
d3114faf
...
...
@@ -103,6 +103,7 @@ public:
bool
readElement
(
QXmlStreamReader
*
xml
);
void
writeElement
(
QXmlStreamWriter
*
xml
);
bool
loadFromXml
(
QXmlStreamReader
*
xml
);
bool
loadFromFile_Native
(
QIODevice
*
device
);
bool
saveToFile_Native
(
QIODevice
*
device
);
...
...
common/protocol.cpp
View file @
d3114faf
...
...
@@ -3,6 +3,7 @@
#include
<QDebug>
#include
"protocol.h"
#include
"protocol_items.h"
#include
"decklist.h"
QHash
<
QString
,
ProtocolItem
::
NewItemFunction
>
ProtocolItem
::
itemNameHash
;
...
...
@@ -69,6 +70,7 @@ void ProtocolItem::initializeHash()
itemNameHash
.
insert
(
"resp"
,
ProtocolResponse
::
newItem
);
ProtocolResponse
::
initializeHash
();
itemNameHash
.
insert
(
"respdeck_list"
,
Response_DeckList
::
newItem
);
itemNameHash
.
insert
(
"respdeck_download"
,
Response_DeckDownload
::
newItem
);
itemNameHash
.
insert
(
"generic_eventlist_games"
,
Event_ListGames
::
newItem
);
itemNameHash
.
insert
(
"generic_eventlist_chat_channels"
,
Event_ListChatChannels
::
newItem
);
...
...
@@ -99,6 +101,49 @@ void Command::processResponse(ProtocolResponse *response)
emit
finished
(
response
->
getResponseCode
());
}
Command_DeckUpload
::
Command_DeckUpload
(
int
_cmdId
,
DeckList
*
_deck
,
const
QString
&
_path
)
:
Command
(
"deck_upload"
,
_cmdId
),
deck
(
_deck
),
path
(
_path
),
readFinished
(
false
)
{
setParameter
(
"path"
,
path
);
}
Command_DeckUpload
::~
Command_DeckUpload
()
{
delete
deck
;
}
void
Command_DeckUpload
::
extractParameters
()
{
Command
::
extractParameters
();
path
=
parameters
[
"path"
];
}
bool
Command_DeckUpload
::
readElement
(
QXmlStreamReader
*
xml
)
{
if
(
readFinished
)
return
false
;
if
(
!
deck
)
{
if
(
xml
->
isStartElement
()
&&
(
xml
->
name
()
==
"cockatrice_deck"
))
{
deck
=
new
DeckList
;
return
true
;
}
return
false
;
}
if
(
deck
->
readElement
(
xml
))
readFinished
=
true
;
return
true
;
}
void
Command_DeckUpload
::
writeElement
(
QXmlStreamWriter
*
xml
)
{
if
(
deck
)
deck
->
writeElement
(
xml
);
}
QHash
<
QString
,
ResponseCode
>
ProtocolResponse
::
responseHash
;
ProtocolResponse
::
ProtocolResponse
(
int
_cmdId
,
ResponseCode
_responseCode
,
const
QString
&
_itemName
)
...
...
@@ -180,7 +225,7 @@ void Response_DeckList::Directory::writeElement(QXmlStreamWriter *xml)
}
Response_DeckList
::
Response_DeckList
(
int
_cmdId
,
ResponseCode
_responseCode
,
Directory
*
_root
)
:
ProtocolResponse
(
_cmdId
,
_responseCode
,
"deck_list"
),
root
(
_root
)
:
ProtocolResponse
(
_cmdId
,
_responseCode
,
"deck_list"
),
root
(
_root
)
,
readFinished
(
false
)
{
}
...
...
@@ -191,13 +236,20 @@ Response_DeckList::~Response_DeckList()
bool
Response_DeckList
::
readElement
(
QXmlStreamReader
*
xml
)
{
if
(
readFinished
)
return
false
;
if
(
!
root
)
{
if
(
xml
->
isStartElement
()
&&
(
xml
->
name
()
==
"directory"
))
if
(
xml
->
isStartElement
()
&&
(
xml
->
name
()
==
"directory"
))
{
root
=
new
Directory
;
return
true
;
}
return
false
;
}
return
!
root
->
readElement
(
xml
);
if
(
root
->
readElement
(
xml
))
readFinished
=
true
;
return
true
;
}
void
Response_DeckList
::
writeElement
(
QXmlStreamWriter
*
xml
)
...
...
@@ -205,6 +257,56 @@ void Response_DeckList::writeElement(QXmlStreamWriter *xml)
root
->
writeElement
(
xml
);
}
Response_DeckDownload
::
Response_DeckDownload
(
int
_cmdId
,
ResponseCode
_responseCode
,
DeckList
*
_deck
)
:
ProtocolResponse
(
_cmdId
,
_responseCode
,
"deck_download"
),
deck
(
_deck
),
readFinished
(
false
)
{
}
Response_DeckDownload
::~
Response_DeckDownload
()
{
delete
deck
;
}
bool
Response_DeckDownload
::
readElement
(
QXmlStreamReader
*
xml
)
{
if
(
readFinished
)
return
false
;
if
(
!
deck
)
{
if
(
xml
->
isStartElement
()
&&
(
xml
->
name
()
==
"cockatrice_deck"
))
{
deck
=
new
DeckList
;
return
true
;
}
return
false
;
}
if
(
deck
->
readElement
(
xml
))
readFinished
=
true
;
return
true
;
}
void
Response_DeckDownload
::
writeElement
(
QXmlStreamWriter
*
xml
)
{
if
(
deck
)
deck
->
writeElement
(
xml
);
}
Response_DeckUpload
::
Response_DeckUpload
(
int
_cmdId
,
ResponseCode
_responseCode
,
int
_deckId
)
:
ProtocolResponse
(
_cmdId
,
_responseCode
,
"deck_upload"
),
deckId
(
_deckId
)
{
setParameter
(
"deck_id"
,
deckId
);
}
void
Response_DeckUpload
::
extractParameters
()
{
ProtocolResponse
::
extractParameters
();
bool
ok
;
deckId
=
parameters
[
"deck_id"
].
toInt
(
&
ok
);
if
(
!
ok
)
deckId
=
-
1
;
}
GenericEvent
::
GenericEvent
(
const
QString
&
_eventName
)
:
ProtocolItem
(
_eventName
)
{
...
...
common/protocol.h
View file @
d3114faf
...
...
@@ -14,13 +14,16 @@ class QXmlStreamWriter;
class
QXmlStreamAttributes
;
class
ProtocolResponse
;
class
DeckList
;
enum
ItemId
{
ItemId_Command_DeckUpload
=
ItemId_Other
+
1
,
ItemId_Event_ListChatChannels
=
ItemId_Other
+
2
,
ItemId_Event_ChatListPlayers
=
ItemId_Other
+
3
,
ItemId_Event_ListGames
=
ItemId_Other
+
4
,
ItemId_Response_DeckList
=
ItemId_Other
+
5
ItemId_Response_DeckList
=
ItemId_Other
+
5
,
ItemId_Response_DeckDownload
=
ItemId_Other
+
6
,
ItemId_Response_DeckUpload
=
ItemId_Other
+
7
};
class
ProtocolItem
:
public
QObject
{
...
...
@@ -123,10 +126,21 @@ public:
class
Command_DeckUpload
:
public
Command
{
Q_OBJECT
private:
DeckList
*
deck
;
QString
path
;
bool
readFinished
;
protected:
void
extractParameters
();
bool
readElement
(
QXmlStreamReader
*
xml
);
void
writeElement
(
QXmlStreamWriter
*
xml
);
public:
Command_DeckUpload
(
int
_cmdId
=
-
1
)
:
Command
(
"deck_upload"
,
_cmdId
)
{
}
Command_DeckUpload
(
int
_cmdId
=
-
1
,
DeckList
*
_deck
=
0
,
const
QString
&
_path
=
QString
());
~
Command_DeckUpload
();
static
ProtocolItem
*
newItem
()
{
return
new
Command_DeckUpload
;
}
int
getItemId
()
const
{
return
ItemId_Command_DeckUpload
;
}
DeckList
*
getDeck
()
const
{
return
deck
;
}
QString
getPath
()
const
{
return
path
;
}
};
// -----------------
...
...
@@ -182,6 +196,7 @@ public:
};
private:
Directory
*
root
;
bool
readFinished
;
protected:
bool
readElement
(
QXmlStreamReader
*
xml
);
void
writeElement
(
QXmlStreamWriter
*
xml
);
...
...
@@ -193,6 +208,35 @@ public:
Directory
*
getRoot
()
const
{
return
root
;
}
};
class
Response_DeckDownload
:
public
ProtocolResponse
{
Q_OBJECT
private:
DeckList
*
deck
;
bool
readFinished
;
protected:
bool
readElement
(
QXmlStreamReader
*
xml
);
void
writeElement
(
QXmlStreamWriter
*
xml
);
public:
Response_DeckDownload
(
int
_cmdId
=
-
1
,
ResponseCode
_responseCode
=
RespOk
,
DeckList
*
_deck
=
0
);
~
Response_DeckDownload
();
int
getItemId
()
const
{
return
ItemId_Response_DeckDownload
;
}
static
ProtocolItem
*
newItem
()
{
return
new
Response_DeckDownload
;
}
DeckList
*
getDeck
()
const
{
return
deck
;
}
};
class
Response_DeckUpload
:
public
ProtocolResponse
{
Q_OBJECT
private:
int
deckId
;
protected:
void
extractParameters
();
public:
Response_DeckUpload
(
int
_cmdId
=
-
1
,
ResponseCode
_responseCode
=
RespOk
,
int
_deckId
=
-
1
);
int
getItemId
()
const
{
return
ItemId_Response_DeckUpload
;
}
static
ProtocolItem
*
newItem
()
{
return
new
Response_DeckUpload
;
}
int
getDeckId
()
const
{
return
deckId
;
}
};
// --------------
// --- EVENTS ---
// --------------
...
...
common/protocol_datastructures.h
View file @
d3114faf
...
...
@@ -4,7 +4,7 @@
#include
<QString>
#include
<QColor>
enum
ResponseCode
{
RespNothing
,
RespOk
,
RespInvalidCommand
,
RespNameNotFound
,
RespLoginNeeded
,
RespContextError
,
RespWrongPassword
,
RespSpectatorsNotAllowed
};
enum
ResponseCode
{
RespNothing
,
RespOk
,
RespInvalidCommand
,
RespInvalidData
,
RespNameNotFound
,
RespLoginNeeded
,
RespContextError
,
RespWrongPassword
,
RespSpectatorsNotAllowed
};
class
ServerChatChannelInfo
{
private:
...
...
servatrice/servatrice.pro
View file @
d3114faf
...
...
@@ -14,6 +14,7 @@ QT += network sql
HEADERS
+=
src
/
servatrice
.
h
\
src
/
serversocketinterface
.
h
\
..
/
common
/
decklist
.
h
\
..
/
common
/
protocol
.
h
\
..
/
common
/
protocol_items
.
h
\
..
/
common
/
protocol_datastructures
.
h
\
...
...
@@ -32,6 +33,7 @@ HEADERS += src/servatrice.h \
SOURCES
+=
src
/
main
.
cpp
\
src
/
servatrice
.
cpp
\
src
/
serversocketinterface
.
cpp
\
..
/
common
/
decklist
.
cpp
\
..
/
common
/
protocol
.
cpp
\
..
/
common
/
protocol_items
.
cpp
\
..
/
common
/
rng_abstract
.
cpp
\
...
...
servatrice/src/serversocketinterface.cpp
View file @
d3114faf
...
...
@@ -25,6 +25,7 @@
#include
"servatrice.h"
#include
"protocol.h"
#include
"protocol_items.h"
#include
"decklist.h"
ServerSocketInterface
::
ServerSocketInterface
(
Servatrice
*
_server
,
QTcpSocket
*
_socket
,
QObject
*
parent
)
:
Server_ProtocolHandler
(
_server
,
parent
),
servatrice
(
_server
),
socket
(
_socket
),
currentItem
(
0
)
...
...
@@ -113,8 +114,6 @@ int ServerSocketInterface::getDeckPathId(int basePathId, QStringList path)
if
(
path
[
0
].
isEmpty
())
return
0
;
servatrice
->
checkSql
();
QSqlQuery
query
;
query
.
prepare
(
"select id from decklist_folders where id_parent = :id_parent and name = :name and user = :user"
);
query
.
bindValue
(
":id_parent"
,
basePathId
);
...
...
@@ -136,28 +135,33 @@ int ServerSocketInterface::getDeckPathId(const QString &path)
return
getDeckPathId
(
0
,
path
.
split
(
"/"
));
}
void
ServerSocketInterface
::
deckListHelper
(
Response_DeckList
::
Directory
*
folder
)
bool
ServerSocketInterface
::
deckListHelper
(
Response_DeckList
::
Directory
*
folder
)
{
QSqlQuery
query
;
query
.
prepare
(
"select id, name from decklist_folders where id_parent = :id_parent and user = :user"
);
query
.
bindValue
(
":id_parent"
,
folder
->
getId
());
query
.
bindValue
(
":user"
,
playerName
);
servatrice
->
execSqlQuery
(
query
);
if
(
!
servatrice
->
execSqlQuery
(
query
))
return
false
;
while
(
query
.
next
())
{
Response_DeckList
::
Directory
*
newFolder
=
new
Response_DeckList
::
Directory
(
query
.
value
(
1
).
toString
(),
query
.
value
(
0
).
toInt
());
folder
->
append
(
newFolder
);
deckListHelper
(
newFolder
);
if
(
!
deckListHelper
(
newFolder
))
return
false
;
}
query
.
prepare
(
"select id, name from decklist_files where id_folder = :id_folder"
);
query
.
bindValue
(
":id_folder"
,
folder
->
getId
());
servatrice
->
execSqlQuery
(
query
);
if
(
!
servatrice
->
execSqlQuery
(
query
))
return
false
;
while
(
query
.
next
())
{
Response_DeckList
::
File
*
newFile
=
new
Response_DeckList
::
File
(
query
.
value
(
1
).
toString
(),
query
.
value
(
0
).
toInt
());
folder
->
append
(
newFile
);
}
return
true
;
}
// CHECK AUTHENTICATION!
...
...
@@ -165,11 +169,12 @@ void ServerSocketInterface::deckListHelper(Response_DeckList::Directory *folder)
ResponseCode
ServerSocketInterface
::
cmdDeckList
(
Command_DeckList
*
cmd
)
{
Response_DeckList
::
Directory
*
root
=
new
Response_DeckList
::
Directory
(
QString
());
servatrice
->
checkSql
();
Response_DeckList
::
Directory
*
root
=
new
Response_DeckList
::
Directory
(
QString
());
QSqlQuery
query
;
deckListHelper
(
root
);
if
(
!
deckListHelper
(
root
))
return
RespContextError
;
sendProtocolItem
(
new
Response_DeckList
(
cmd
->
getCmdId
(),
RespOk
,
root
));
...
...
@@ -178,6 +183,8 @@ ResponseCode ServerSocketInterface::cmdDeckList(Command_DeckList *cmd)
ResponseCode
ServerSocketInterface
::
cmdDeckNewDir
(
Command_DeckNewDir
*
cmd
)
{
servatrice
->
checkSql
();
int
folderId
=
getDeckPathId
(
cmd
->
getPath
());
if
(
folderId
==
-
1
)
return
RespNameNotFound
;
...
...
@@ -194,6 +201,8 @@ ResponseCode ServerSocketInterface::cmdDeckNewDir(Command_DeckNewDir *cmd)
void
ServerSocketInterface
::
deckDelDirHelper
(
int
basePathId
)
{
servatrice
->
checkSql
();
QSqlQuery
query
;
query
.
prepare
(
"select id from decklist_folders where id_parent = :id_parent"
);
...
...
@@ -213,14 +222,19 @@ void ServerSocketInterface::deckDelDirHelper(int basePathId)
ResponseCode
ServerSocketInterface
::
cmdDeckDelDir
(
Command_DeckDelDir
*
cmd
)
{
servatrice
->
checkSql
();
int
basePathId
=
getDeckPathId
(
cmd
->
getPath
());
if
(
basePathId
==
-
1
)
return
RespNameNotFound
;
deckDelDirHelper
(
basePathId
);
return
RespOk
;
}
ResponseCode
ServerSocketInterface
::
cmdDeckDel
(
Command_DeckDel
*
cmd
)
{
servatrice
->
checkSql
();
QSqlQuery
query
;
query
.
prepare
(
"select id from decklist_files where id = :id and user = :user"
);
...
...
@@ -239,8 +253,53 @@ ResponseCode ServerSocketInterface::cmdDeckDel(Command_DeckDel *cmd)
ResponseCode
ServerSocketInterface
::
cmdDeckUpload
(
Command_DeckUpload
*
cmd
)
{
servatrice
->
checkSql
();
if
(
!
cmd
->
getDeck
())
return
RespInvalidData
;
int
folderId
=
getDeckPathId
(
cmd
->
getPath
());
if
(
folderId
==
-
1
)
return
RespNameNotFound
;
QString
deckContents
;
QXmlStreamWriter
deckWriter
(
&
deckContents
);
deckWriter
.
writeStartDocument
();
cmd
->
getDeck
()
->
writeElement
(
&
deckWriter
);
deckWriter
.
writeEndDocument
();
QSqlQuery
query
;
query
.
prepare
(
"insert into decklist_files (id_folder, user, name, content) value(:id_folder, :user, :name, :content)"
);
query
.
bindValue
(
":id_folder"
,
folderId
);
query
.
bindValue
(
":user"
,
playerName
);
query
.
bindValue
(
":name"
,
cmd
->
getDeck
()
->
getName
());
query
.
bindValue
(
":content"
,
deckContents
);
servatrice
->
execSqlQuery
(
query
);
sendProtocolItem
(
new
Response_DeckUpload
(
cmd
->
getCmdId
(),
RespOk
,
query
.
lastInsertId
().
toInt
()));
return
RespNothing
;
}
ResponseCode
ServerSocketInterface
::
cmdDeckDownload
(
Command_DeckDownload
*
cmd
)
{
servatrice
->
checkSql
();
QSqlQuery
query
;
query
.
prepare
(
"select content from decklist_files where id = :id and user = :user"
);
query
.
bindValue
(
":id"
,
cmd
->
getDeckId
());
query
.
bindValue
(
":user"
,
playerName
);
servatrice
->
execSqlQuery
(
query
);
if
(
!
query
.
next
())
return
RespNameNotFound
;
QXmlStreamReader
deckReader
(
query
.
value
(
0
).
toString
());
DeckList
*
deck
=
new
DeckList
;
if
(
!
deck
->
loadFromXml
(
&
deckReader
))
{
delete
deck
;
deck
=
0
;
return
RespInvalidData
;
}
sendProtocolItem
(
new
Response_DeckDownload
(
cmd
->
getCmdId
(),
RespOk
,
deck
));
return
RespNothing
;
}
servatrice/src/serversocketinterface.h
View file @
d3114faf
...
...
@@ -43,7 +43,7 @@ private:
int
getDeckPathId
(
int
basePathId
,
QStringList
path
);
int
getDeckPathId
(
const
QString
&
path
);
void
deckListHelper
(
Response_DeckList
::
Directory
*
folder
);
bool
deckListHelper
(
Response_DeckList
::
Directory
*
folder
);
ResponseCode
cmdDeckList
(
Command_DeckList
*
cmd
);
ResponseCode
cmdDeckNewDir
(
Command_DeckNewDir
*
cmd
);
void
deckDelDirHelper
(
int
basePathId
);
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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