mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-24 21:55:52 +00:00
[FEATURE] REST API Tool breaks out editable components of URL only, move method to a button instead of a dropdown
This commit is contained in:
@@ -98,7 +98,7 @@ h2 {
|
|||||||
#output {
|
#output {
|
||||||
width: 60%;
|
width: 60%;
|
||||||
}
|
}
|
||||||
p, h3 {
|
h3 {
|
||||||
margin:16px 0;
|
margin:16px 0;
|
||||||
font-family:'open sans';
|
font-family:'open sans';
|
||||||
font-weight:300;
|
font-weight:300;
|
||||||
@@ -150,7 +150,7 @@ p, h3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#request_options > div {
|
#request_options > div {
|
||||||
float:right;
|
/* float:right;
|
||||||
margin:0;
|
margin:0;
|
||||||
width:200px;
|
width:200px;
|
||||||
color:#fff;
|
color:#fff;
|
||||||
@@ -161,23 +161,25 @@ p, h3 {
|
|||||||
background-image:linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
background-image:linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
||||||
background-image:-webkit-linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), -webkit-linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
background-image:-webkit-linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), -webkit-linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
||||||
background-image:-moz-linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), -moz-linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
background-image:-moz-linear-gradient(283deg, rgba(255,255,255,0.1) 50%, transparent 55%), -moz-linear-gradient(top, rgba(255,255,255,0.15), transparent);
|
||||||
*/
|
|
||||||
|
|
||||||
border-radius:4px;
|
border-radius:4px;
|
||||||
border-bottom:1px solid #fff;
|
border-bottom:1px solid #fff;
|
||||||
background-color: #346AA9;
|
background-color: #346AA9;
|
||||||
font-weight:300;
|
font-weight:300;
|
||||||
font-size:16px;
|
font-size:16px;
|
||||||
/* line-height:50px; */
|
/* line-height:50px;
|
||||||
text-align:center;
|
text-align:center;
|
||||||
cursor:default;
|
cursor:default;
|
||||||
|
|
||||||
user-select:none;
|
user-select:none;
|
||||||
-webkit-user-select:none;
|
-webkit-user-select:none;
|
||||||
-moz-user-select:none;
|
-moz-user-select:none;
|
||||||
|
*/
|
||||||
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
#request_button {
|
#request_button {
|
||||||
background:#3a87ad;
|
background-color: #346AA9;
|
||||||
}
|
}
|
||||||
#request_button.depressed {
|
#request_button.depressed {
|
||||||
background:#295F7A;
|
background:#295F7A;
|
||||||
@@ -290,9 +292,9 @@ ul.toolbar li {
|
|||||||
border:1px dotted #3a87ad;
|
border:1px dotted #3a87ad;
|
||||||
}
|
}
|
||||||
|
|
||||||
#selected_command {
|
/*#selected_command {
|
||||||
margin-top:-32px;
|
margin-top:-32px;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
#selected_command a {
|
#selected_command a {
|
||||||
text-decoration:none;
|
text-decoration:none;
|
||||||
@@ -410,7 +412,8 @@ span.cm-atom {
|
|||||||
#rest_url_wrapper {
|
#rest_url_wrapper {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: -11px;
|
text-indent: -2em;
|
||||||
|
padding-left: 2.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#rest_url {
|
#rest_url {
|
||||||
@@ -418,4 +421,37 @@ span.cm-atom {
|
|||||||
border: 0;
|
border: 0;
|
||||||
background: none;
|
background: none;
|
||||||
font-size:13px;
|
font-size:13px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url .editable {
|
||||||
|
font-weight: bold;
|
||||||
|
font-family:'inconsolata',monospace;
|
||||||
|
border-width: 0 0px 1px 0;
|
||||||
|
border-style: dotted;
|
||||||
|
border-color: #aaa;
|
||||||
|
min-width: 5em;
|
||||||
|
background: none;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url .non_editable {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url_wrapper p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url div {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url input {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#rest_url div label,
|
||||||
|
#rest_url div input {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ var rest_url = $('#rest_url');
|
|||||||
var rest_method = $("#rest_method");
|
var rest_method = $("#rest_method");
|
||||||
var selected_command = $("#selected_command");
|
var selected_command = $("#selected_command");
|
||||||
var spinner = $(".loader");
|
var spinner = $(".loader");
|
||||||
|
var reminders = $("#rest_url_wrapper .rest_reminders");
|
||||||
|
var test_warning = $("#test_warning");
|
||||||
|
|
||||||
var GET = "GET";
|
var GET = "GET";
|
||||||
var POST = "POST";
|
var POST = "POST";
|
||||||
@@ -14,6 +16,8 @@ var PUT = "PUT";
|
|||||||
var DELETE = "DELETE";
|
var DELETE = "DELETE";
|
||||||
var URL_BASE = "https://api.ripple.com:443";
|
var URL_BASE = "https://api.ripple.com:443";
|
||||||
|
|
||||||
|
var DOC_BASE = "ripple-rest.html";
|
||||||
|
|
||||||
|
|
||||||
function slugify(str) {
|
function slugify(str) {
|
||||||
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
||||||
@@ -57,32 +61,46 @@ $(commands).click(function() {
|
|||||||
|
|
||||||
//---------- List of requests ------------------------//
|
//---------- List of requests ------------------------//
|
||||||
|
|
||||||
|
var DEFAULT_ADDRESS_1 = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
|
var DEFAULT_ADDRESS_2 = "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX";
|
||||||
|
var DEFAULT_HASH = "9D591B18EDDD34F0B6CF4223A2940AEA2C3CC778925BABF289E0011CD8FA056E";
|
||||||
|
|
||||||
Request('Generate Account', {
|
Request('Generate Account', {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/accounts/new",
|
path: "/v1/accounts/new",
|
||||||
description: 'Generate the keys for a potential new account',
|
description: 'Generate the keys for a potential new account',
|
||||||
link: 'ripple-rest.html#generating-accounts'
|
link: '#generating-accounts'
|
||||||
});
|
});
|
||||||
|
|
||||||
Request('Get Account Balances', {
|
Request('Get Account Balances', {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: '/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/balances',
|
path: '/v1/accounts/{:address}/balances',
|
||||||
description: 'Retrieve the current balances for the given Ripple account',
|
description: 'Retrieve the current balances for the given Ripple account',
|
||||||
link: 'ripple-rest.html#account-balances'
|
link: '#account-balances',
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request('Get Account Settings', {
|
Request('Get Account Settings', {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: '/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings',
|
path: '/v1/accounts/{:address}/settings',
|
||||||
description: 'Retrieve the current settings for the given Ripple account',
|
description: 'Retrieve the current settings for the given Ripple account',
|
||||||
link: 'ripple-rest.html#account-settings'
|
link: '#account-settings',
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request('Update Account Settings', {
|
Request('Update Account Settings', {
|
||||||
method: POST,
|
method: POST,
|
||||||
path: '/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/settings',
|
path: '/v1/accounts/{:address}/settings',
|
||||||
description: 'Change the current settings for the given Ripple account. <span class="btn-danger">Please, only use test accounts here.</span>',
|
description: 'Change the current settings for the given Ripple account. <span class="btn-danger">Please, only use test accounts here.</span>',
|
||||||
link: 'ripple-rest.html#updating-account-settings',
|
link: '#updating-account-settings',
|
||||||
|
test_only: true,
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1
|
||||||
|
},
|
||||||
body: {
|
body: {
|
||||||
secret: "sssssssssssssssssssssssssssss",
|
secret: "sssssssssssssssssssssssssssss",
|
||||||
settings: {
|
settings: {
|
||||||
@@ -97,21 +115,28 @@ Request('Update Account Settings', {
|
|||||||
|
|
||||||
Request('Prepare Payment', {
|
Request('Prepare Payment', {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: '/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/payments/paths/ra5nK24KXen9AHvsdFTKHSANinZseWnPcX/1+USD+rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
|
path: '/v1/accounts/{:source_address}/payments/paths/{:destination_address}/{:amount}?{:query_params}',
|
||||||
description: 'Change the current settings for the given Ripple account',
|
description: 'Change the current settings for the given Ripple account',
|
||||||
link: 'ripple-rest.html#preparing-a-payment'
|
link: '#prepare-payment',
|
||||||
|
params: {
|
||||||
|
"{:source_address}": DEFAULT_ADDRESS_1,
|
||||||
|
"{:destination_address}": DEFAULT_ADDRESS_2,
|
||||||
|
"{:amount}": "1+USD+rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"{:query_params}": "source_currencies=USD"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request('Submit Payment', {
|
Request('Submit Payment', {
|
||||||
method: POST,
|
method: POST,
|
||||||
path: '/v1/payments',
|
path: '/v1/payments',
|
||||||
description: 'Send a prepared payment to the network. <span class="btn-danger">Please, only use test accounts here.</span>',
|
description: 'Send a prepared payment to the network. <span class="btn-danger">Please, only use test accounts here.</span>',
|
||||||
link: 'ripple-rest.html#submitting-a-payment',
|
link: '#submitting-a-payment',
|
||||||
|
test_only: true,
|
||||||
body: {
|
body: {
|
||||||
"secret": "sssssssssssssssssssssssssssss",
|
"secret": "sssssssssssssssssssssssssssss",
|
||||||
"client_resource_id": "348170b9-16b9-4927-854d-7f9d4a2a692d",
|
"client_resource_id": "348170b9-16b9-4927-854d-7f9d4a2a692d",
|
||||||
"payment": {
|
"payment": {
|
||||||
"source_account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"source_account": DEFAULT_ADDRESS_1,
|
||||||
"source_tag": "",
|
"source_tag": "",
|
||||||
"source_amount": {
|
"source_amount": {
|
||||||
"value": "1",
|
"value": "1",
|
||||||
@@ -119,12 +144,12 @@ Request('Submit Payment', {
|
|||||||
"issuer": ""
|
"issuer": ""
|
||||||
},
|
},
|
||||||
"source_slippage": "0",
|
"source_slippage": "0",
|
||||||
"destination_account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
"destination_account": DEFAULT_ADDRESS_2,
|
||||||
"destination_tag": "",
|
"destination_tag": "",
|
||||||
"destination_amount": {
|
"destination_amount": {
|
||||||
"value": "1",
|
"value": "1",
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn"
|
"issuer": DEFAULT_ADDRESS_1
|
||||||
},
|
},
|
||||||
"invoice_id": "",
|
"invoice_id": "",
|
||||||
"paths": "[]",
|
"paths": "[]",
|
||||||
@@ -136,30 +161,46 @@ Request('Submit Payment', {
|
|||||||
|
|
||||||
Request("Confirm Payment", {
|
Request("Confirm Payment", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/payments/9D591B18EDDD34F0B6CF4223A2940AEA2C3CC778925BABF289E0011CD8FA056E",
|
path: "/v1/accounts/{:address}/payments/{:hash}",
|
||||||
description: "Retrieve details of a payment and its status",
|
description: "Retrieve details of a payment and its status",
|
||||||
link: "ripple-rest.html#confirming-a-payment"
|
link: "#confirming-a-payment",
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1,
|
||||||
|
"{:hash}": DEFAULT_HASH
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Get Payment History", {
|
Request("Get Payment History", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/payments",
|
path: "/v1/accounts/{:address}/payments?{:query_params}",
|
||||||
description: "Browse through the history of payments sent and received by an account",
|
description: "Browse through the history of payments sent and received by an account",
|
||||||
link: "ripple-rest.html#payment-history",
|
link: "#payment-history",
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1,
|
||||||
|
"{:query_params}": "direction=incoming&exclude_failed=true"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Get Trustlines", {
|
Request("Get Trustlines", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/trustlines?currency=USD",
|
path: "/v1/accounts/{:address}/trustlines?{:query_params}",
|
||||||
description: "Check the status of one or more trustlines attached to an account",
|
description: "Check the status of one or more trustlines attached to an account",
|
||||||
link: "ripple-rest.html#reviewing-trustlines"
|
link: "#reviewing-trustlines",
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1,
|
||||||
|
"{:query_params}": "currency=USD&counterparty=ra5nK24KXen9AHvsdFTKHSANinZseWnPcX"
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Grant Trustline", {
|
Request("Grant Trustline", {
|
||||||
method: POST,
|
method: POST,
|
||||||
path: "/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/trustlines",
|
path: "/v1/accounts/{:address}/trustlines",
|
||||||
description: "Add or modify a trustline from this account. <span class='btn-danger'>Please, only use test accounts here.</span>",
|
description: "Add or modify a trustline from this account. <span class='btn-danger'>Please, only use test accounts here.</span>",
|
||||||
link: "ripple-rest.html#granting-a-trustline",
|
link: "#granting-a-trustline",
|
||||||
|
test_only: true,
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1
|
||||||
|
},
|
||||||
body: {
|
body: {
|
||||||
"secret": "sneThnzgBgxc3zXPG....",
|
"secret": "sneThnzgBgxc3zXPG....",
|
||||||
"trustline": {
|
"trustline": {
|
||||||
@@ -173,37 +214,44 @@ Request("Grant Trustline", {
|
|||||||
|
|
||||||
Request("Check Notifications", {
|
Request("Check Notifications", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/accounts/rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn/notifications/9D591B18EDDD34F0B6CF4223A2940AEA2C3CC778925BABF289E0011CD8FA056E",
|
path: "/v1/accounts/{:address}/notifications/{:hash}",
|
||||||
description: "Browse through the history of payments sent and received by an account",
|
description: "Browse through the history of payments sent and received by an account",
|
||||||
link: "ripple-rest.html#checking-notifications"
|
link: "#checking-notifications",
|
||||||
|
params: {
|
||||||
|
"{:address}": DEFAULT_ADDRESS_1,
|
||||||
|
"{:hash}": DEFAULT_HASH
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Check Connection", {
|
Request("Check Connection", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/server/connected",
|
path: "/v1/server/connected",
|
||||||
description: "Check whether the REST server is connected to a rippled server",
|
description: "Check whether the REST server is connected to a rippled server",
|
||||||
link: "ripple-rest.html#check-connection-state"
|
link: "#check-connection-state"
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Get Server Status", {
|
Request("Get Server Status", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/server",
|
path: "/v1/server",
|
||||||
description: "Retrieve information about the current status of the Ripple-REST server and the rippled server it is connected to",
|
description: "Retrieve information about the current status of the Ripple-REST server and the rippled server it is connected to",
|
||||||
link: "ripple-rest.html#get-server-status"
|
link: "#get-server-status"
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Retrieve Ripple Transaction", {
|
Request("Retrieve Ripple Transaction", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/tx/85C5E6762DE7969DC1BD69B3C8C7387A5B8FCE6A416AA1F74C0ED5D10F08EADD",
|
path: "/v1/tx/{:hash}",
|
||||||
description: "Retrieve a raw Ripple transaction",
|
description: "Retrieve a raw Ripple transaction",
|
||||||
link: "ripple-rest.html#retrieve-ripple-transaction"
|
link: "#retrieve-ripple-transaction",
|
||||||
|
params: {
|
||||||
|
"{:hash}": DEFAULT_HASH
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Request("Generate UUID", {
|
Request("Generate UUID", {
|
||||||
method: GET,
|
method: GET,
|
||||||
path: "/v1/uuid",
|
path: "/v1/uuid",
|
||||||
description: "Create a universally-unique identifier (UUID) to use as the client resource ID for a payment",
|
description: "Create a universally-unique identifier (UUID) to use as the client resource ID for a payment",
|
||||||
link: "ripple-rest.html#create-client-resource-id"
|
link: "#create-client-resource-id"
|
||||||
});
|
});
|
||||||
|
|
||||||
//---------- End req. List ---------------------------//
|
//---------- End req. List ---------------------------//
|
||||||
@@ -221,8 +269,13 @@ var cm_response = CodeMirror(response_body.get(0), {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
});
|
});
|
||||||
|
|
||||||
function update_method() {
|
function update_method(el) {
|
||||||
method = $(this).val();
|
if (el === undefined) {
|
||||||
|
method = $(this).val();
|
||||||
|
} else {
|
||||||
|
method = $(el).val();
|
||||||
|
}
|
||||||
|
|
||||||
if (method == GET || method == DELETE) {
|
if (method == GET || method == DELETE) {
|
||||||
request_body.hide();
|
request_body.hide();
|
||||||
} else {
|
} else {
|
||||||
@@ -231,12 +284,47 @@ function update_method() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function change_path(command) {
|
||||||
|
rest_url.empty();
|
||||||
|
reminders.html(" ");
|
||||||
|
|
||||||
|
var re = /(\{:[^}]+\})/g; // match stuff like {:address}
|
||||||
|
params = command.path.split(re);
|
||||||
|
|
||||||
|
//console.log(params);
|
||||||
|
for (i=0; i<params.length; i++) {
|
||||||
|
if (params[i].match(/\{:[^}]+\}/) !== null) {
|
||||||
|
if (command.params === undefined || command.params[params[i]] === undefined) {
|
||||||
|
var default_val = params[i];
|
||||||
|
} else {
|
||||||
|
var default_val = command.params[params[i]];
|
||||||
|
}
|
||||||
|
//rest_url.append("<span class='editable' contenteditable='true' id='resturl_"+params[i]+"'>"+default_val+"</span>");
|
||||||
|
|
||||||
|
var new_div = $("<div>").appendTo(rest_url);
|
||||||
|
var new_param = $("<input type='text' id='resturl_"+params[i]+"' value='"+default_val+"' class='editable' title='"+params[i]+"' />").appendTo(new_div);
|
||||||
|
new_param.autosizeInput({"space": 0});
|
||||||
|
//var new_label = $("<label class='reminder' for='resturl_"+params[i]+"'>"+params[i]+"</label>").appendTo(new_div);
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rest_url.append("<span class='non_editable'>"+params[i]+"</span>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function select_request(request) {
|
function select_request(request) {
|
||||||
command = requests[request];
|
command = requests[request];
|
||||||
|
|
||||||
|
if (command.test_only === true) {
|
||||||
|
test_warning.show();
|
||||||
|
} else {
|
||||||
|
test_warning.hide();
|
||||||
|
}
|
||||||
|
|
||||||
if (command.description) {
|
if (command.description) {
|
||||||
$(description).html($('<a>')
|
$(description).html($('<a>')
|
||||||
.attr('href', command.link)
|
.attr('href', DOC_BASE+command.link)
|
||||||
.html(command.description));
|
.html(command.description));
|
||||||
$(description).show();
|
$(description).show();
|
||||||
} else {
|
} else {
|
||||||
@@ -244,15 +332,19 @@ function select_request(request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selected_command.html($('<a>')
|
selected_command.html($('<a>')
|
||||||
.attr('href', command.link)
|
.attr('href', DOC_BASE+command.link)
|
||||||
.text(command.name));
|
.text(command.name));
|
||||||
|
|
||||||
//rest_url.val(command.path);
|
//rest_url.val(command.path);
|
||||||
rest_url.text(command.path);
|
//rest_url.text(command.path);
|
||||||
|
change_path(command);
|
||||||
|
|
||||||
|
|
||||||
rest_method.val(command.method);
|
// rest_method.val(command.method);
|
||||||
rest_method.change();
|
// rest_method.change();
|
||||||
|
request_button.val(command.method);
|
||||||
|
request_button.text(command.method+" request");
|
||||||
|
update_method(request_button);
|
||||||
|
|
||||||
if (command.method == POST || command.method == PUT) {
|
if (command.method == POST || command.method == PUT) {
|
||||||
cm_request.setValue(JSON.stringify(command.body, null, 2));
|
cm_request.setValue(JSON.stringify(command.body, null, 2));
|
||||||
@@ -270,22 +362,33 @@ function get_uuid(callback) {
|
|||||||
$.get(URL_BASE + "/v1/uuid").done(callback);
|
$.get(URL_BASE + "/v1/uuid").done(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_path() {
|
||||||
|
s = "";
|
||||||
|
rest_url.find(".non_editable, .editable").each(function() {
|
||||||
|
if (this.tagName == "INPUT") {
|
||||||
|
s += $(this).val();
|
||||||
|
} else {
|
||||||
|
s += $(this).text();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
function send_request() {
|
function send_request() {
|
||||||
var method = rest_method.val();
|
//var method = rest_method.val();
|
||||||
|
var method = request_button.val();
|
||||||
if (method != GET && method != POST && method != PUT && method != DELETE) {
|
if (method != GET && method != POST && method != PUT && method != DELETE) {
|
||||||
console.log("ERROR: unrecognized http method");
|
console.log("ERROR: unrecognized http method");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//var path = rest_url.val();
|
//var path = rest_url.val();
|
||||||
var path = rest_url.text();
|
var path = get_path();
|
||||||
|
|
||||||
$(this).addClass('depressed');
|
$(this).addClass('depressed');
|
||||||
response_body.addClass('obscured');
|
response_body.addClass('obscured');
|
||||||
|
|
||||||
if (method == PUT || method == POST) {
|
if (method == PUT || method == POST) {
|
||||||
var body = cm_request.getValue();
|
var body = cm_request.getValue();
|
||||||
console.log(body);
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: method,
|
type: method,
|
||||||
url: URL_BASE + path,
|
url: URL_BASE + path,
|
||||||
@@ -326,7 +429,7 @@ function reset_response_area() {
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
request_button.click(send_request);
|
request_button.click(send_request);
|
||||||
rest_method.change(update_method);
|
//rest_method.change(update_method);
|
||||||
|
|
||||||
get_uuid(function(resp,status,xhr) {
|
get_uuid(function(resp,status,xhr) {
|
||||||
requests["submit-payment"].body.client_resource_id = resp.uuid;
|
requests["submit-payment"].body.client_resource_id = resp.uuid;
|
||||||
|
|||||||
1
js/jquery.autosize.input.min.js
vendored
Normal file
1
js/jquery.autosize.input.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
var Plugins;(function(n){var t=function(){function n(n){typeof n=="undefined"&&(n=30);this.space=n}return n}(),i;n.AutosizeInputOptions=t;i=function(){function n(t,i){var r=this;this._input=$(t);this._options=$.extend({},n.getDefaultOptions(),i);this._mirror=$('<span style="position:absolute; top:-999px; left:0; white-space:pre;"/>');$.each(["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent"],function(n,t){r._mirror[0].style[t]=r._input.css(t)});$("body").append(this._mirror);this._input.on("keydown keyup input propertychange change",function(){r.update()});(function(){r.update()})()}return n.prototype.getOptions=function(){return this._options},n.prototype.update=function(){var n=this._input.val()||"",t;n!==this._mirror.text()&&(this._mirror.text(n),t=this._mirror.width()+this._options.space,this._input.width(t))},n.getDefaultOptions=function(){return this._defaultOptions},n.getInstanceKey=function(){return"autosizeInputInstance"},n._defaultOptions=new t,n}();n.AutosizeInput=i,function(t){var i="autosize-input",r=["text","password","search","url","tel","email","number"];t.fn.autosizeInput=function(u){return this.each(function(){if(this.tagName=="INPUT"&&t.inArray(this.type,r)>-1){var f=t(this);f.data(n.AutosizeInput.getInstanceKey())||(u==undefined&&(u=f.data(i)),f.data(n.AutosizeInput.getInstanceKey(),new n.AutosizeInput(this,u)))}})};t(function(){t("input[data-"+i+"]").autosizeInput()})}(jQuery)})(Plugins||(Plugins={}))
|
||||||
@@ -129,21 +129,25 @@ mixpanel.init("132d42885e094171f34467fc54da6fab");
|
|||||||
<div id='io_wrapper'>
|
<div id='io_wrapper'>
|
||||||
<div id='input' class='io'>
|
<div id='input' class='io'>
|
||||||
<h2>REST Request</h2>
|
<h2>REST Request</h2>
|
||||||
<div id='request_options'>
|
<div id='test_warning' class='alert alert-danger' style='display:none;'>
|
||||||
<div class="button btn btn-primary api" id='request_button'>Send request</div>
|
<h4>Test accounts only!</h4>
|
||||||
|
<p>Never submit account secrets to a server you do not control, unless you are prepared to lose ownership of the account!</p>
|
||||||
</div>
|
</div>
|
||||||
<div style="clear:both;"></div>
|
<div style="clear:both;"></div>
|
||||||
<h3 id='selected_command' title='Reference information'></h3>
|
<h3 id='selected_command' title='Reference information'></h3>
|
||||||
<p id='description'></p>
|
<p id='description'></p>
|
||||||
<div id='invalid'>Invalid JSON</div>
|
<div id='invalid'>Invalid JSON</div>
|
||||||
<select id='rest_method'>
|
<!-- <select id='rest_method'>
|
||||||
<option value='GET'>GET</option>
|
<option value='GET'>GET</option>
|
||||||
<option value='POST'>POST</option>
|
<option value='POST'>POST</option>
|
||||||
</select>
|
</select>-->
|
||||||
<div id='rest_url_wrapper'>
|
<div id='rest_url_wrapper'>
|
||||||
<span id='rest_host'>https://api.ripple.com</span><span id='rest_url' contenteditable='true'></span>
|
<p><span id='rest_host'>https://api.ripple.com</span><span id='rest_url'></span></p>
|
||||||
</div>
|
</div>
|
||||||
<div id='request_body'></div>
|
<div id='request_body'></div>
|
||||||
|
<div id='request_options'>
|
||||||
|
<div class="button btn btn-primary api" id='request_button'>Send request</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id='output' class='io'>
|
<div id='output' class='io'>
|
||||||
<h2>Response</h2>
|
<h2>Response</h2>
|
||||||
@@ -232,6 +236,7 @@ mixpanel.init("132d42885e094171f34467fc54da6fab");
|
|||||||
<script type='text/javascript' src='js/es5-shim.js'></script>
|
<script type='text/javascript' src='js/es5-shim.js'></script>
|
||||||
<script src='//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/codemirror.min.js'></script>
|
<script src='//cdnjs.cloudflare.com/ajax/libs/codemirror/3.16.0/codemirror.min.js'></script>
|
||||||
<script type='text/javascript' src='js/cm-javascript.min.js'></script>
|
<script type='text/javascript' src='js/cm-javascript.min.js'></script>
|
||||||
|
<script type='text/javascript' src='js/jquery.autosize.input.min.js'></script>
|
||||||
<script type='text/javascript' src='js/apitool-rest.js'></script>
|
<script type='text/javascript' src='js/apitool-rest.js'></script>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -132,15 +132,15 @@ mixpanel.init("132d42885e094171f34467fc54da6fab");
|
|||||||
<div id='io_wrapper'>
|
<div id='io_wrapper'>
|
||||||
<div id='input' class='io'>
|
<div id='input' class='io'>
|
||||||
<h2>WebSocket Request</h2>
|
<h2>WebSocket Request</h2>
|
||||||
<div id='request_options'>
|
|
||||||
<div class="button btn btn-primary api" id='request_button'>Send request</div>
|
|
||||||
</div>
|
|
||||||
<div style="clear:both;"></div>
|
<div style="clear:both;"></div>
|
||||||
<h3 id='selected_command' title='Reference information'>server_info</h3>
|
<h3 id='selected_command' title='Reference information'>server_info</h3>
|
||||||
<p id='description'></p>
|
<p id='description'></p>
|
||||||
<div id='invalid'>Invalid JSON</div>
|
<div id='invalid'>Invalid JSON</div>
|
||||||
<p>JSON</p>
|
<p>JSON</p>
|
||||||
<div id='request'></div>
|
<div id='request'></div>
|
||||||
|
<div id='request_options'>
|
||||||
|
<div class="button btn btn-primary api" id='request_button'>Send request</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id='output' class='io'>
|
<div id='output' class='io'>
|
||||||
<h2>Response</h2>
|
<h2>Response</h2>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Installation instructions and source code can be found in the [Ripple-REST repos
|
|||||||
|
|
||||||
#### Payments ####
|
#### Payments ####
|
||||||
|
|
||||||
* [Prepare Payment - `GET /v1/accounts/{:address}/payments/paths`](#preparing-a-payment)
|
* [Prepare Payment - `GET /v1/accounts/{:address}/payments/paths`](#prepare-payment)
|
||||||
* [Submit Payment - `POST /v1/payments`](#submitting-a-payment)
|
* [Submit Payment - `POST /v1/payments`](#submitting-a-payment)
|
||||||
* [Confirm Payment - `GET /v1/accounts/{:address}/payments/{:payment}`](#confirming-a-payment)
|
* [Confirm Payment - `GET /v1/accounts/{:address}/payments/{:payment}`](#confirming-a-payment)
|
||||||
* [Get Payment History - `GET /v1/accounts/{:address}/payments`](#confirming-a-payment)
|
* [Get Payment History - `GET /v1/accounts/{:address}/payments`](#confirming-a-payment)
|
||||||
@@ -76,11 +76,9 @@ Note that when you submit a payment for processing, you have to assign a unique
|
|||||||
|
|
||||||
The Ripple protocol supports multiple types of transactions other than just payments. Transactions are considered to be any changes to the database made on behalf of a Ripple Address. Transactions are first constructed and then submitted to the network. After transaction processing, meta data is associated with the transaction which itemizes the resulting changes to the ledger.
|
The Ripple protocol supports multiple types of transactions other than just payments. Transactions are considered to be any changes to the database made on behalf of a Ripple Address. Transactions are first constructed and then submitted to the network. After transaction processing, meta data is associated with the transaction which itemizes the resulting changes to the ledger.
|
||||||
|
|
||||||
+ Payment: A Payment transaction is an authorized transfer of balance from one address to another. (This maps to rippled's [Payment transaction type](transactions.html#payment))
|
* Payment: A Payment transaction is an authorized transfer of balance from one address to another. (This maps to rippled's [Payment transaction type](transactions.html#payment))
|
||||||
|
* Trustline: A Trustline transaction is an authorized grant of trust between two addresses. (This maps to rippled's [TrustSet transaction type](transactions.html#payment))
|
||||||
+ Trustline: A Trustline transaction is an authorized grant of trust between two addresses. (This maps to rippled's [TrustSet transaction type](transactions.html#payment))
|
* Setting: A Setting transaction is an authorized update of account flags under a Ripple Account. (This maps to rippled's [AccountSet transaction type](transactions.html#payment))
|
||||||
|
|
||||||
+ Setting: A Setting transaction is an authorized update of account flags under a Ripple Account. (This maps to rippled's [AccountSet transaction type](transactions.html#payment))
|
|
||||||
|
|
||||||
## Getting Started ##
|
## Getting Started ##
|
||||||
|
|
||||||
@@ -100,6 +98,7 @@ If you want to run your own Ripple-REST server, see the [installation instructio
|
|||||||
|
|
||||||
|
|
||||||
As a programmer, you will also need to have a suitable HTTP client that allows you to make secure HTTP (`HTTPS`) GET and POST requests. There are lots of options, including:
|
As a programmer, you will also need to have a suitable HTTP client that allows you to make secure HTTP (`HTTPS`) GET and POST requests. There are lots of options, including:
|
||||||
|
|
||||||
* The [`curl`](http://curl.haxx.se/) commandline utility
|
* The [`curl`](http://curl.haxx.se/) commandline utility
|
||||||
* The [Poster Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/poster/)
|
* The [Poster Firefox extension](https://addons.mozilla.org/en-US/firefox/addon/poster/)
|
||||||
* The [Postman Chrome extension](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en)
|
* The [Postman Chrome extension](https://chrome.google.com/webstore/detail/postman-rest-client/fdmmgilgnpjigdojojpjoooidkmcomcm?hl=en)
|
||||||
@@ -195,13 +194,23 @@ All currencies on the Ripple Network have issuers, except for XRP. In the case o
|
|||||||
|
|
||||||
For more information about XRP see [the Ripple wiki page on XRP](https://ripple.com/wiki/XRP). For more information about using currencies other than XRP on the Ripple Network see [the Ripple wiki page for gateways](https://ripple.com/wiki/Ripple_for_Gateways).
|
For more information about XRP see [the Ripple wiki page on XRP](https://ripple.com/wiki/XRP). For more information about using currencies other than XRP on the Ripple Network see [the Ripple wiki page for gateways](https://ripple.com/wiki/Ripple_for_Gateways).
|
||||||
|
|
||||||
Amount Object:
|
### Amounts in JSON ###
|
||||||
|
|
||||||
|
When an amount of currency (or other asset) is specified as part of a JSON body, it is encoded as an object with three fields:
|
||||||
|
|
||||||
|
| Field | Value | Description |
|
||||||
|
|-------|-------|-------------|
|
||||||
|
| value | String (Quoted decimal) | The quantity of the currency |
|
||||||
|
| currency | String | Three-digit [ISO 4217 Currency Code](http://www.xe.com/iso4217.php) specifying which currency |
|
||||||
|
| issuer | String | The Ripple address of the account issuing the currency. This is usually an [issuing gateway](https://wiki.ripple.com/Gateway_List). Always an empty string for XRP. |
|
||||||
|
|
||||||
|
Example Amount Object:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
"value": "1.0",
|
"value": "1.0",
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"issuer": "r..."
|
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -217,6 +226,18 @@ or for XRP:
|
|||||||
|
|
||||||
The `value` field can get very large or very small. See the [Currency Format](https://wiki.ripple.com/Currency_Format) for the exact limits of Ripple's precision.
|
The `value` field can get very large or very small. See the [Currency Format](https://wiki.ripple.com/Currency_Format) for the exact limits of Ripple's precision.
|
||||||
|
|
||||||
|
### Amounts in URLs ###
|
||||||
|
|
||||||
|
When an amount of currency has to be specified in a URL, you use the same fields as the JSON object -- value, currency, and issuer -- but concatenate them with `+` symbols.
|
||||||
|
|
||||||
|
Example Amount:
|
||||||
|
|
||||||
|
`1.0+USD+rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q`
|
||||||
|
|
||||||
|
When specifying an amount of XRP, you can omit the issuer entirely. For example:
|
||||||
|
|
||||||
|
`1.0+XRP`
|
||||||
|
|
||||||
## <a id="payment_object"></a> Payment Objects ##
|
## <a id="payment_object"></a> Payment Objects ##
|
||||||
|
|
||||||
The `Payment` object is a simplified version of the standard Ripple transaction format.
|
The `Payment` object is a simplified version of the standard Ripple transaction format.
|
||||||
@@ -261,7 +282,7 @@ The fields of a Payment object are defined as follows:
|
|||||||
| `destination_tag` | Unsigned Integer | (Optional) A 32-bit unsigned integer (0-4294967294, inclusive) that is generally used if the recipient of the payment is a hosted wallet at a gateway. |
|
| `destination_tag` | Unsigned Integer | (Optional) A 32-bit unsigned integer (0-4294967294, inclusive) that is generally used if the recipient of the payment is a hosted wallet at a gateway. |
|
||||||
| `source_slippage` | String (Quoted decimal number) | can be specified to give the `source_amount` a cushion and increase its chance of being processed successfully. This is helpful if the payment path changes slightly between the time when a payment options quote is given and when the payment is submitted. The `source_address` will never be charged more than `source_slippage` + the `value` specified in `source_amount`. |
|
| `source_slippage` | String (Quoted decimal number) | can be specified to give the `source_amount` a cushion and increase its chance of being processed successfully. This is helpful if the payment path changes slightly between the time when a payment options quote is given and when the payment is submitted. The `source_address` will never be charged more than `source_slippage` + the `value` specified in `source_amount`. |
|
||||||
| `invoice_id` | String | (Optional) 256-bit hash that can be used to link payments to an invoice or bill. |
|
| `invoice_id` | String | (Optional) 256-bit hash that can be used to link payments to an invoice or bill. |
|
||||||
| `paths` | String | A "stringified" version of the Ripple PathSet structure. You can get a path for your payment from the [Prepare Payment](#preparing-a-payment) method. |
|
| `paths` | String | A "stringified" version of the Ripple PathSet structure. You can get a path for your payment from the [Prepare Payment](#prepare-payment) method. |
|
||||||
| `flag_no_direct_ripple` | Boolean | (Optional, defaults to false) `true` if `paths` are specified and the sender would like the Ripple Network to disregard any direct paths from the `source_address` to the `destination_address`. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet. Most users will not need to use this option. |
|
| `flag_no_direct_ripple` | Boolean | (Optional, defaults to false) `true` if `paths` are specified and the sender would like the Ripple Network to disregard any direct paths from the `source_address` to the `destination_address`. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet. Most users will not need to use this option. |
|
||||||
| `flag_partial_payment` | Boolean | (Optional, defaults to false) If set to `true`, fees will be deducted from the delivered amount instead of the sent amount. (*Caution:* There is no minimum amount that will actually arrive as a result of using this flag; only a miniscule amount may actually be received.) See [Partial Payments](transactions.html#partial-payments) |
|
| `flag_partial_payment` | Boolean | (Optional, defaults to false) If set to `true`, fees will be deducted from the delivered amount instead of the sent amount. (*Caution:* There is no minimum amount that will actually arrive as a result of using this flag; only a miniscule amount may actually be received.) See [Partial Payments](transactions.html#partial-payments) |
|
||||||
|
|
||||||
@@ -270,30 +291,35 @@ The fields of a Payment object are defined as follows:
|
|||||||
|
|
||||||
`ripple-rest` provides access to `ripple-lib`'s robust transaction submission processes. This means that it will set the fee, manage the transaction sequence numbers, sign the transaction with your secret, and resubmit the transaction up to 10 times if `rippled` reports an initial error that can be solved automatically.
|
`ripple-rest` provides access to `ripple-lib`'s robust transaction submission processes. This means that it will set the fee, manage the transaction sequence numbers, sign the transaction with your secret, and resubmit the transaction up to 10 times if `rippled` reports an initial error that can be solved automatically.
|
||||||
|
|
||||||
## Making Payments ##
|
## Payments ##
|
||||||
|
|
||||||
### Preparing a Payment ###
|
## Prepare Payment ##
|
||||||
|
|
||||||
__GET /v1/accounts/{:address}/payments/paths/{:destination_account}/{:destination_amount}__
|
__GET /v1/accounts/{:address}/payments/paths/{:destination_account}/{:destination_amount}__
|
||||||
|
|
||||||
[Try it! >](rest-api-tool.html#prepare-payment)
|
[Try it! >](rest-api-tool.html#prepare-payment)
|
||||||
|
|
||||||
To prepare a payment, you first make an HTTP `GET` call to the above endpoint. This will generate a list of possible payments between the two parties for the desired amount, taking into account the established trustlines between the two parties for the currency being transferred. You can then choose one of the returned payments, modify it if necessary (for example, to set slippage values or tags), and then submit the payment for processing.
|
Before you make a payment, it is necessary to figure out the possible ways in which that payment can be made. This method gets a list possible ways to make a payment, but it does not affect the network: consider it like getting quotes before actually making the payment.
|
||||||
|
|
||||||
|
You can then choose one of the returned payment objects, modify it as desired (for example, to set slippage values or tags), and then submit the payment for processing.
|
||||||
|
|
||||||
The following URL parameters are required by this API endpoint:
|
The following URL parameters are required by this API endpoint:
|
||||||
|
|
||||||
+ `address` *[required]* The Ripple address for the source account.
|
| Field | Value | Description |
|
||||||
+ `destination_account` *[required]* The Ripple address for the destination account.
|
|-------|-------|-------------|
|
||||||
+ `destination_amount` *[required]* The amount to be sent to the destination account. Note that this value uses `+` characters to separate the `value`, `currency` and `issuer` fields.
|
| `address` | String | The Ripple address for the account that would send the payment. |
|
||||||
+ For XRP, the format is: `0.1+XRP`
|
| `destination_account` | String | The Ripple address for the account that would receive the payment. |
|
||||||
|
| `destination_amount` | String ([URL-formatted Amount](#amounts-in-urls) | The amount that the destination account should receive. |
|
||||||
|
|
||||||
+ For other currencies, you need to include the Ripple address of the currency's issuer, like this: `0.1+USD+r...`
|
Optionally, you can also include the following as a query parameter:
|
||||||
|
|
||||||
Optionally, you can also include the following as a query string parameter:
|
| Field | Value | Description |
|
||||||
|
|-------|-------|-------------|
|
||||||
|
| `source_currencies` | Comma-separated list of source currencies. Each should be an [ISO 4217 currency code](http://www.xe.com/iso4217.php), or a `{:currency}+{:issuer}` string. | Filters possible payments to include only ones that spend the source account's balances in the specified currencies. If an issuer is not specified, include all issuances of that currency held by the sending account. |
|
||||||
|
|
||||||
`source_currencies` *[optional]* A comma-separated list of source currencies. This is used to filter the returned list of possible payments. Each source currency can be specified either as a currency code (eg, `USD`), or as a currency code and issuer (eg, `USD+r...`). If the issuer is not specified for a currency other than XRP, then the results will be limited to the specified currency, but any issuer for that currency will be included in the results.
|
This method effectively performs a [ripple_path_find](rippled-apis.html#ripple-path-find) and constructs a payment object for the paths it finds.
|
||||||
|
|
||||||
Note that this call is a wrapper around the [Ripple path-find](https://ripple.com/wiki/RPC_API#path_find) command, and returns an array of [`Payment`](#payment_object) objects, like this:
|
Response Body:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user