Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
J
jio
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
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Aurel
jio
Commits
379f636d
Commit
379f636d
authored
May 06, 2013
by
Jonathan Rivalan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Modified the connector with jslint feedbacks
parent
9753e423
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
323 additions
and
48 deletions
+323
-48
index.html
index.html
+261
-0
src/jio.storage/s3storage.js
src/jio.storage/s3storage.js
+62
-48
No files found.
index.html
0 → 100644
View file @
379f636d
<html>
<head>
<title></title>
<script
type=
"text/javascript"
src=
"lib/md5/md5.js"
></script>
<script
type=
"text/javascript"
src=
"jio.js"
></script>
<script
type=
"text/javascript"
src=
"lib/jquery/jquery.min.js"
></script>
<script
type=
"text/javascript"
src=
"lib/jsSha1/sha1.js"
></script>
<script
type=
"text/javascript"
src=
"src/jio.storage/s3storage.js"
></script>
</head>
<body>
<div
class=
"put form ajaxWITH"
>
<h2>
JIO-S3 PUT form
</h2>
<form
id=
"s3AJAXUP"
action=
"https://s3.amazonaws.com/jiobucket/"
method=
"post"
enctype=
"multipart/form-data"
>
<div
class=
"text_holder"
>
<p>
Give a name to your attachment :
</p>
<input
type=
"text"
>
<p>
Below text will be uploaded as an attachment to my_2nd_document :
</p>
<textarea
name=
"text_area"
>
Contenu de l'attachement
</textarea>
</div>
<div
class=
"submit"
>
Put the attachment
</div>
</form>
</div>
<div
class=
"ALLDOCS"
>
<h2>
All docs
</h2>
<div
class=
"button"
>
Get all the docs
</div>
<div
class=
"content"
></div>
</div>
<style
type=
"text/css"
media=
"screen"
>
.button
{
cursor
:
pointer
;
margin
:
4px
;}
.text-holder
{
width
:
100%
;
clear
:
both
;
float
:
left
;
width
:
140px
;}
.text-holder
>
input
{
float
:
left
}
.text-holder
>
textarea
{
clear
:
both
;
float
:
left
;
margin
:
10px
;
font-size
:
10px
;}
div
.submit
,
.ALLDOCS
.button
{
background
:
silver
;
border-radius
:
3px
;
color
:
black
;
width
:
150px
;
height
:
auto
;
box-shadow
:
0px
0px
1px
1px
black
;
cursor
:
pointer
;
margin
:
4px
;
padding
:
2px
;}
</style>
<script>
var
alldocsDATA
=
''
;
$
(
document
).
ready
(
function
(){
//mise en cache des éléments DOM
var
alldocsAREA
=
$
(
'
.ALLDOCS
'
);
var
alldocsButton
=
alldocsAREA
.
find
(
'
.button
'
);
var
alldocsContent
=
alldocsAREA
.
find
(
'
.content
'
);
var
metaAREA
=
$
(
'
.META
'
);
var
metaContent
=
metaAREA
.
find
(
'
.content
'
);
var
outputAREA
=
$
(
'
.OUTPUT
'
);
var
outputContent
=
outputAREA
.
find
(
'
.content
'
);
//mise en cache des éléments DOM
var
jioform
=
$
(
'
#s3AJAXUP
'
);
var
submitButton
=
$
(
'
div.submit
'
);
var
textTitle
=
jioform
.
find
(
'
input[type="text"]
'
).
val
();
var
textContent
=
jioform
.
find
(
'
textarea
'
).
val
();
//Initiation JIO
var
jio_instance
=
jIO
.
newJio
({
"
type
"
:
"
s3
"
,
"
AWSIdentifier
"
:
"
AKIAJLNYGVLTV66RHPEQ
"
,
"
password
"
:
"
/YHoa5r2X6EUHfvP31jdYx6t75h81pAjIZ4Mt94y
"
,
"
server
"
:
"
jiobucket
"
,
"
url
"
:
"
https://jiobucket.s3.amazonaws.com
"
});
//jio_instance.get({"_id": "my_1st_document", "title": "myDoc1"}, function(err, response){
//console.log("GET response :"+ response);
//});
//
//jio_instance.getAttachment({"_id": "my_1st_document","_attachment": "1er_attachment"}, function(err, response){
//console.log("GET Attachment response :"+ response);
//});
//
//jio_instance.put({"_id": "my_6th_document", "title": "myDoc6"}, function(err, response){
//console.log('PUT response : '+JSON.stringify(response));
//});
//
//
jio_instance
.
post
({
"
_id
"
:
'
documentONE
'
,
"
title
"
:
"
doc1
"
},
function
(
err
,
response
){
console
.
log
(
'
POST response :
'
+
JSON
.
stringify
(
response
));
//removeWrapper();
});
jio_instance
.
putAttachment
({
"
_id
"
:
"
documentONE
"
,
"
_attachment
"
:
'
1st_Attachment_manual
'
,
"
_data
"
:
"
Ceci est le contenu de l'attachment
"
,
"
_mimetype
"
:
"
text/plain
"
},
function
(
err
,
response
){
console
.
log
(
"
PUT Attachment response :
"
+
JSON
.
stringify
(
response
));
//removeAttachmentWrapper();
});
//
//function removeWrapper(){
//jio_instance.remove({"_id":"my_5th_document","title":"myDoc5"}, function(err,response){
//console.log('REMOVE response : '+ response);
//});
//};
//
//function removeAttachmentWrapper(){
//jio_instance.removeAttachment({
//"_id": "my_1st_document",
//"_attachment": '99rd_Attachment_manual'
//}, function(err,response){
//console.log(response);
//});
//};
//jio_instance.remove({"_id":"documentONE","title":"doc1"}, function(err,response){
//console.log('REMOVE response : '+ response);
//});
//Sera utile plus tard
jioform
.
on
(
'
change
'
,
function
(){
textTitle
=
jioform
.
find
(
'
input[type="text"]
'
).
val
();
textContent
=
jioform
.
find
(
'
textarea
'
).
val
();
});
//Ajout de l'attachment
submitButton
.
on
(
'
click
'
,
function
(){
//console.log("putAttachment");
if
(
textTitle
==
''
){
alert
(
"
Veuillez donner un titre pour l'attachment.
"
);
}
else
{
jio_instance
.
putAttachment
({
"
_id
"
:
"
documentONE
"
,
"
_attachment
"
:
textTitle
,
"
_data
"
:
textContent
,
"
_mimetype
"
:
"
text/plain
"
},
function
(
err
,
response
)
{
console
.
log
(
'
Put Attachment response :
'
+
JSON
.
stringify
(
response
));
});
}
});
alldocsButton
.
on
(
'
click
'
,
function
(){
//console.log("alldocs");
jio_instance
.
allDocs
({
"
include_docs
"
:
true
},
function
(
err
,
response
){
console
.
log
(
JSON
.
stringify
(
response
));
console
.
log
(
response
);
var
doc_keys
=
$
(
response
.
rows
);
//console.log(doc_keys.length);
doc_keys
.
each
(
function
(
index
){
var
that
=
$
(
this
);
var
filename
=
that
[
0
][
"
id
"
];
//console.log(filename);
var
DOCAttachment
=
that
[
0
][
"
doc
"
][
"
_attachments
"
];
//console.log(DOCAttachment);
if
(
DOCAttachment
==
undefined
){
//n'a pas d'attachment
alldocsContent
.
append
(
'
<a alt="click to remove" title="click to remove" href="#" rel="
'
+
filename
+
'
">
'
+
filename
+
'
</a><br>
'
);
}
else
{
for
(
property
in
that
[
0
][
"
doc
"
])
{
//console.log( property ); // Outputs: foo, fiz or fiz, foo
}
var
names
=
''
;
names
+=
'
<a alt="click to remove" title="click to remove" href="#" rel="
'
+
filename
+
'
">
'
+
filename
+
'
</a><br>
'
;
for
(
var
key
in
DOCAttachment
)
{
if
(
DOCAttachment
.
hasOwnProperty
(
key
))
{
//names += '
<
a
alt
=
"
click to remove
"
title
=
"
click to remove
"
type
=
"
attachment
"
href
=
"
#
"
rel
=
"
'+filename+'/'+key+'
"
>
'
+filename+
'
/
'
+key+
'
<
/a><br>'
;
}
}
// console.log(names.join(', ')); //a, b
//console.log(names);
alldocsContent
.
append
(
names
);
//console.log(DOCAttachment);
}
//alldocsContent.append('
<
a
alt
=
"
click to remove
"
title
=
"
click to remove
"
href
=
"
#
"
rel
=
"
'+filename+'
"
>
'
+filename+
'
<
/a><br>'
)
;
});
alldocsContent
.
html
(
JSON
.
stringify
(
response
));
//addRemove();
});
});
function
addRemove
(){
var
linksToRemove
=
alldocsContent
.
find
(
'
a
'
);
//console.log(linksToRemove);
linksToRemove
.
on
(
'
click
'
,
function
(){
var
that
=
$
(
this
);
console
.
log
(
that
.
attr
(
'
type
'
));
if
(
that
.
attr
(
'
type
'
)
!==
'
attachment
'
){
var
docID
=
that
.
text
();
jio_instance
.
remove
({
"
_id
"
:
docID
},
function
(
err
,
response
){
console
.
log
(
err
);
console
.
log
(
response
);
});
}
else
{
var
docID
=
that
.
text
();
var
splitDoc
=
docID
.
split
(
'
/
'
);
jio_instance
.
removeAttachment
({
"
_id
"
:
splitDoc
[
0
],
"
_attachment
"
:
splitDoc
[
1
]},
function
(
err
,
response
){
console
.
log
(
err
);
console
.
log
(
response
);
});
}
})
};
//function addRemove(){
//var linksToRemove = alldocsContent.find('a');
////console.log(linksToRemove);
//linksToRemove.on('click',function(){
//var that = $(this);
//console.log(that.attr('type'));
//if (that.attr('type') == 'attachment'){
//var docID = that.text();
//jio_instance.remove({"_id": docID},function(){});
//}
//else {
//var docID = that.text();
//jio_instance.removeAttachment({"_id": "my_document", "_attachment": "its_attachment"},function(){});
//}
//
//})
//};
/*fin document ready*/
});
</script>
</body>
</html>
src/jio.storage/s3storage.js
View file @
379f636d
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
"
use strict
"
;
/*jslint indent: 2, maxlen: 80, nomen: true */
/*global jIO: true, btoa: true, b64_hmac_sha1: true */
/*global jIO: true, btoa: true, b64_hmac_sha1: true */
/**
/**
* JIO S3 Storage. Type = "s3".
* JIO S3 Storage. Type = "s3".
...
@@ -286,7 +287,13 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -286,7 +287,13 @@ jIO.addStorageType("s3", function (spec, my) {
return
Signature
;
return
Signature
;
};
};
function
xhr_onreadystatechange
(
docId
,
command
,
obj
,
http
,
jio
,
isAttachment
,
callback
)
{
function
xhr_onreadystatechange
(
docId
,
command
,
obj
,
http
,
jio
,
isAttachment
,
callback
)
{
obj
.
onreadystatechange
=
function
()
{
obj
.
onreadystatechange
=
function
()
{
if
(
obj
.
readyState
===
4
)
{
if
(
obj
.
readyState
===
4
)
{
if
(
this
.
status
===
204
||
this
.
status
===
201
||
this
.
status
===
200
)
{
if
(
this
.
status
===
204
||
this
.
status
===
201
||
this
.
status
===
200
)
{
...
@@ -315,7 +322,7 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -315,7 +322,7 @@ jIO.addStorageType("s3", function (spec, my) {
delete
response
.
_attachments
;
delete
response
.
_attachments
;
that
.
success
(
JSON
.
stringify
(
response
));
that
.
success
(
JSON
.
stringify
(
response
));
}
else
{
}
else
{
if
(
isAttachment
===
true
){
if
(
isAttachment
===
true
)
{
that
.
success
(
this
.
responseText
);
that
.
success
(
this
.
responseText
);
}
else
{
}
else
{
that
.
success
(
JSON
.
parse
(
this
.
responseText
));
that
.
success
(
JSON
.
parse
(
this
.
responseText
));
...
@@ -327,7 +334,7 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -327,7 +334,7 @@ jIO.addStorageType("s3", function (spec, my) {
break
;
break
;
case
'
DELETE
'
:
case
'
DELETE
'
:
if
(
jio
===
true
)
{
if
(
jio
===
true
)
{
if
(
isAttachment
===
false
){
if
(
isAttachment
===
false
)
{
that
.
success
({
that
.
success
({
ok
:
true
,
ok
:
true
,
id
:
command
.
getDocId
()
id
:
command
.
getDocId
()
...
@@ -404,7 +411,8 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -404,7 +411,8 @@ jIO.addStorageType("s3", function (spec, my) {
doc
=
JSON
.
stringify
(
doc
);
doc
=
JSON
.
stringify
(
doc
);
break
;
break
;
case
"
remove
"
:
case
"
remove
"
:
if
(
typeof
doc
.
_attachments
!==
'
undefined
'
)
{
//if (typeof doc._attachments !== 'undefined') {
if
(
doc
.
_attachments
!==
'
undefined
'
)
{
delete
doc
.
_attachments
[
attachid
];
delete
doc
.
_attachments
[
attachid
];
}
}
doc
=
JSON
.
stringify
(
doc
);
doc
=
JSON
.
stringify
(
doc
);
...
@@ -417,7 +425,6 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -417,7 +425,6 @@ jIO.addStorageType("s3", function (spec, my) {
doc
=
JSON
.
stringify
(
doc
);
doc
=
JSON
.
stringify
(
doc
);
break
;
break
;
}
}
return
doc
;
return
doc
;
};
};
...
@@ -471,7 +478,8 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -471,7 +478,8 @@ jIO.addStorageType("s3", function (spec, my) {
is_attachment
,
is_attachment
,
callback
)
{
callback
)
{
var
docFile
=
priv
.
secureName
(
priv
.
idsToFileName
(
docId
,
attachId
||
undefined
));
var
docFile
=
priv
.
secureName
(
priv
.
idsToFileName
(
docId
,
attachId
||
undefined
));
var
requestUTC
=
new
Date
().
toUTCString
();
var
requestUTC
=
new
Date
().
toUTCString
();
...
@@ -488,7 +496,7 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -488,7 +496,7 @@ jIO.addStorageType("s3", function (spec, my) {
var
Signature
=
b64_hmac_sha1
(
priv
.
password
,
StringToSign
);
var
Signature
=
b64_hmac_sha1
(
priv
.
password
,
StringToSign
);
var
xhr
;
var
xhr
,
XMLHttpRequest
;
xhr
=
new
XMLHttpRequest
();
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
http
,
url
,
true
);
xhr
.
open
(
http
,
url
,
true
);
...
@@ -501,7 +509,13 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -501,7 +509,13 @@ jIO.addStorageType("s3", function (spec, my) {
xhr
.
setRequestHeader
(
"
Content-Type
"
,
mime
);
xhr
.
setRequestHeader
(
"
Content-Type
"
,
mime
);
xhr
.
responseType
=
'
text
'
;
xhr
.
responseType
=
'
text
'
;
xhr_onreadystatechange
(
docId
,
command
,
xhr
,
http
,
jio
,
is_attachment
,
callback
);
xhr_onreadystatechange
(
docId
,
command
,
xhr
,
http
,
jio
,
is_attachment
,
callback
);
if
(
http
===
'
PUT
'
)
{
if
(
http
===
'
PUT
'
)
{
xhr
.
send
(
data
);
xhr
.
send
(
data
);
...
@@ -531,7 +545,8 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -531,7 +545,8 @@ jIO.addStorageType("s3", function (spec, my) {
//conflicts due to the multipart enctype
//conflicts due to the multipart enctype
doc
=
JSON
.
stringify
(
doc
);
doc
=
JSON
.
stringify
(
doc
);
var
http_response
=
''
;
var
http_response
=
''
;
var
fd
=
new
FormData
();
var
fd
,
FormData
;
fd
=
new
FormData
();
//virtually builds the form fields
//virtually builds the form fields
//filename
//filename
fd
.
append
(
'
key
'
,
doc_id
);
fd
.
append
(
'
key
'
,
doc_id
);
...
@@ -557,7 +572,8 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -557,7 +572,8 @@ jIO.addStorageType("s3", function (spec, my) {
fd
.
append
(
'
signature
'
,
Signature
);
fd
.
append
(
'
signature
'
,
Signature
);
//uploaded content !!may must be a string rather than an object
//uploaded content !!may must be a string rather than an object
fd
.
append
(
'
file
'
,
doc
);
fd
.
append
(
'
file
'
,
doc
);
var
xhr
=
new
XMLHttpRequest
();
var
xhr
,
XMLHttpRequest
;
xhr
=
new
XMLHttpRequest
();
xhr_onreadystatechange
(
doc_id
,
command
,
xhr
,
'
POST
'
,
true
,
false
,
''
);
xhr_onreadystatechange
(
doc_id
,
command
,
xhr
,
'
POST
'
,
true
,
false
,
''
);
xhr
.
open
(
'
POST
'
,
'
https://
'
+
priv
.
server
+
'
.s3.amazonaws.com/
'
,
true
);
xhr
.
open
(
'
POST
'
,
'
https://
'
+
priv
.
server
+
'
.s3.amazonaws.com/
'
,
true
);
xhr
.
send
(
fd
);
xhr
.
send
(
fd
);
...
@@ -573,10 +589,9 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -573,10 +589,9 @@ jIO.addStorageType("s3", function (spec, my) {
function
(
response
)
{
function
(
response
)
{
if
(
response
===
'
404
'
)
{
if
(
response
===
'
404
'
)
{
postDocument
();
postDocument
();
}
}
else
{
//si ce n'est pas une 404,
//si ce n'est pas une 404,
//alors on renvoit une erreur 405
//alors on renvoit une erreur 405
else
{
return
that
.
error
(
priv
.
createError
(
return
that
.
error
(
priv
.
createError
(
409
,
409
,
"
Cannot create document
"
,
"
Cannot create document
"
,
...
@@ -692,18 +707,16 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -692,18 +707,16 @@ jIO.addStorageType("s3", function (spec, my) {
//XHRwrapper(command,'PUT','text/plain; charset=UTF-8',true);
//XHRwrapper(command,'PUT','text/plain; charset=UTF-8',true);
XHRwrapper
(
command
,
docId
,
''
,
'
GET
'
,
mime
,
''
,
false
,
false
,
XHRwrapper
(
command
,
docId
,
''
,
'
GET
'
,
mime
,
''
,
false
,
false
,
function
(
reponse
)
{
function
(
reponse
)
{
if
(
reponse
===
'
404
'
){
if
(
reponse
===
'
404
'
)
{
return
that
.
error
(
priv
.
createError
(
return
that
.
error
(
priv
.
createError
(
404
,
404
,
"
Cannot find document
"
,
"
Cannot find document
"
,
"
Document does not exist
"
"
Document does not exist
"
));
));
}
}
else
{
mon_document
=
reponse
;
mon_document
=
reponse
;
putDocument
();
putDocument
();
}
}
}
);
);
}
}
getDocument
();
getDocument
();
...
@@ -719,14 +732,27 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -719,14 +732,27 @@ jIO.addStorageType("s3", function (spec, my) {
var
docId
=
command
.
getDocId
();
var
docId
=
command
.
getDocId
();
var
mime
=
'
text/plain; charset=UTF-8
'
;
var
mime
=
'
text/plain; charset=UTF-8
'
;
function
deleteDocument
()
{
XHRwrapper
(
command
,
docId
,
''
,
'
DELETE
'
,
mime
,
''
,
true
,
false
,
function
(
reponse
)
{
that
.
success
({
// response
"
ok
"
:
true
,
"
id
"
:
docId
//"rev": current_revision
});
}
);
}
XHRwrapper
(
command
,
docId
,
''
,
'
GET
'
,
mime
,
''
,
false
,
false
,
XHRwrapper
(
command
,
docId
,
''
,
'
GET
'
,
mime
,
''
,
false
,
false
,
function
(
response
)
{
function
(
response
)
{
console
.
log
(
response
);
console
.
log
(
response
);
var
attachKeys
=
(
JSON
.
parse
(
response
))
[
'
_attachments
'
]
;
var
attachKeys
=
(
JSON
.
parse
(
response
))
.
_attachments
;
var
keys
;
for
(
keys
in
attachKeys
)
{
for
(
keys
in
attachKeys
)
{
XHRwrapper
(
command
,
docId
,
keys
,
'
DELETE
'
,
mime
,
''
,
false
,
false
,
XHRwrapper
(
command
,
docId
,
keys
,
'
DELETE
'
,
mime
,
''
,
false
,
false
,
function
(
response
){
function
(
response
)
{
//console.log('this key got deleted : ' + keys);
//console.log('this key got deleted : ' + keys);
}
}
);
);
...
@@ -734,19 +760,6 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -734,19 +760,6 @@ jIO.addStorageType("s3", function (spec, my) {
deleteDocument
();
deleteDocument
();
}
}
);
);
function
deleteDocument
(){
XHRwrapper
(
command
,
docId
,
''
,
'
DELETE
'
,
mime
,
''
,
true
,
false
,
function
(
reponse
)
{
that
.
success
({
// response
"
ok
"
:
true
,
"
id
"
:
docId
//"rev": current_revision
});
}
);
}
};
};
that
.
removeAttachment
=
function
(
command
)
{
that
.
removeAttachment
=
function
(
command
)
{
...
@@ -806,6 +819,7 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -806,6 +819,7 @@ jIO.addStorageType("s3", function (spec, my) {
var
mime
=
'
text/plain; charset=UTF-8
'
;
var
mime
=
'
text/plain; charset=UTF-8
'
;
function
makeJSON
()
{
function
makeJSON
()
{
var
$
;
var
keys
=
$
(
mon_document
).
find
(
'
Key
'
);
var
keys
=
$
(
mon_document
).
find
(
'
Key
'
);
var
resultTable
=
[];
var
resultTable
=
[];
var
counter
=
0
;
var
counter
=
0
;
...
@@ -898,7 +912,7 @@ jIO.addStorageType("s3", function (spec, my) {
...
@@ -898,7 +912,7 @@ jIO.addStorageType("s3", function (spec, my) {
}
}
}
else
{
}
else
{
for
(
i
;
i
>=
0
;
i
--
)
{
for
(
i
;
i
>=
0
;
i
--
)
{
var
docId
=
resultTable
[
i
];
docId
=
resultTable
[
i
];
allDocResponse
.
rows
[
i
]
=
{
allDocResponse
.
rows
[
i
]
=
{
"
id
"
:
priv
.
fileNameToIds
(
docId
)
+
''
,
"
id
"
:
priv
.
fileNameToIds
(
docId
)
+
''
,
"
key
"
:
docId
,
"
key
"
:
docId
,
...
...
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