mirror of
https://github.com/silkimen/cordova-plugin-advanced-http.git
synced 2026-02-22 00:00:04 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9870dde50a | ||
|
|
ed08534cf3 | ||
|
|
ea15617fae | ||
|
|
87c915f7c0 | ||
|
|
43b65fa887 | ||
|
|
48445786b6 | ||
|
|
08ec3203e9 |
101
.github/workflows/ci.yml
vendored
101
.github/workflows/ci.yml
vendored
@@ -3,63 +3,70 @@ name: Cordova HTTP Plugin CI
|
||||
on: [push]
|
||||
|
||||
env:
|
||||
nodejs: '10.x'
|
||||
nodejs: "16.x"
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
|
||||
jobs:
|
||||
test-www-interface:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run WWW interface tests
|
||||
run: npm run testjs
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Run WWW interface tests
|
||||
run: npm run test:js
|
||||
|
||||
build-ios:
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run updatecert
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --ios --emulator
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run update:cert
|
||||
# need to find a solution for signing iOS App so we can build for device target instead simulator
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --ios --emulator
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/upload-browserstack.sh --ios
|
||||
# need to have an App for device target
|
||||
# - name: Run e2e tests
|
||||
# if: env.BROWSERSTACK_USERNAME != ''
|
||||
# run: scripts/test-app.sh --ios --device
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }}
|
||||
BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }}
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Install JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run updatecert
|
||||
- name: Add workaround for missing DX files in build-tools 31 (https://stackoverflow.com/a/68430992)
|
||||
run: ln -s $ANDROID_HOME/build-tools/31.0.0/d8 $ANDROID_HOME/build-tools/31.0.0/dx && ln -s $ANDROID_HOME/build-tools/31.0.0/lib/d8.jar $ANDROID_HOME/build-tools/31.0.0/lib/dx.jar
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --android --device
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/upload-browserstack.sh --android
|
||||
- name: Run e2e tests
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/test-app.sh --android --device
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install Node.js ${{ env.nodejs }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ env.nodejs }}
|
||||
- name: Install node modules
|
||||
run: npm ci
|
||||
- name: Install JDK 1.8
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 1.8
|
||||
- name: Update test cert for httpbin.org
|
||||
run: npm run update:cert
|
||||
- name: Add workaround for missing DX files in build-tools 32 (https://stackoverflow.com/a/68430992)
|
||||
run: ln -s $ANDROID_HOME/build-tools/32.0.0/d8 $ANDROID_HOME/build-tools/32.0.0/dx && ln -s $ANDROID_HOME/build-tools/32.0.0/lib/d8.jar $ANDROID_HOME/build-tools/32.0.0/lib/dx.jar
|
||||
- name: Build test app
|
||||
run: scripts/build-test-app.sh --android --device
|
||||
- name: Upload artifact to BrowserStack
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/upload-browserstack.sh --android
|
||||
- name: Run e2e tests
|
||||
if: env.BROWSERSTACK_USERNAME != ''
|
||||
run: scripts/test-app.sh --android --device
|
||||
|
||||
82
.travis.yml
82
.travis.yml
@@ -7,52 +7,52 @@ addons:
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- name: "iOS Build & Test"
|
||||
language: objective-c
|
||||
sudo: false
|
||||
os: osx
|
||||
osx_image: xcode12.5
|
||||
- name: "iOS Build & Test"
|
||||
language: objective-c
|
||||
sudo: false
|
||||
os: osx
|
||||
osx_image: xcode12.5
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
nvm use 14
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
nvm use 14
|
||||
|
||||
install:
|
||||
- npm install
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- npm run testjs &&
|
||||
npm run updatecert &&
|
||||
scripts/build-test-app.sh --ios --emulator &&
|
||||
scripts/upload-saucelabs.sh --ios &&
|
||||
scripts/test-app.sh --ios --emulator;
|
||||
script:
|
||||
- npm run test:js &&
|
||||
npm run update:cert &&
|
||||
scripts/build-test-app.sh --ios --emulator &&
|
||||
scripts/upload-saucelabs.sh --ios &&
|
||||
scripts/test-app.sh --ios --emulator;
|
||||
|
||||
- name: "Android Build & Test"
|
||||
language: android
|
||||
sudo : required
|
||||
- name: "Android Build & Test"
|
||||
language: android
|
||||
sudo: required
|
||||
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-28.0.3
|
||||
- android-28
|
||||
- extra-android-support
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-30.0.1
|
||||
- android-28
|
||||
- extra-android-support
|
||||
- extra-android-m2repository
|
||||
- extra-google-m2repository
|
||||
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - &&
|
||||
sudo apt-get install -y nodejs
|
||||
- yes | sdkmanager --update
|
||||
before_install:
|
||||
- export LANG=en_US.UTF-8 &&
|
||||
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - &&
|
||||
sudo apt-get install -y nodejs
|
||||
- yes | sdkmanager --update
|
||||
|
||||
install:
|
||||
- npm install
|
||||
install:
|
||||
- npm install
|
||||
|
||||
script:
|
||||
- npm run testjs &&
|
||||
npm run updatecert &&
|
||||
scripts/build-test-app.sh --android --emulator &&
|
||||
scripts/upload-saucelabs.sh --android &&
|
||||
scripts/test-app.sh --android --emulator;
|
||||
script:
|
||||
- npm run test:js &&
|
||||
npm run update:cert &&
|
||||
scripts/build-test-app.sh --android --emulator &&
|
||||
scripts/upload-saucelabs.sh --android &&
|
||||
scripts/test-app.sh --android --emulator;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
# 3.3.0
|
||||
|
||||
- Feature #451: expose response object on `downloadFile()` (thanks to @MobisysGmbH)
|
||||
|
||||
# 3.2.2
|
||||
|
||||
- Fixed #438: requests not working correctly on browser platform because request options are not processed correctly
|
||||
|
||||
46
README.md
46
README.md
@@ -414,21 +414,32 @@ cordova.plugin.http.uploadFile("https://google.com/", {
|
||||
```
|
||||
|
||||
### downloadFile<a name="downloadFile"></a>
|
||||
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry).
|
||||
Downloads a file and saves it to the device. Takes a URL, parameters, headers, and a filePath. See [post](#post) documentation for details on what is returned on failure. On success this function returns a cordova [FileEntry object](http://cordova.apache.org/docs/en/3.3.0/cordova_file_file.md.html#FileEntry) as first and the response object as second parameter.
|
||||
|
||||
```js
|
||||
cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
cordova.plugin.http.downloadFile(
|
||||
"https://google.com/",
|
||||
{ id: '12', message: 'test' },
|
||||
{ Authorization: 'OAuth2: token' },
|
||||
'file:///somepicture.jpg',
|
||||
// success callback
|
||||
function(entry, response) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
}, function(response) {
|
||||
console.error(response.error);
|
||||
});
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
|
||||
// prints all header key/value pairs
|
||||
Object.keys(response.headers).forEach(function (key) {
|
||||
console.log(key, response.headers[key]);
|
||||
});
|
||||
},
|
||||
// error callback
|
||||
function(response) {
|
||||
console.error(response.error);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### abort<a name="abort"></a>
|
||||
@@ -445,12 +456,15 @@ If the request is still in progress, the request's `failure` callback will be in
|
||||
var requestId = cordova.plugin.http.downloadFile("https://google.com/", {
|
||||
id: '12',
|
||||
message: 'test'
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry) {
|
||||
}, { Authorization: 'OAuth2: token' }, 'file:///somepicture.jpg', function(entry, response) {
|
||||
// prints the filename
|
||||
console.log(entry.name);
|
||||
|
||||
// prints the filePath
|
||||
console.log(entry.fullPath);
|
||||
|
||||
// prints the status code
|
||||
console.log(response.status);
|
||||
}, function(response) {
|
||||
// if request was actually aborted, failure callback with status -8 will be invoked
|
||||
if(response.status === -8){
|
||||
@@ -512,7 +526,7 @@ First, install current package with `npm install` to fetch dev dependencies.
|
||||
|
||||
Then, to execute Javascript tests:
|
||||
```shell
|
||||
npm run testjs
|
||||
npm run test:js
|
||||
```
|
||||
|
||||
And, to execute E2E tests:
|
||||
@@ -523,8 +537,8 @@ And, to execute E2E tests:
|
||||
- run
|
||||
- updating client and server certificates, building test app, and running e2e tests
|
||||
```shell
|
||||
npm run testandroid
|
||||
npm run testios
|
||||
npm run test:android
|
||||
npm run test:ios
|
||||
```
|
||||
|
||||
## Contribute & Develop
|
||||
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.0",
|
||||
"engines": [
|
||||
{
|
||||
"name": "cordova",
|
||||
@@ -20,7 +20,7 @@
|
||||
"cordova": "10.0.0",
|
||||
"mocha": "8.2.0",
|
||||
"umd-tough-cookie": "2.4.3",
|
||||
"wd": "1.12.1",
|
||||
"wd": "1.14.0",
|
||||
"xml2js": "0.4.23"
|
||||
}
|
||||
},
|
||||
@@ -6376,13 +6376,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/wd": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.12.1.tgz",
|
||||
"integrity": "sha512-O99X8OnOgkqfmsPyLIRzG9LmZ+rjmdGFBCyhGpnsSL4MB4xzHoeWmSVcumDiQ5QqPZcwGkszTgeJvjk2VjtiNw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.14.0.tgz",
|
||||
"integrity": "sha512-X7ZfGHHYlQ5zYpRlgP16LUsvYti+Al/6fz3T/ClVyivVCpCZQpESTDdz6zbK910O5OIvujO23Ym2DBBo3XsQlA==",
|
||||
"dev": true,
|
||||
"engines": [
|
||||
"node"
|
||||
],
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"archiver": "^3.0.0",
|
||||
"async": "^2.0.0",
|
||||
@@ -11815,9 +11816,9 @@
|
||||
}
|
||||
},
|
||||
"wd": {
|
||||
"version": "1.12.1",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.12.1.tgz",
|
||||
"integrity": "sha512-O99X8OnOgkqfmsPyLIRzG9LmZ+rjmdGFBCyhGpnsSL4MB4xzHoeWmSVcumDiQ5QqPZcwGkszTgeJvjk2VjtiNw==",
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/wd/-/wd-1.14.0.tgz",
|
||||
"integrity": "sha512-X7ZfGHHYlQ5zYpRlgP16LUsvYti+Al/6fz3T/ClVyivVCpCZQpESTDdz6zbK910O5OIvujO23Ym2DBBo3XsQlA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"archiver": "^3.0.0",
|
||||
|
||||
22
package.json
22
package.json
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "cordova-plugin-advanced-http",
|
||||
"version": "3.2.2",
|
||||
"version": "3.3.0",
|
||||
"description": "Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning",
|
||||
"scripts": {
|
||||
"updatecert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
"buildbrowser": "./scripts/build-test-app.sh --browser",
|
||||
"buildandroid": "./scripts/build-test-app.sh --android --emulator",
|
||||
"buildios": "./scripts/build-test-app.sh --ios --emulator",
|
||||
"testandroid": "npm run updatecert && npm run buildandroid && ./scripts/test-app.sh --android --emulator",
|
||||
"testios": "npm run updatecert && npm run buildios && ./scripts/test-app.sh --ios --emulator",
|
||||
"testapp": "npm run testandroid && npm run testios",
|
||||
"testjs": "mocha ./test/js-specs.js",
|
||||
"test": "npm run testjs && npm run testapp",
|
||||
"update:cert": "node ./scripts/update-e2e-server-cert.js && node ./scripts/update-e2e-client-cert.js",
|
||||
"build:browser": "./scripts/build-test-app.sh --browser",
|
||||
"build:android": "./scripts/build-test-app.sh --android --emulator",
|
||||
"build:ios": "./scripts/build-test-app.sh --ios --emulator",
|
||||
"test:android": "npm run update:cert && npm run build:android && ./scripts/test-app.sh --android --emulator",
|
||||
"test:ios": "npm run update:cert && npm run build:ios && ./scripts/test-app.sh --ios --emulator",
|
||||
"test:app": "npm run test:android && npm run test:ios",
|
||||
"test:js": "mocha ./test/js-specs.js",
|
||||
"test": "npm run test:js && npm run test:app",
|
||||
"release": "npm run test && ./scripts/release.sh"
|
||||
},
|
||||
"cordova": {
|
||||
@@ -63,7 +63,7 @@
|
||||
"cordova": "10.0.0",
|
||||
"mocha": "8.2.0",
|
||||
"umd-tough-cookie": "2.4.3",
|
||||
"wd": "1.12.1",
|
||||
"wd": "1.14.0",
|
||||
"xml2js": "0.4.23"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.2.2">
|
||||
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0" xmlns:android="http://schemas.android.com/apk/res/android" id="cordova-plugin-advanced-http" version="3.3.0">
|
||||
<name>Advanced HTTP plugin</name>
|
||||
<description>
|
||||
Cordova / Phonegap plugin for communicating with HTTP servers using SSL pinning
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"cordova": "10.0.0",
|
||||
"cordova-android": "9.0.0",
|
||||
"cordova-android": "10.1.1",
|
||||
"cordova-browser": "6.0.0",
|
||||
"cordova-ios": "6.2.0",
|
||||
"cordova-plugin-device": "2.0.3",
|
||||
|
||||
@@ -52,6 +52,19 @@ const helpers = {
|
||||
xhr.open('GET', url);
|
||||
xhr.send();
|
||||
},
|
||||
readFileEntryAsText: function(fileEntry, onSuccess, onFail) {
|
||||
var reader = new FileReader();
|
||||
|
||||
reader.onerror = onFail;
|
||||
|
||||
reader.onloadend = function() {
|
||||
onSuccess(reader.result);
|
||||
};
|
||||
|
||||
fileEntry.file(function(file) {
|
||||
reader.readAsText(file);
|
||||
}, onFail);
|
||||
},
|
||||
writeToFile: function (done, fileName, content) {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function (directoryEntry) {
|
||||
directoryEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
|
||||
@@ -342,7 +355,7 @@ const tests = [
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
helpers.readFileEntryAsText(entry, function(content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -350,7 +363,7 @@ const tests = [
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
@@ -520,7 +533,7 @@ const tests = [
|
||||
cordova.plugin.http.setCookie('http://httpbin.org/get', 'mySecondCookie=mySecondValue');
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
helpers.readFileEntryAsText(entry, function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -528,7 +541,7 @@ const tests = [
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
@@ -697,7 +710,7 @@ const tests = [
|
||||
var targetPath = cordova.file.cacheDirectory + 'test.xml';
|
||||
|
||||
cordova.plugin.http.downloadFile(sourceUrl, {}, {}, targetPath, function (entry) {
|
||||
helpers.getWithXhr(function (content) {
|
||||
helpers.readFileEntryAsText(entry, function (content) {
|
||||
resolve({
|
||||
sourceUrl: sourceUrl,
|
||||
targetPath: targetPath,
|
||||
@@ -705,7 +718,7 @@ const tests = [
|
||||
name: entry.name,
|
||||
content: content
|
||||
});
|
||||
}, targetPath);
|
||||
}, reject);
|
||||
}, reject);
|
||||
},
|
||||
validationFunc: function (driver, result) {
|
||||
|
||||
@@ -64,7 +64,7 @@ const configs = {
|
||||
os_version: '14',
|
||||
project: 'HTTP Test App',
|
||||
autoWebview: true,
|
||||
app: 'HttpTestAppAndroid',
|
||||
app: 'HttpTestAppIos',
|
||||
'browserstack.networkLogs': false
|
||||
},
|
||||
browserstackAndroidDevice: {
|
||||
|
||||
@@ -343,7 +343,10 @@ module.exports = function init(global, jsUtil, cookieHandler, messages, base64,
|
||||
|
||||
function injectFileEntryHandler(cb) {
|
||||
return function (response) {
|
||||
cb(createFileEntry(response.file));
|
||||
var fileEntry = createFileEntry(response.file);
|
||||
response.file = fileEntry;
|
||||
response.data = fileEntry;
|
||||
cb(fileEntry, response);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user