mirror of
https://github.com/PrivateBin/PrivateBin.git
synced 2026-03-22 17:14:03 -04:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3ee624d3a | ||
|
|
1eee8032c7 | ||
|
|
f3fba1c0f5 | ||
|
|
a54a55a9a1 | ||
|
|
56f0ec7d52 | ||
|
|
208c922e04 | ||
|
|
4db0db320b | ||
|
|
772ab96364 | ||
|
|
dc2db9e622 | ||
|
|
6d559c3043 | ||
|
|
096a5118d1 | ||
|
|
5084ab7a07 | ||
|
|
fbe4ac5a6c | ||
|
|
4e21e980de | ||
|
|
a3243b3165 | ||
|
|
223ef02a43 | ||
|
|
c7da424ef9 | ||
|
|
e1c74457e5 | ||
|
|
1a6c3dd8d8 | ||
|
|
146b2513c1 | ||
|
|
5ec8a57c60 | ||
|
|
cd67d81ae3 | ||
|
|
218e68a604 | ||
|
|
9611991748 | ||
|
|
b613d9558c | ||
|
|
5523ba9a45 | ||
|
|
421342396f | ||
|
|
4e3b469c08 | ||
|
|
cb0edf4f2b | ||
|
|
a0f8e0a566 | ||
|
|
089652382a | ||
|
|
a1408601d9 | ||
|
|
3d4887e5f0 | ||
|
|
5c876df2ea | ||
|
|
fe4a64529a | ||
|
|
a25bfd4cce | ||
|
|
53639b913e | ||
|
|
9379a73632 | ||
|
|
efd6c3f8c4 | ||
|
|
51f68f0c78 | ||
|
|
ad2de3a135 | ||
|
|
ea7f1fca6e | ||
|
|
57b1890815 | ||
|
|
7bb913acdf | ||
|
|
25de89c954 | ||
|
|
950c0b56b4 | ||
|
|
070ae2e5ec | ||
|
|
239f6da73c | ||
|
|
257fc5d2b6 | ||
|
|
03d2291ec7 | ||
|
|
56f5b2386c | ||
|
|
d0e03e5167 | ||
|
|
0d2376cd88 | ||
|
|
df703dfe4b | ||
|
|
3ff3db72a1 | ||
|
|
a27cb0cffc | ||
|
|
ba25ab8fa9 | ||
|
|
d091382313 | ||
|
|
2a508cb7bf | ||
|
|
8516a3f4a4 | ||
|
|
2cc2cf0de7 | ||
|
|
e1e8618015 | ||
|
|
7fddefeb05 | ||
|
|
4e62e1f6ef | ||
|
|
405479642f | ||
|
|
fd82b937a9 | ||
|
|
d493ba7337 | ||
|
|
37ee3b1c7c | ||
|
|
7ab20c23e9 | ||
|
|
cc5c81afbc | ||
|
|
9fb7aee589 | ||
|
|
ba17e94c5e | ||
|
|
c3331070cb | ||
|
|
cc0b6e387a | ||
|
|
a80bd4e4ea | ||
|
|
b3b97b69c6 | ||
|
|
8427c1136c | ||
|
|
7cb1f8ca67 | ||
|
|
9a707e288a | ||
|
|
83a1de271a | ||
|
|
c264904f77 | ||
|
|
712715caba | ||
|
|
2985305dbb | ||
|
|
0581522414 | ||
|
|
dc8cb66adc | ||
|
|
54585549e0 | ||
|
|
d49be80ffb | ||
|
|
a17529c7e1 | ||
|
|
0a7a341fda | ||
|
|
d88945663e | ||
|
|
255fd4b12b | ||
|
|
43d162f452 | ||
|
|
9b07e3ff62 | ||
|
|
03e3e4fa06 |
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -46,4 +46,4 @@ jobs:
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
2
.github/workflows/snyk-scan.yml
vendored
2
.github/workflows/snyk-scan.yml
vendored
@@ -24,6 +24,6 @@ jobs:
|
||||
with:
|
||||
args: --sarif-file-output=snyk.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
@@ -41,7 +41,7 @@ jobs:
|
||||
key: ${{ runner.os }}-${{ env.extensions-cache-key }}
|
||||
|
||||
- name: Cache extensions
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.extcache.outputs.dir }}
|
||||
key: ${{ steps.extcache.outputs.key }}
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }}
|
||||
|
||||
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,5 +1,16 @@
|
||||
# PrivateBin version history
|
||||
|
||||
## 1.7.0 (2024-02-11)
|
||||
* ADDED: Translations for Romanian
|
||||
* ADDED: Detect and report on damaged pastes (#1218)
|
||||
* CHANGED: Ask for confirmation, before loading burn after reading pastes #1237
|
||||
* CHANGED: Focus on password input in modal dialog
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 3.0.8 & zlib 1.3.1
|
||||
* FIXED: Support more types of valid URLs for shorteners, incl. IDN ones (#1224)
|
||||
* FIXED: Email timezone buttons overlapping in some languages #1039
|
||||
* FIXED: Changing language mangles URL #1191
|
||||
* FIXED: Needless reload when visiting default URL
|
||||
|
||||
## 1.6.2 (2023-12-15)
|
||||
* FIXED: English not selectable when `languageselection` enabled (#1208)
|
||||
* FIXED: SRI mismatch due to cached file having changed (#1207)
|
||||
|
||||
@@ -65,3 +65,4 @@
|
||||
* jaideejung007 - Thai
|
||||
* Nicolas Le Gall - Japanese
|
||||
* lazerns - Arabic
|
||||
* Edward205 - Romanian
|
||||
|
||||
4
Makefile
4
Makefile
@@ -1,7 +1,7 @@
|
||||
.PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help
|
||||
|
||||
CURRENT_VERSION = 1.6.2
|
||||
VERSION ?= 1.6.3
|
||||
CURRENT_VERSION = 1.7.0
|
||||
VERSION ?= 1.7.1
|
||||
VERSION_FILES = index.php bin/ cfg/ *.md doc/Installation.md css/ i18n/ img/ js/package.json js/privatebin.js lib/ Makefile tpl/ tst/
|
||||
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
|
||||
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# [](https://privatebin.info/)
|
||||
|
||||
*Current version: 1.6.2*
|
||||
*Current version: 1.7.0*
|
||||
|
||||
**PrivateBin** is a minimalist, open source online
|
||||
[pastebin](https://en.wikipedia.org/wiki/Pastebin)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.6.2 | :heavy_check_mark: |
|
||||
| < 1.6.2 | :x: |
|
||||
| 1.7.0 | :heavy_check_mark: |
|
||||
| < 1.7.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Configuration;
|
||||
use PrivateBin\Data\AbstractData;
|
||||
use PrivateBin\Model\Paste;
|
||||
@@ -195,6 +196,7 @@ EOT, PHP_EOL;
|
||||
{
|
||||
$counters = array(
|
||||
'burn' => 0,
|
||||
'damaged' => 0,
|
||||
'discussion' => 0,
|
||||
'expired' => 0,
|
||||
'md' => 0,
|
||||
@@ -217,7 +219,12 @@ EOT, PHP_EOL;
|
||||
|
||||
echo "Total:\t\t\t{$counters['total']}", PHP_EOL;
|
||||
foreach ($ids as $pasteid) {
|
||||
$paste = $this->_store->read($pasteid);
|
||||
try {
|
||||
$paste = $this->_store->read($pasteid);
|
||||
} catch (Exception $e) {
|
||||
echo "Error reading paste {$pasteid}: ", $e->getMessage(), PHP_EOL;
|
||||
++$counters['damaged'];
|
||||
}
|
||||
++$counters['progress'];
|
||||
|
||||
if (
|
||||
@@ -271,6 +278,9 @@ Plain Text:\t\t{$counters['plain']}
|
||||
Source Code:\t\t{$counters['syntax']}
|
||||
Markdown:\t\t{$counters['md']}
|
||||
EOT, PHP_EOL;
|
||||
if ($counters['damaged'] > 0) {
|
||||
echo "Damaged:\t\t{$counters['damaged']}", PHP_EOL;
|
||||
}
|
||||
if ($counters['unknown'] > 0) {
|
||||
echo "Unknown format:\t\t{$counters['unknown']}", PHP_EOL;
|
||||
}
|
||||
@@ -305,7 +315,12 @@ EOT, PHP_EOL;
|
||||
}
|
||||
|
||||
if ($this->_option('p', 'purge') !== null) {
|
||||
$this->_store->purge(PHP_INT_MAX);
|
||||
try {
|
||||
$this->_store->purge(PHP_INT_MAX);
|
||||
} catch (Exception $e) {
|
||||
echo 'Error purging pastes: ', $e->getMessage(), PHP_EOL,
|
||||
'Run the statistics to find damaged paste IDs and either delete them or restore them from backup.', PHP_EOL;
|
||||
}
|
||||
exit('purging of expired pastes concluded' . PHP_EOL);
|
||||
}
|
||||
|
||||
|
||||
66
composer.lock
generated
66
composer.lock
generated
@@ -316,16 +316,16 @@
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.17.1",
|
||||
"version": "v4.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d"
|
||||
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||
"reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
|
||||
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -366,9 +366,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0"
|
||||
},
|
||||
"time": "2023-08-13T19:53:39+00:00"
|
||||
"time": "2023-12-10T21:03:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
@@ -483,23 +483,23 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.29",
|
||||
"version": "9.2.30",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76"
|
||||
"reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76",
|
||||
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089",
|
||||
"reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"nikic/php-parser": "^4.15",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=7.3",
|
||||
"phpunit/php-file-iterator": "^3.0.3",
|
||||
"phpunit/php-text-template": "^2.0.2",
|
||||
@@ -549,7 +549,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -557,7 +557,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-09-19T04:57:46+00:00"
|
||||
"time": "2023-12-22T06:47:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
@@ -802,16 +802,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "9.6.15",
|
||||
"version": "9.6.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1"
|
||||
"reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1",
|
||||
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3767b2c56ce02d01e3491046f33466a1ae60a37f",
|
||||
"reference": "3767b2c56ce02d01e3491046f33466a1ae60a37f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -885,7 +885,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15"
|
||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.16"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -901,7 +901,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-12-01T16:55:19+00:00"
|
||||
"time": "2024-01-19T07:03:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
@@ -1146,20 +1146,20 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/complexity.git",
|
||||
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88"
|
||||
"reference": "25f207c40d62b8b7aa32f5ab026c53561964053a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88",
|
||||
"reference": "739b35e53379900cc9ac327b2147867b8b6efd88",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a",
|
||||
"reference": "25f207c40d62b8b7aa32f5ab026c53561964053a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.7",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -1191,7 +1191,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/complexity",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/complexity/issues",
|
||||
"source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2"
|
||||
"source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1199,7 +1199,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-26T15:52:27+00:00"
|
||||
"time": "2023-12-22T06:19:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/diff",
|
||||
@@ -1473,20 +1473,20 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/lines-of-code",
|
||||
"version": "1.0.3",
|
||||
"version": "1.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/lines-of-code.git",
|
||||
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc"
|
||||
"reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc",
|
||||
"reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5",
|
||||
"reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nikic/php-parser": "^4.6",
|
||||
"nikic/php-parser": "^4.18 || ^5.0",
|
||||
"php": ">=7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
@@ -1518,7 +1518,7 @@
|
||||
"homepage": "https://github.com/sebastianbergmann/lines-of-code",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3"
|
||||
"source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1526,7 +1526,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-28T06:42:11+00:00"
|
||||
"time": "2023-12-22T06:20:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/object-enumerator",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
body {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
/* When there is no script at all other */
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
|
||||
|
||||
@@ -201,7 +201,7 @@ CREATE INDEX parent ON prefix_comment(pasteid);
|
||||
CREATE TABLE prefix_config (
|
||||
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
|
||||
);
|
||||
INSERT INTO prefix_config VALUES('VERSION', '1.6.2');
|
||||
INSERT INTO prefix_config VALUES('VERSION', '1.7.0');
|
||||
```
|
||||
|
||||
In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns
|
||||
@@ -213,11 +213,25 @@ to be `CLOB` and not `BLOB` or `MEDIUMBLOB`, the `id` column in the `config`
|
||||
table needs to be `VARCHAR2(16)` and the `meta` column in the `paste` table
|
||||
and the `value` column in the `config` table need to be `VARCHAR2(4000)`.
|
||||
|
||||
### Cloud Storage Backends
|
||||
|
||||
Due to the large size of the respective cloud SDKs required for these, we didn't
|
||||
include these in the `vendor` directory shipped in our release archives. To use
|
||||
these in your manual installation, you will need [composer installed](https://getcomposer.org/)
|
||||
and require the used library (see instructions below).
|
||||
|
||||
This is not required if using the dedicated container images that have these SDKs
|
||||
preinstalled.
|
||||
|
||||
#### Using Google Cloud Storage
|
||||
If you want to deploy PrivateBin in a serverless manner in the Google Cloud, you
|
||||
can choose the `GoogleCloudStorage` as backend. To use this backend, you create
|
||||
a GCS bucket and specify the name as the model option `bucket`. Alternatively,
|
||||
you can set the name through the environment variable `PRIVATEBIN_GCS_BUCKET`.
|
||||
can choose the `GoogleCloudStorage` as backend.
|
||||
|
||||
To use this backend, you first have to install the SDK from the installation
|
||||
directory of PrivateBin: `composer require google/cloud-storage`
|
||||
|
||||
You have to create a GCS bucket and specify the name as the model option `bucket`.
|
||||
Alternatively, you can set the name through the environment variable `PRIVATEBIN_GCS_BUCKET`.
|
||||
|
||||
The default prefix for pastes stored in the bucket is `pastes`. To change the
|
||||
prefix, specify the option `prefix`.
|
||||
@@ -226,15 +240,15 @@ Google Cloud Storage buckets may be significantly slower than a `FileSystem` or
|
||||
`Database` backend. The big advantage is that the deployment on Google Cloud
|
||||
Platform using Google Cloud Run is easy and cheap.
|
||||
|
||||
To use the Google Cloud Storage backend you have to install the suggested
|
||||
library using the command `composer require google/cloud-storage`.
|
||||
|
||||
#### Using S3 Storage
|
||||
Similar to Google Cloud Storage, you can choose S3 as storage backend. It uses
|
||||
the AWS SDK for PHP, but can also talk to a Rados gateway as part of a CEPH
|
||||
cluster. To use this backend, you first have to install the SDK in the
|
||||
document root of PrivateBin: `composer require aws/aws-sdk-php`. You have to
|
||||
create the S3 bucket on the CEPH cluster before using the S3 backend.
|
||||
the AWS SDK for PHP, but can also talk to a Rados gateway as part of a Ceph
|
||||
cluster.
|
||||
|
||||
To use this backend, you first have to install the SDK from the installation
|
||||
directory of PrivateBin: `composer require aws/aws-sdk-php`
|
||||
|
||||
You have to create an S3 bucket on the Ceph cluster before using the S3 backend.
|
||||
|
||||
In the `[model]` section of cfg/conf.php, set `class` to `S3Storage`.
|
||||
|
||||
@@ -256,7 +270,7 @@ data beneath this prefix.
|
||||
For AWS, you have to provide at least `region`, `bucket`, `accesskey`, and
|
||||
`secretkey`.
|
||||
|
||||
For CEPH, follow this example:
|
||||
For Ceph, follow this example:
|
||||
|
||||
```
|
||||
region = ""
|
||||
|
||||
44
doc/Release.md
Normal file
44
doc/Release.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Release
|
||||
|
||||
## Overview of Supply-Chain Security
|
||||
|
||||
As of the PrivateBin 1.0 release we [cryptographically sign](https://git-scm.com/book/uz/v2/Git-Tools-Signing-Your-Work) our git commits and tags, so that you can verify we actually developed the software. Later, we also [started signing the release archives on GitHub](https://github.com/PrivateBin/PrivateBin/issues/219) and retroactively signed all releases from 1.0 forward.
|
||||
|
||||
Since [release 1.6.2](https://github.com/PrivateBin/PrivateBin/releases/tag/1.6.2) our release assets additionally also are [verified with the SLSA (Supply-chain Levels for Software Artifacts) framework](https://slsa.dev/), providing an in-toto manifest of the release archive.
|
||||
|
||||
This achieves the following:
|
||||
1. It ensures no maintainer has gone rogue and has modified/tampered with the source code before “building” the release.
|
||||
2. It ensures the release is build exactly according to the source as defined by the branch that was used for the release.
|
||||
This includes the workflow file defining how the release is done itself.
|
||||
3. Our release should achieve [SLSA build level 3](https://slsa.dev/spec/v1.0/levels#build-l3) as it [runs on GitHub](https://slsa.dev/spec/v1.0/threats). Some more properties [are thus achieved](https://slsa.dev/spec/v1.0/threats).
|
||||
|
||||
For more information [see the corresponding issue](https://github.com/PrivateBin/PrivateBin/issues/1169) and [the GitHub workflow file](/.github/workflows/release.yml).
|
||||
|
||||
## Reproducible builds
|
||||
|
||||
All releases `.tar.gz` and `.zip` archives since 1.0 come with corresponding `.asc` signatures that can be used to confirm the authenticity of the fact that the release has been issued by a PrivateBin maintainer.
|
||||
|
||||
This uses traditional [PGP](https://en.wikipedia.org/wiki/Pretty_Good_Privacy) signatures.
|
||||
|
||||
## Verification
|
||||
|
||||
You can use the gpg signatures for verifying the reproducibility and that a maintainer in posession with that PGP private key created the release with that content:
|
||||
|
||||
```
|
||||
$ gpg2 --verify 1.6.2.tar.gz.asc
|
||||
gpg: assuming signed data in '1.6.2.tar.gz'
|
||||
gpg: Signature made Fri Dec 15 06:21:08 2023 UTC
|
||||
gpg: using RSA key 28CA7C964938EA5C1481D42AE11B7950E9E183DB
|
||||
gpg: Good signature from "PrivateBin release (solely used for signing releases)" [unknown]
|
||||
gpg: WARNING: This key is not certified with a trusted signature!
|
||||
gpg: There is no indication that the signature belongs to the owner.
|
||||
Primary key fingerprint: 28CA 7C96 4938 EA5C 1481 D42A E11B 7950 E9E1 83DB
|
||||
```
|
||||
|
||||
For a more step-by-step guide in detail [see this FAQ](https://github.com/PrivateBin/PrivateBin/wiki/FAQ#how-can-i-securely-clonedownload-your-project).
|
||||
|
||||
SLSA verification can be performed using the [SLSA verifier](https://github.com/slsa-framework/slsa-verifier#verification-of-slsa-provenance).
|
||||
|
||||
## Release process
|
||||
|
||||
The release process is outlined in the [release checklist](https://github.com/PrivateBin/PrivateBin/wiki/Release-Checklist). The key manual steps are performed using a [Makefile](https://github.com/PrivateBin/PrivateBin/blob/master/Makefile#L31-L43) and using a [shell script](https://github.com/rugk/gittools/blob/master/signrelease.sh).
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "عنوان IP الخاص بك غير مصرح له بإنشاء لصُق.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "محاولة تقصير عنوان URL لا يشير إلى خادمنا.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "خطأ في الاتصال بـ YOURLS. ربما تكون هناك مشكلة في التضبيط، مثل \"apiurl\" أو \"التوقيع\" الخاطئ أو المفقود.",
|
||||
"Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS."
|
||||
"Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "U vostru indirizzu IP ùn hè micca auturizatu à creà l’appiccichi.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Pruvate d’ammuzzà un indirizzu web chì ùn punta micca versu a vostra instanza.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Sbagliu à a chjama di YOURLS. Seria forse una cunfigurazione gattiva, tale una \"apiurl\" o \"signature\" falsa o assente.",
|
||||
"Error parsing YOURLS response.": "Sbagliu durante l’analisa di a risposta di YOURLS."
|
||||
"Error parsing YOURLS response.": "Sbagliu durante l’analisa di a risposta di YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Si pò affissà l’appiccichi « Squassà dopu a lettura » solu dopu u so caricamentu. Vulete aprelu subitu ?",
|
||||
"Yes, load it": "Iè, caricatelu"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Vaše IP adresa nemá oprávnění k vytvoření vložení.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Deine IP ist nicht berechtigt, Texte zu erstellen.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Versuch eine URL zu verkürzen, die nicht auf unsere Instanz zeigt.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Fehler beim Aufruf von YOURLS. Wahrscheinlich ein Konfigurationsproblem, wie eine falsche oder fehlende \"apiurl\" oder \"signature\".",
|
||||
"Error parsing YOURLS response.": "Fehler beim Verarbeiten der YOURLS-Antwort."
|
||||
"Error parsing YOURLS response.": "Fehler beim Verarbeiten der YOURLS-Antwort.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Texte des \"Einmal\"-Typs können nach dem Herunterladen nur einmal angezeigt werden. Möchtest Du ihn jetzt öffnen?",
|
||||
"Yes, load it": "Ja, jetzt öffnen"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Η IP σας δεν επιτρέπεται να δημιουργεί επικολλήσεις.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Tu IP no está autorizada para crear contenido.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Intentando acortar una URL que no apunta a nuestra instancia.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
10
i18n/et.json
10
i18n/et.json
@@ -211,8 +211,10 @@
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kirja nägemiseks külasta seda linki. Teistele URL-i andmine lubab ka neil ligi pääseda kirjale.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL-i lühendaja võib paljastada sinu dekrüpteerimisvõtme URL-is.",
|
||||
"Save paste": "Salvesta kleebe",
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Your IP is not authorized to create pastes.": "Su IP-l ei ole lubatud kleepeid luua.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Püüame lühendada URL-i, mis ei viita meie instantsile.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Viga YOURLS-i kutsumisel. Tõenäoliselt konfiguratsiooniprobleem, näiteks vale või puuduv \"apiurl\" või \"signature\".",
|
||||
"Error parsing YOURLS response.": "Viga YOURLS vastuse parsimisel.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "IP:llesi ei ole annettu oikeutta luoda pasteja.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Yritetään lyhentää URL-osoite, joka ei osoita meidän instanssiiin.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Virhe kutsuttaessa YOURLS. Luultavasti asetusongelma kuten väärä tai puuttuuva \"apiurl\" tai \"signature\".",
|
||||
"Error parsing YOURLS response.": "Virhe jäsennettäessä YOURLS-vastausta."
|
||||
"Error parsing YOURLS response.": "Virhe jäsennettäessä YOURLS-vastausta.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -212,7 +212,9 @@
|
||||
"URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.",
|
||||
"Save paste": "Sauver le paste",
|
||||
"Your IP is not authorized to create pastes.": "Votre adresse IP n'est pas autorisée à créer des pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Essayer de raccourcir une URL qui ne pointe pas vers notre instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Erreur lors de l'appel de YOURLS. Peut-être un problème de configuration, comme \"apiurl\" ou \"signature\" manquant.",
|
||||
"Error parsing YOURLS response.": "Erreur d'analyse de la réponse YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Effacer après la lecture des paste ne peut être affiché qu'une seule fois lors du chargement. Voulez-vous l'ouvrir maintenant ?",
|
||||
"Yes, load it": "Oui, chargez-le"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Il tuo IP non è autorizzato a creare dei messaggi.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Tantativo in corso di accorciare un URL che non punta alla nostra istanza.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Errore nella chiamata a YOURLS. Probabilmente un problema di configurazione, come un \"apiurl\" o una \"signature\" sbagliati o mancanti.",
|
||||
"Error parsing YOURLS response.": "Errore nell'analizzare la risposta YOURLS."
|
||||
"Error parsing YOURLS response.": "Errore nell'analizzare la risposta YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Messaggi di tipo Distruggi-dopo-lettura piovono essere visualizzata solo una volta al caricamento. Vuoi aprirle ora?",
|
||||
"Yes, load it": "Sì, caricalo"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "あなたのIPアドレスにはペーストを作成する権限がありません。",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "このインスタンスを指していないURLを短縮しようとしています。",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "YOURLSの呼び出し中にエラーが発生しました。\"apiurl\"または\"signature\"等の設定に問題がある可能性があります。",
|
||||
"Error parsing YOURLS response.": "YOURLSレスポンスの解析中にエラーが発生しました。"
|
||||
"Error parsing YOURLS response.": "YOURLSレスポンスの解析中にエラーが発生しました。",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Jūsų IP adresas neturi įgaliojimų kurti įdėjimų.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Bandoma sutrumpinti URL adresą, kuris nenurodo į mūsų egzempliorių.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Klaida iškviečiant YOURLS. Tikriausiai, konfigūracijos klaida, pavyzdžiui, neteisingi „apiurl“ ar „signature“, arba jų nėra.",
|
||||
"Error parsing YOURLS response.": "Klaida nagrinėjant YOURLS atsaką."
|
||||
"Error parsing YOURLS response.": "Klaida nagrinėjant YOURLS atsaką.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Je IP-adres is niet gemachtigd om pastes te maken.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Proberen om een URL te verkorten dat niet naar ons systeem wijst.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Foutmelding ophalen YOURLS. Waarschijnlijk een configuratiefout, zoals een verkeerde of missende \"apiurl\" of \"signature\".",
|
||||
"Error parsing YOURLS response.": "Foutmelding bij parsen van YOURLS respons."
|
||||
"Error parsing YOURLS response.": "Foutmelding bij parsen van YOURLS respons.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Vòstra adreça IP a pas l’autorizacion de crear de tèxtes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Ensag d’abracar una URL que mena pas a nòstra instància.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error en cridant YOURLS. Es probablament un problèma de configuracion, quicòm coma « apirul » o « signature » marrit o absent.",
|
||||
"Error parsing YOURLS response.": "Error d'analisi de la responsa YOURLS."
|
||||
"Error parsing YOURLS response.": "Error d'analisi de la responsa YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Seu IP não está autorizado a criar cópias.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Tentando encurtar uma URL que não aponta para a nossa instância.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
220
i18n/ro.json
Normal file
220
i18n/ro.json
Normal file
@@ -0,0 +1,220 @@
|
||||
{
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s este un pastebin online, minimalist și cu sursă deschisă unde serverul are zero acces la datele introduse. Datele sunt criptate/decriptate %sîn browser%s folosind AES cu 256 de biți.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "Mai multe informații pe <a href=\"https://privatebin.info/\">pagina proiectului</a>.",
|
||||
"Because ignorance is bliss": "Ignoranța este o binecuvântare",
|
||||
"Paste does not exist, has expired or has been deleted.": "Paste-ul nu există, a expirat sau a fost șters.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s necesită php %s sau mai nou pentru a funcționa. Scuze.",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s necesită ca secțiunea de configurare [%s] să fie prezentă în fișierul de configurare.",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"Vă rugăm să așteptați %d secundă între fiecare postare",
|
||||
"Vă rugăm să așteptați %d secunde între fiecare postare",
|
||||
"Vă rugăm să așteptați %d de secunde între fiecare postare",
|
||||
"Please wait %d seconds between each post. (3rd plural)",
|
||||
"Please wait %d seconds between each post. (4th plural)",
|
||||
"Please wait %d seconds between each post. (5th plural)"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "Paste-ul este limitat la %s de date criptate.",
|
||||
"Invalid data.": "Date invalide.",
|
||||
"You are unlucky. Try again.": "Ați avut ghinion. Încercați din nou.",
|
||||
"Error saving comment. Sorry.": "Eroare la salvarea comentariului. Ne pare rău.",
|
||||
"Error saving paste. Sorry.": "Eroare la salvarea paste-ului. Ne pare rău.",
|
||||
"Invalid paste ID.": "ID paste invalid.",
|
||||
"Paste is not of burn-after-reading type.": "Paste-ul nu se șterge după citire.",
|
||||
"Wrong deletion token. Paste was not deleted.": "Token de ștergere incorect. Paste-ul nu a fost șters.",
|
||||
"Paste was properly deleted.": "Paste-ul a fost șters cu succes.",
|
||||
"JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript este necesar pentru ca %s să funcționeze. Ne cerem scuze pentru neplăceri.",
|
||||
"%s requires a modern browser to work.": "%s necesită un browser modern pentru a funcționa.",
|
||||
"New": "Nou",
|
||||
"Send": "Trimiteți",
|
||||
"Clone": "Clonați",
|
||||
"Raw text": "Text brut",
|
||||
"Expires": "Expiră",
|
||||
"Burn after reading": "Ștergere după citire",
|
||||
"Open discussion": "Deschideți discuția",
|
||||
"Password (recommended)": "Parolă (recomandată)",
|
||||
"Discussion": "Discuție",
|
||||
"Toggle navigation": "Comutați navigarea",
|
||||
"%d seconds": [
|
||||
"%d secundă",
|
||||
"%d secunde",
|
||||
"%d de secunde",
|
||||
"%d seconds (3rd plural)",
|
||||
"%d seconds (4th plural)",
|
||||
"%d seconds (5th plural)"
|
||||
],
|
||||
"%d minutes": [
|
||||
"%d minut",
|
||||
"%d minute",
|
||||
"%d de minute",
|
||||
"%d minutes (3rd plural)",
|
||||
"%d minutes (4th plural)",
|
||||
"%d minutes (5th plural)"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d oră",
|
||||
"%d ore",
|
||||
"%d de ore",
|
||||
"%d hours (3rd plural)",
|
||||
"%d hours (4th plural)",
|
||||
"%d hours (5th plural)"
|
||||
],
|
||||
"%d days": [
|
||||
"%d zi",
|
||||
"%d zile",
|
||||
"%d de zile",
|
||||
"%d days (3rd plural)",
|
||||
"%d days (4th plural)",
|
||||
"%d days (5th plural)"
|
||||
],
|
||||
"%d weeks": [
|
||||
"%d week (singular)",
|
||||
"%d săptămână",
|
||||
"%d săptămâni",
|
||||
"%d de săptămâni",
|
||||
"%d weeks (4th plural)",
|
||||
"%d weeks (5th plural)"
|
||||
],
|
||||
"%d months": [
|
||||
"%d lună",
|
||||
"%d luni",
|
||||
"%d de luni",
|
||||
"%d months (3rd plural)",
|
||||
"%d months (4th plural)",
|
||||
"%d months (5th plural)"
|
||||
],
|
||||
"%d years": [
|
||||
"%d an",
|
||||
"%d ani",
|
||||
"%d de ani",
|
||||
"%d years (3rd plural)",
|
||||
"%d years (4th plural)",
|
||||
"%d years (5th plural)"
|
||||
],
|
||||
"Never": "Niciodată",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Notă: Acesta este un serviciu de test: Datele ar putea fi șterse oricând. Vor muri niște pisicuțe dacă abuzați acest serviciu.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"Acest document va expira în %d secundă.",
|
||||
"Acest document va expira în %d secunde.",
|
||||
"Acest document va expira în %d de secunde.",
|
||||
"This document will expire in %d seconds. (3rd plural)",
|
||||
"This document will expire in %d seconds. (4th plural)",
|
||||
"This document will expire in %d seconds. (5th plural)"
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"This document will expire in %d minute. (singular)",
|
||||
"This document will expire in %d minutes. (1st plural)",
|
||||
"This document will expire in %d minutes. (2nd plural)",
|
||||
"This document will expire in %d minutes. (3rd plural)",
|
||||
"This document will expire in %d minutes. (4th plural)",
|
||||
"This document will expire in %d minutes. (5th plural)"
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"Acest document va expira în %d oră.",
|
||||
"Acest document va expira în %d ore.",
|
||||
"Acest document va expira în %d de ore.",
|
||||
"This document will expire in %d hours. (3rd plural)",
|
||||
"This document will expire in %d hours. (4th plural)",
|
||||
"This document will expire in %d hours. (5th plural)"
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"Acest document va expira în %d zi.",
|
||||
"Acest document va expira în %d zile.",
|
||||
"Acest document va expira în %d de zile.",
|
||||
"This document will expire in %d days. (3rd plural)",
|
||||
"This document will expire in %d days. (4th plural)",
|
||||
"This document will expire in %d days. (5th plural)"
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"Acest document va expira în %d lună.",
|
||||
"Acest document va expira în %d luni.",
|
||||
"Acest document va expira în %d de luni.",
|
||||
"This document will expire in %d months. (3rd plural)",
|
||||
"This document will expire in %d months. (4th plural)",
|
||||
"This document will expire in %d months. (5th plural)"
|
||||
],
|
||||
"Please enter the password for this paste:": "Va rugăm să introduceți parola pentru acest paste:",
|
||||
"Could not decrypt data (Wrong key?)": "Nu s-au putut decripta datele (Cheie gresită?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "Nu s-a putut șterge paste-ul, nu a fost stocat în modul de ștergere după citire.",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "NUMAI PENTRU OCHII DVS. Nu închideți această fereastră, acest mesaj nu poate fi afișat din nou.",
|
||||
"Could not decrypt comment; Wrong key?": "Nu s-a putut decripta comentariul; Cheie greșită?",
|
||||
"Reply": "Răspundeți",
|
||||
"Anonymous": "Anonim",
|
||||
"Avatar generated from IP address": "Avatar generat din adresa IP",
|
||||
"Add comment": "Adăugați un comentariu",
|
||||
"Optional nickname…": "Poreclă opțională…",
|
||||
"Post comment": "Postați comentariul",
|
||||
"Sending comment…": "Se trimite comentariul…",
|
||||
"Comment posted.": "Comentariul a fost postat.",
|
||||
"Could not refresh display: %s": "Nu s-a putut actualiza afișarea: %s",
|
||||
"unknown status": "stare necunoscută",
|
||||
"server error or not responding": "eroare de server sau nu răspunde",
|
||||
"Could not post comment: %s": "Nu s-a putut posta comentariul: %s",
|
||||
"Sending paste…": "Se trimite paste-ul…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Paste-ul dvs. este <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Apăsați [Ctrl]+[c] pentru a copia)</span>",
|
||||
"Delete data": "Ștergeți datele",
|
||||
"Could not create paste: %s": "Nu s-a putut crea paste-ul: %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Nu s-a putut decripta paste-ul: Cheia de decriptare lipsește din URL (Ați folosit un redirector sau un scurtător de URL care a tăiat o parte din URL?)",
|
||||
"B": "B",
|
||||
"KiB": "KiB",
|
||||
"MiB": "MiB",
|
||||
"GiB": "GiB",
|
||||
"TiB": "TiB",
|
||||
"PiB": "PiB",
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"Format": "Formatare",
|
||||
"Plain Text": "Text neformatat",
|
||||
"Source Code": "Cod sursă",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "Descărcați fișierul atașat",
|
||||
"Cloned: '%s'": "S-a clonat: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "Fișierul clonat '%s' a fost atașat la acest paste.",
|
||||
"Attach a file": "Atașați un fișier",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "alternativ, trageți și plasați un fișier sau lipiți o imagine din clipboard",
|
||||
"File too large, to display a preview. Please download the attachment.": "Fișierul este prea mare pentru a afișa o previzualizare. Vă rugăm să descărcaţi fișierul.",
|
||||
"Remove attachment": "Eliminați fișierul atașat",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Browserul dvs. nu acceptă încărcarea fișierelor criptate. Vă rugăm să folosiți un browser mai nou.",
|
||||
"Invalid attachment.": "Fișier invalid.",
|
||||
"Options": "Opţiuni",
|
||||
"Shorten URL": "Scurtați URL-ul",
|
||||
"Editor": "Editor",
|
||||
"Preview": "Previzualizare",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s necesită ca PATH să se termine cu \"%s\". Vă rugăm să actualizați PATH în index.php.",
|
||||
"Decrypt": "Decriptare",
|
||||
"Enter password": "Introduceţi parola",
|
||||
"Loading…": "Se încarcă…",
|
||||
"Decrypting paste…": "Se decriptează paste-ul…",
|
||||
"Preparing new paste…": "Se pregătește un paste nou…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "În cazul în care acest mesaj nu dispare niciodată, vă rugăm să aruncaţi o privire la <a href=\"%s\">acest FAQ pentru informații de depanare</a>.",
|
||||
"+++ no paste text +++": "+++ fără text +++",
|
||||
"Could not get paste data: %s": "Nu s-a putut obține datele paste-ului: %s",
|
||||
"QR code": "Cod QR",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "Acest website folosește o conexiune HTTP nesigură! Vă rugăm să îl folosiți doar pentru teste.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "Pentru mai multe informații <a href=\"%s\">a se vedea secțiunea FAQ</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "Este posibil ca browserul dvs. să necesite o conexiune HTTPS pentru a suporta API-ul WebCrypto. Încercați să <a href=\"%s\">comutați la HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Browserul dvs. nu acceptă WebAssembly folosit pentru compresia zlib. Puteți crea documente necompresate, dar nu le puteți citi pe cele compresate.",
|
||||
"waiting on user to provide a password": "se așteaptă ca utilizatorul să furnizeze o parolă",
|
||||
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Nu s-au putut decripta datele. Ați introdus parola greșită? Reîncercați folosind butonul de sus.",
|
||||
"Retry": "Reîncercați",
|
||||
"Showing raw text…": "Se afișează textul brut…",
|
||||
"Notice:": "Observaţie:",
|
||||
"This link will expire after %s.": "Acest link va expira după %s.",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "Acest link poate fi accesat o singură dată, nu folosiți butonul înapoi sau reîmprospătare din browserul dvs.",
|
||||
"Link:": "Link:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "Destinatarul vă poate afla fusul dvs. orar, convertiți timpul în UTC?",
|
||||
"Use Current Timezone": "Se folosește fusul orar actual",
|
||||
"Convert To UTC": "Convertire la UTC",
|
||||
"Close": "Închideți",
|
||||
"Encrypted note on %s": "Notă criptată pe %s",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Accesați acest link pentru a vedea nota. Nota poate fi accesată de către oricine care are acest URL.",
|
||||
"URL shortener may expose your decrypt key in URL.": "Scurtătorul de URL ar putea să vă expună cheia de decriptare din URL.",
|
||||
"Save paste": "Salvați paste-ul",
|
||||
"Your IP is not authorized to create pastes.": "Adresa dvs. IP nu este autorizată să creeze paste-uri.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Încercarea de a scurta un URL care nu direcționează spre instanța noastră.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Eroare la apelarea YOURLS. Probabil o problemă de configurare, cum ar fi \"apiurl\" sau \"signature\" greșite sau lipsă.",
|
||||
"Error parsing YOURLS response.": "Eroare la analizarea răspunsului YOURLS.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Вашему IP адресу не разрешено создавать записи.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Vaša IP adresa nie je oprávnená vytvárať príspevky.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Pokúšate sa skrátiť adresu URL, ktorá neukazuje na túto inštanciu.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Your IP is not authorized to create pastes.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "IP ของคุณไม่ได้รับอนุญาตให้สร้างการฝากโค้ด",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "กำลังพยายามใช้เครื่องมือสร้างลิงก์ย่อ ที่ไม่ได้ชี้ไปที่อินสแตนซ์ของเรา",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "เกิดข้อผิดพลาดในการเรียก YOURLS อาจเป็นปัญหามาจากการกำหนดค่า เช่น \"apiurl\" หรือ \"signature\" ไม่ถูกต้องหรือขาดหายไป",
|
||||
"Error parsing YOURLS response.": "เกิดข้อผิดพลาดในการแยกวิเคราะห์การตอบสนองของ YOURLS"
|
||||
"Error parsing YOURLS response.": "เกิดข้อผิดพลาดในการแยกวิเคราะห์การตอบสนองของ YOURLS",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "IP adresinizin yazı oluşturmaya yetkisi yoktur.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "Вашому IP не дозволено створювати вставки.",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response."
|
||||
"Error parsing YOURLS response.": "Error parsing YOURLS response.",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"PrivateBin": "PrivateBin",
|
||||
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s 是一个极简、开源、对粘贴内容毫不知情的在线粘贴板,数据%s在浏览器内%s进行 AES-256 加密和解密。",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "更多信息请查看<a href=\"https://privatebin.info/\">项目主页</a>。",
|
||||
"Because ignorance is bliss": "因为无知是福",
|
||||
"Because ignorance is bliss": "以不知为幸",
|
||||
"Paste does not exist, has expired or has been deleted.": "粘贴内容不存在、已过期或已被删除。",
|
||||
"%s requires php %s or above to work. Sorry.": "抱歉,%s 需要 PHP %s 及以上版本才能运行。",
|
||||
"%s requires configuration section [%s] to be present in configuration file.": "%s 需要设置配置文件中的 [%s] 部分。",
|
||||
@@ -214,5 +214,7 @@
|
||||
"Your IP is not authorized to create pastes.": "您的 IP 无权创建粘贴。",
|
||||
"Trying to shorten a URL that isn't pointing at our instance.": "尝试缩短一个不指向我们实例的URL。",
|
||||
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "调用 YOURLS 时出错。可能是配置问题,例如“apiurl”或“signature”错误或缺失。",
|
||||
"Error parsing YOURLS response.": "解析 YOURLS 响应时出错。"
|
||||
"Error parsing YOURLS response.": "解析 YOURLS 响应时出错。",
|
||||
"Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?": "Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?",
|
||||
"Yes, load it": "Yes, load it"
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
// change this, if your php files and data is outside of your webservers document root
|
||||
|
||||
31
js/common.js
31
js/common.js
@@ -12,12 +12,12 @@ global.WebCrypto = require('@peculiar/webcrypto').Crypto;
|
||||
// application libraries to test
|
||||
global.$ = global.jQuery = require('./jquery-3.7.0');
|
||||
global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
|
||||
global.zlib = require('./zlib-1.2.13-1').zlib;
|
||||
global.zlib = require('./zlib-1.3.1').zlib;
|
||||
require('./prettify');
|
||||
global.prettyPrint = window.PR.prettyPrint;
|
||||
global.prettyPrintOne = window.PR.prettyPrintOne;
|
||||
global.showdown = require('./showdown-2.1.0');
|
||||
global.DOMPurify = require('./purify-3.0.6');
|
||||
global.DOMPurify = require('./purify-3.0.8');
|
||||
global.baseX = require('./base-x-4.0.0').baseX;
|
||||
global.Legacy = require('./legacy').Legacy;
|
||||
require('./bootstrap-3.4.1');
|
||||
@@ -37,7 +37,7 @@ var a2zString = ['a','b','c','d','e','f','g','h','i','j','k','l','m',
|
||||
})
|
||||
),
|
||||
schemas = ['ftp','http','https'],
|
||||
supportedLanguages = ['de', 'es', 'fr', 'it', 'no', 'pl', 'pt', 'oc', 'ru', 'sl', 'zh'],
|
||||
supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sk', 'sl', 'th', 'tr', 'uk', 'zh'],
|
||||
mimeTypes = ['image/png', 'application/octet-stream'],
|
||||
formats = ['plaintext', 'markdown', 'syntaxhighlighting'],
|
||||
mimeFile = fs.createReadStream('/etc/mime.types'),
|
||||
@@ -113,8 +113,8 @@ exports.jscBase64String = function() {
|
||||
};
|
||||
|
||||
// provides a random URL schema supported by the whatwg-url library
|
||||
exports.jscSchemas = function() {
|
||||
return jsc.elements(schemas);
|
||||
exports.jscSchemas = function(withFtp = true) {
|
||||
return jsc.elements(withFtp ? schemas : schemas.slice(1));
|
||||
};
|
||||
|
||||
// provides a random supported language string
|
||||
@@ -131,3 +131,24 @@ exports.jscMimeTypes = function() {
|
||||
exports.jscFormats = function() {
|
||||
return jsc.elements(formats);
|
||||
};
|
||||
|
||||
// provides random URLs
|
||||
exports.jscUrl = function(withFragment = true, withQuery = true) {
|
||||
let url = {
|
||||
schema: exports.jscSchemas(),
|
||||
address: jsc.nearray(exports.jscA2zString()),
|
||||
};
|
||||
if (withFragment) {
|
||||
url.fragment = jsc.string;
|
||||
}
|
||||
if(withQuery) {
|
||||
url.query = jsc.array(exports.jscQueryString());
|
||||
}
|
||||
return jsc.record(url);
|
||||
};
|
||||
|
||||
exports.urlToString = function (url) {
|
||||
return url.schema + '://' + url.address.join('') + '/' + (url.query ? '?' +
|
||||
encodeURI(url.query.join('').replace(/^&+|&+$/gm,'')) : '') +
|
||||
(url.fragment ? '#' + encodeURI(url.fragment) : '');
|
||||
};
|
||||
|
||||
4
js/package-lock.json
generated
4
js/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "privatebin",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.2",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "privatebin",
|
||||
"version": "1.5.2",
|
||||
"version": "1.6.2",
|
||||
"license": "zlib-acknowledgement",
|
||||
"devDependencies": {
|
||||
"@peculiar/webcrypto": "^1.1.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "privatebin",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.0",
|
||||
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
||||
"main": "privatebin.js",
|
||||
"directories": {
|
||||
|
||||
156
js/privatebin.js
156
js/privatebin.js
@@ -6,7 +6,7 @@
|
||||
* @see {@link https://github.com/PrivateBin/PrivateBin}
|
||||
* @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
|
||||
* @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
* @name PrivateBin
|
||||
* @namespace
|
||||
*/
|
||||
@@ -77,6 +77,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* URL fragment prefix requiring load confirmation
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const loadConfirmPrefix = '#-';
|
||||
|
||||
/**
|
||||
* CryptoData class
|
||||
*
|
||||
@@ -228,7 +235,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'\'': ''',
|
||||
'/': '/',
|
||||
'`': '`',
|
||||
'=': '='
|
||||
@@ -627,7 +634,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
* @prop {string[]}
|
||||
* @readonly
|
||||
*/
|
||||
const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ru', 'sk', 'sl', 'th', 'tr', 'uk', 'zh'];
|
||||
const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ro', 'ru', 'sk', 'sl', 'th', 'tr', 'uk', 'zh'];
|
||||
|
||||
/**
|
||||
* built in language
|
||||
@@ -836,6 +843,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
return n % 10 === 1 && n % 100 !== 11 ? 0 : ((n % 10 >= 2 && n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
case 'pl':
|
||||
return n === 1 ? 0 : (n % 10 >= 2 && n %10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
case 'ro':
|
||||
return n === 1 ? 0 : ((n === 0 || (n % 100 > 0 && n % 100 < 20)) ? 1 : 2);
|
||||
case 'ru':
|
||||
case 'uk':
|
||||
return n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2);
|
||||
@@ -1512,10 +1521,11 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
me.getPasteKey = function()
|
||||
{
|
||||
if (symmetricKey === null) {
|
||||
let newKey = window.location.hash.substring(1);
|
||||
if (newKey === '') {
|
||||
throw 'no encryption key given';
|
||||
let startPos = 1;
|
||||
if(window.location.hash.startsWith(loadConfirmPrefix)) {
|
||||
startPos = loadConfirmPrefix.length;
|
||||
}
|
||||
let newKey = window.location.hash.substring(startPos);
|
||||
|
||||
// Some web 2.0 services and redirectors add data AFTER the anchor
|
||||
// (such as &utm_source=...). We will strip any additional data.
|
||||
@@ -1524,6 +1534,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
{
|
||||
newKey = newKey.substring(0, ampersandPos);
|
||||
}
|
||||
if (newKey === '') {
|
||||
throw 'no encryption key given';
|
||||
}
|
||||
|
||||
// version 2 uses base58, version 1 uses base64 without decoding
|
||||
try {
|
||||
@@ -2035,29 +2048,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
xhrFields: {
|
||||
withCredentials: false
|
||||
},
|
||||
success: function(response) {
|
||||
let responseString = response;
|
||||
if (typeof responseString === 'object') {
|
||||
responseString = JSON.stringify(responseString);
|
||||
}
|
||||
if (typeof responseString === 'string' && responseString.length > 0) {
|
||||
const shortUrlMatcher = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
|
||||
const shortUrl = (responseString.match(shortUrlMatcher) || []).sort(function(a, b) {
|
||||
return a.length - b.length;
|
||||
})[0];
|
||||
if (typeof shortUrl === 'string' && shortUrl.length > 0) {
|
||||
// we disable the button to avoid calling shortener again
|
||||
$shortenButton.addClass('buttondisabled');
|
||||
// update link
|
||||
$pasteUrl.text(shortUrl);
|
||||
$pasteUrl.prop('href', shortUrl);
|
||||
// we pre-select the link so that the user only has to [Ctrl]+[c] the link
|
||||
Helper.selectText($pasteUrl[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Alert.showError('Cannot parse response from URL shortener.');
|
||||
}
|
||||
success: PasteStatus.extractUrl
|
||||
})
|
||||
.fail(function(data, textStatus, errorThrown) {
|
||||
console.error(textStatus, errorThrown);
|
||||
@@ -2123,6 +2114,50 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
Helper.selectText($pasteUrl[0]);
|
||||
};
|
||||
|
||||
/**
|
||||
* extracts URLs from given string
|
||||
*
|
||||
* if at least one is found, it disables the shortener button and
|
||||
* replaces the paste URL
|
||||
*
|
||||
* @name PasteStatus.extractUrl
|
||||
* @function
|
||||
* @param {string} response
|
||||
*/
|
||||
me.extractUrl = function(response)
|
||||
{
|
||||
if (typeof response === 'object') {
|
||||
response = JSON.stringify(response);
|
||||
}
|
||||
if (typeof response === 'string' && response.length > 0) {
|
||||
const shortUrlMatcher = /https?:\/\/[^\s"<]+/g; // JSON API will have URL in quotes, XML in tags
|
||||
const shortUrl = (response.match(shortUrlMatcher) || []).filter(function(urlRegExMatch) {
|
||||
if (typeof URL.canParse === 'function') {
|
||||
return URL.canParse(urlRegExMatch);
|
||||
}
|
||||
// polyfill for older browsers (< 120) & node (< 19.9 & < 18.17)
|
||||
try {
|
||||
return !!new URL(urlRegExMatch);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}).sort(function(a, b) {
|
||||
return a.length - b.length; // shortest first
|
||||
})[0];
|
||||
if (typeof shortUrl === 'string' && shortUrl.length > 0) {
|
||||
// we disable the button to avoid calling shortener again
|
||||
$shortenButton.addClass('buttondisabled');
|
||||
// update link
|
||||
$pasteUrl.text(shortUrl);
|
||||
$pasteUrl.prop('href', shortUrl);
|
||||
// we pre-select the link so that the user only has to [Ctrl]+[c] the link
|
||||
Helper.selectText($pasteUrl[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Alert.showError('Cannot parse response from URL shortener.');
|
||||
};
|
||||
|
||||
/**
|
||||
* shows the remaining time
|
||||
*
|
||||
@@ -2228,6 +2263,34 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
PasteDecrypter.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Request users confirmation to load possibly burn after reading paste
|
||||
*
|
||||
* @name Prompt.requestLoadConfirmation
|
||||
* @function
|
||||
*/
|
||||
me.requestLoadConfirmation = function()
|
||||
{
|
||||
const $loadconfirmmodal = $('#loadconfirmmodal');
|
||||
if ($loadconfirmmodal.length > 0) {
|
||||
const $loadconfirmOpenNow = $loadconfirmmodal.find('#loadconfirm-open-now');
|
||||
$loadconfirmOpenNow.off('click.loadPaste');
|
||||
$loadconfirmOpenNow.on('click.loadPaste', PasteDecrypter.run);
|
||||
const $loadconfirmClose = $loadconfirmmodal.find('.close');
|
||||
$loadconfirmClose.off('click.close');
|
||||
$loadconfirmClose.on('click.close', Controller.newPaste);
|
||||
$loadconfirmmodal.modal('show');
|
||||
} else {
|
||||
if (window.confirm(
|
||||
I18n._('Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?')
|
||||
)) {
|
||||
PasteDecrypter.run();
|
||||
} else {
|
||||
Controller.newPaste();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ask the user for the password and set it
|
||||
*
|
||||
@@ -2242,6 +2305,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
// focus password input
|
||||
$passwordDecrypt.focus();
|
||||
// then re-focus it, when modal causes it to loose focus again
|
||||
setTimeout(function () {
|
||||
$passwordDecrypt.focus();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2301,13 +2370,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
$passwordForm = $('#passwordform');
|
||||
$passwordModal = $('#passwordmodal');
|
||||
|
||||
// bind events
|
||||
|
||||
// focus password input when it is shown
|
||||
$passwordModal.on('shown.bs.Model', function () {
|
||||
$passwordDecrypt.focus();
|
||||
});
|
||||
// handle Model password submission
|
||||
// bind events - handle Model password submission
|
||||
$passwordForm.submit(submitPasswordModal);
|
||||
};
|
||||
|
||||
@@ -3541,7 +3604,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
if (fadeOut === true) {
|
||||
setTimeout(function () {
|
||||
$comment.removeClass('highlight');
|
||||
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
@@ -3789,6 +3851,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
{
|
||||
document.cookie = 'lang=' + $(event.target).data('lang') + ';secure';
|
||||
UiHelper.reloadHome();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3944,10 +4007,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
const $emailconfirmmodal = $('#emailconfirmmodal');
|
||||
if ($emailconfirmmodal.length > 0) {
|
||||
if (expirationDate !== null) {
|
||||
I18n._(
|
||||
$emailconfirmmodal.find('#emailconfirm-display'),
|
||||
'Recipient may become aware of your timezone, convert time to UTC?'
|
||||
);
|
||||
const $emailconfirmTimezoneCurrent = $emailconfirmmodal.find('#emailconfirm-timezone-current');
|
||||
const $emailconfirmTimezoneUtc = $emailconfirmmodal.find('#emailconfirm-timezone-utc');
|
||||
$emailconfirmTimezoneCurrent.off('click.sendEmailCurrentTimezone');
|
||||
@@ -4802,7 +4861,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
|
||||
// show notification
|
||||
const baseUri = Helper.baseUri() + '?',
|
||||
url = baseUri + data.id + '#' + CryptTool.base58encode(data.encryptionKey),
|
||||
url = baseUri + data.id + (TopNav.getBurnAfterReading() ? loadConfirmPrefix : '#') + CryptTool.base58encode(data.encryptionKey),
|
||||
deleteUrl = baseUri + 'pasteid=' + data.id + '&deletetoken=' + data.deletetoken;
|
||||
PasteStatus.createPasteNotification(url, deleteUrl);
|
||||
|
||||
@@ -5221,7 +5280,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
Alert.hideMessages();
|
||||
Alert.showLoading('Decrypting paste…', 'cloud-download');
|
||||
|
||||
if (typeof paste === 'undefined') {
|
||||
if (typeof paste === 'undefined' || paste.type === 'click') {
|
||||
// get cipher data and wait until it is available
|
||||
Model.getPasteData(me.run);
|
||||
return;
|
||||
@@ -5328,7 +5387,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
AttachmentViewer.removeAttachmentData();
|
||||
|
||||
Alert.hideLoading();
|
||||
history.pushState({type: 'create'}, document.title, Helper.baseUri());
|
||||
// only push new state if we are coming from a different one
|
||||
if (Helper.baseUri() != window.location) {
|
||||
history.pushState({type: 'create'}, document.title, Helper.baseUri());
|
||||
}
|
||||
|
||||
// clear discussion
|
||||
DiscussionViewer.prepareNewDiscussion();
|
||||
@@ -5354,6 +5416,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
}
|
||||
}
|
||||
|
||||
// check if we should request loading confirmation
|
||||
if(window.location.hash.startsWith(loadConfirmPrefix)) {
|
||||
Prompt.requestLoadConfirmation();
|
||||
return;
|
||||
}
|
||||
|
||||
// show proper elements on screen
|
||||
PasteDecrypter.run();
|
||||
};
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
js/purify-3.0.8.js
Normal file
2
js/purify-3.0.8.js
Normal file
File diff suppressed because one or more lines are too long
@@ -96,36 +96,34 @@ describe('Helper', function () {
|
||||
jsc.property(
|
||||
'replaces URLs with anchors',
|
||||
'string',
|
||||
jsc.elements(['http', 'https', 'ftp']),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
common.jscUrl(),
|
||||
jsc.array(common.jscHashString()),
|
||||
'string',
|
||||
function (prefix, schema, address, query, fragment, postfix) {
|
||||
query = query.join('');
|
||||
fragment = fragment.join('');
|
||||
function (prefix, url, fragment, postfix) {
|
||||
prefix = prefix.replace(/\r|\f/g, '\n').replace(/\u0000/g, '').replace(/\u000b/g, '');
|
||||
postfix = ' ' + postfix.replace(/\r/g, '\n').replace(/\u0000/g, '');
|
||||
let url = schema + '://' + address.join('') + '/?' + query + '#' + fragment,
|
||||
url.fragment = fragment.join('');
|
||||
let urlString = common.urlToString(url),
|
||||
clean = jsdom();
|
||||
$('body').html('<div id="foo"></div>');
|
||||
let e = $('#foo');
|
||||
|
||||
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. � or &#x
|
||||
if (
|
||||
query.slice(-1) === '&' &&
|
||||
(parseInt(fragment.substring(0, 1), 10) >= 0 || fragment.charAt(0) === 'x' )
|
||||
)
|
||||
{
|
||||
url = schema + '://' + address.join('') + '/?' + query.substring(0, query.length - 1);
|
||||
url.query[-1] === '&' &&
|
||||
(parseInt(url.fragment.charAt(0), 10) >= 0 || url.fragment.charAt(0) === 'x')
|
||||
) {
|
||||
url.query.pop();
|
||||
urlString = common.urlToString(url);
|
||||
postfix = '';
|
||||
}
|
||||
e.text(prefix + url + postfix);
|
||||
e.text(prefix + urlString + postfix);
|
||||
$.PrivateBin.Helper.urls2links(e);
|
||||
let result = e.html();
|
||||
clean();
|
||||
url = $('<div />').text(url).html();
|
||||
return $('<div />').text(prefix).html() + '<a href="' + url + '" target="_blank" rel="nofollow noopener noreferrer">' + url + '</a>' + $('<div />').text(postfix).html() === result;
|
||||
urlString = $('<div />').text(urlString).html();
|
||||
const expected = $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html();
|
||||
return $('<div />').text(prefix).html() + '<a href="' + urlString + '" target="_blank" rel="nofollow noopener noreferrer">' + urlString + '</a>' + $('<div />').text(postfix).html() === result;
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
@@ -261,16 +259,16 @@ describe('Helper', function () {
|
||||
this.timeout(30000);
|
||||
jsc.property(
|
||||
'returns the URL without query & fragment',
|
||||
jsc.elements(['http', 'https']),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'string',
|
||||
function (schema, address, path, query, fragment) {
|
||||
common.jscSchemas(false),
|
||||
common.jscUrl(),
|
||||
function (schema, url) {
|
||||
url.schema = schema;
|
||||
const fullUrl = common.urlToString(url);
|
||||
delete(url.query);
|
||||
delete(url.fragment);
|
||||
$.PrivateBin.Helper.reset();
|
||||
var path = path.join('') + (path.length > 0 ? '/' : ''),
|
||||
expected = schema + '://' + address.join('') + '/' + path,
|
||||
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
|
||||
const expected = common.urlToString(url),
|
||||
clean = jsdom('', {url: fullUrl}),
|
||||
result = $.PrivateBin.Helper.baseUri();
|
||||
clean();
|
||||
return expected === result;
|
||||
|
||||
112
js/test/Model.js
112
js/test/Model.js
@@ -80,23 +80,22 @@ describe('Model', function () {
|
||||
|
||||
jsc.property(
|
||||
'returns the query string without separator, if any',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
common.jscUrl(true, false),
|
||||
jsc.tuple(new Array(16).fill(common.jscHexString)),
|
||||
jsc.array(common.jscQueryString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'string',
|
||||
function (schema, address, pasteId, queryStart, queryEnd, fragment) {
|
||||
var pasteIdString = pasteId.join(''),
|
||||
queryStartString = queryStart.join('') + (queryStart.length > 0 ? '&' : ''),
|
||||
queryEndString = (queryEnd.length > 0 ? '&' : '') + queryEnd.join(''),
|
||||
queryString = queryStartString + pasteIdString + queryEndString,
|
||||
clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + queryString + '#' + fragment
|
||||
});
|
||||
global.URL = require('jsdom-url').URL;
|
||||
var result = $.PrivateBin.Model.getPasteId();
|
||||
function (url, pasteId, queryStart, queryEnd) {
|
||||
if (queryStart.length > 0) {
|
||||
queryStart.push('&');
|
||||
}
|
||||
if (queryEnd.length > 0) {
|
||||
queryEnd.unshift('&');
|
||||
}
|
||||
url.query = queryStart.concat(pasteId, queryEnd);
|
||||
const pasteIdString = pasteId.join(''),
|
||||
clean = jsdom('', {url: common.urlToString(url)});
|
||||
global.URL = require('jsdom-url').URL;
|
||||
const result = $.PrivateBin.Model.getPasteId();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
return pasteIdString === result;
|
||||
@@ -104,14 +103,9 @@ describe('Model', function () {
|
||||
);
|
||||
jsc.property(
|
||||
'throws exception on empty query string',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
'string',
|
||||
function (schema, address, fragment) {
|
||||
var clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/#' + fragment
|
||||
}),
|
||||
common.jscUrl(true, false),
|
||||
function (url) {
|
||||
let clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = false;
|
||||
global.URL = require('jsdom-url').URL;
|
||||
try {
|
||||
@@ -135,35 +129,24 @@ describe('Model', function () {
|
||||
|
||||
jsc.property(
|
||||
'returns the fragment of a v1 URL',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'nestring',
|
||||
function (schema, address, query, fragment) {
|
||||
const fragmentString = common.btoa(fragment.padStart(32, '\u0000'));
|
||||
let clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + query.join('') + '#' + fragmentString
|
||||
}),
|
||||
common.jscUrl(),
|
||||
function (url) {
|
||||
url.fragment = common.btoa(url.fragment.padStart(32, '\u0000'));
|
||||
const clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = $.PrivateBin.Model.getPasteKey();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
return fragmentString === result;
|
||||
return url.fragment === result;
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
'returns the v1 fragment stripped of trailing query parts',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'nestring',
|
||||
common.jscUrl(),
|
||||
jsc.array(common.jscHashString()),
|
||||
function (schema, address, query, fragment, trail) {
|
||||
const fragmentString = common.btoa(fragment.padStart(32, '\u0000'));
|
||||
let clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') + '/?' +
|
||||
query.join('') + '#' + fragmentString + '&' + trail.join('')
|
||||
}),
|
||||
function (url, trail) {
|
||||
const fragmentString = common.btoa(url.fragment.padStart(32, '\u0000'));
|
||||
url.fragment = fragmentString + '&' + trail.join('');
|
||||
const clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = $.PrivateBin.Model.getPasteKey();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
@@ -172,18 +155,12 @@ describe('Model', function () {
|
||||
);
|
||||
jsc.property(
|
||||
'returns the fragment of a v2 URL',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'nestring',
|
||||
function (schema, address, query, fragment) {
|
||||
common.jscUrl(),
|
||||
function (url) {
|
||||
// base58 strips leading NULL bytes, so the string is padded with these if not found
|
||||
fragment = fragment.padStart(32, '\u0000');
|
||||
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment),
|
||||
clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + query.join('') + '#' + fragmentString
|
||||
}),
|
||||
const fragment = url.fragment.padStart(32, '\u0000');
|
||||
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment);
|
||||
const clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = $.PrivateBin.Model.getPasteKey();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
@@ -192,19 +169,13 @@ describe('Model', function () {
|
||||
);
|
||||
jsc.property(
|
||||
'returns the v2 fragment stripped of trailing query parts',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'nestring',
|
||||
common.jscUrl(),
|
||||
jsc.array(common.jscHashString()),
|
||||
function (schema, address, query, fragment, trail) {
|
||||
function (url, trail) {
|
||||
// base58 strips leading NULL bytes, so the string is padded with these if not found
|
||||
fragment = fragment.padStart(32, '\u0000');
|
||||
let fragmentString = $.PrivateBin.CryptTool.base58encode(fragment),
|
||||
clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') + '/?' +
|
||||
query.join('') + '#' + fragmentString + '&' + trail.join('')
|
||||
}),
|
||||
const fragment = url.fragment.padStart(32, '\u0000');
|
||||
url.fragment = $.PrivateBin.CryptTool.base58encode(fragment) + '&' + trail.join('');
|
||||
const clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = $.PrivateBin.Model.getPasteKey();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
@@ -213,14 +184,9 @@ describe('Model', function () {
|
||||
);
|
||||
jsc.property(
|
||||
'throws exception on empty fragment of the URL',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
function (schema, address, query) {
|
||||
var clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + query.join('')
|
||||
}),
|
||||
common.jscUrl(false),
|
||||
function (url) {
|
||||
let clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = false;
|
||||
try {
|
||||
$.PrivateBin.Model.getPasteKey();
|
||||
|
||||
@@ -1,32 +1,39 @@
|
||||
'use strict';
|
||||
var common = require('../common');
|
||||
|
||||
function urlStrings(schema, longUrl, shortUrl) {
|
||||
longUrl.schema = schema;
|
||||
shortUrl.schema = schema;
|
||||
let longUrlString = common.urlToString(longUrl),
|
||||
shortUrlString = common.urlToString(shortUrl);
|
||||
// ensure the two random URLs actually are sorted as expected
|
||||
if (longUrlString.length <= shortUrlString.length) {
|
||||
if (longUrlString.length === shortUrlString.length) {
|
||||
longUrl.address.unshift('a');
|
||||
longUrlString = common.urlToString(longUrl);
|
||||
} else {
|
||||
[longUrlString, shortUrlString] = [shortUrlString, longUrlString];
|
||||
}
|
||||
}
|
||||
return [longUrlString, shortUrlString];
|
||||
}
|
||||
|
||||
describe('PasteStatus', function () {
|
||||
describe('createPasteNotification', function () {
|
||||
this.timeout(30000);
|
||||
|
||||
jsc.property(
|
||||
'creates a notification after a successfull paste upload',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
'string',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
function (
|
||||
schema1, address1, query1, fragment1,
|
||||
schema2, address2, query2
|
||||
) {
|
||||
var expected1 = schema1 + '://' + address1.join('') + '/?' +
|
||||
encodeURI(query1.join('').replace(/^&+|&+$/gm,'') + '#' + fragment1),
|
||||
expected2 = schema2 + '://' + address2.join('') + '/?' +
|
||||
encodeURI(query2.join('').replace(/^&+|&+$/gm,'')),
|
||||
common.jscUrl(),
|
||||
common.jscUrl(false),
|
||||
function (url1, url2) {
|
||||
const expected1 = common.urlToString(url1).replace(/&(gt|lt)$/, '&$1a'),
|
||||
expected2 = common.urlToString(url2).replace(/&(gt|lt)$/, '&$1a'),
|
||||
clean = jsdom();
|
||||
$('body').html('<div><div id="deletelink"></div><div id="pastelink"></div></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
$.PrivateBin.PasteStatus.createPasteNotification(expected1, expected2);
|
||||
var result1 = $('#pasteurl')[0].href,
|
||||
const result1 = $('#pasteurl')[0].href,
|
||||
result2 = $('#deletelink a')[0].href;
|
||||
clean();
|
||||
return result1 === expected1 && result2 === expected2;
|
||||
@@ -34,6 +41,138 @@ describe('PasteStatus', function () {
|
||||
);
|
||||
});
|
||||
|
||||
describe('extractUrl', function () {
|
||||
this.timeout(30000);
|
||||
|
||||
jsc.property(
|
||||
'extracts and updates IDN URLs found in given response',
|
||||
common.jscSchemas(false),
|
||||
'nestring',
|
||||
common.jscUrl(),
|
||||
function (schema, domain, url) {
|
||||
domain = domain.replace(/\P{Letter}|[\u00AA-\u00BA]/gu, '').toLowerCase();
|
||||
if (domain.length === 0) {
|
||||
domain = 'a';
|
||||
}
|
||||
url.schema = schema;
|
||||
url.address.unshift('.');
|
||||
url.address = domain.split('').concat(url.address);
|
||||
const urlString = common.urlToString(url),
|
||||
expected = urlString.substring((schema + '://' + domain).length),
|
||||
clean = jsdom();
|
||||
|
||||
$('body').html('<div><div id="pastelink"></div></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
$.PrivateBin.PasteStatus.createPasteNotification('', '');
|
||||
$.PrivateBin.PasteStatus.extractUrl(urlString);
|
||||
|
||||
const result = $('#pasteurl')[0].href;
|
||||
clean();
|
||||
|
||||
return result.endsWith(expected) && (
|
||||
result.startsWith(schema + '://xn--') ||
|
||||
result.startsWith(schema + '://' + domain)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// YOURLS API samples from: https://yourls.org/readme.html#API;apireturn
|
||||
jsc.property(
|
||||
'extracts and updates URLs found in YOURLS API JSON response',
|
||||
common.jscSchemas(false),
|
||||
common.jscUrl(),
|
||||
common.jscUrl(false),
|
||||
function (schema, longUrl, shortUrl) {
|
||||
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
|
||||
yourlsResponse = {
|
||||
url: {
|
||||
keyword: longUrl.address.join(''),
|
||||
url: longUrlString,
|
||||
title: "example title",
|
||||
date: "2014-10-24 16:01:39",
|
||||
ip: "127.0.0.1"
|
||||
},
|
||||
status: "success",
|
||||
message: longUrlString + " added to database",
|
||||
title: "example title",
|
||||
shorturl: shortUrlString,
|
||||
statusCode: 200
|
||||
},
|
||||
clean = jsdom();
|
||||
|
||||
$('body').html('<div><div id="pastelink"></div></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
$.PrivateBin.PasteStatus.createPasteNotification('', '');
|
||||
$.PrivateBin.PasteStatus.extractUrl(JSON.stringify(yourlsResponse, undefined, 4));
|
||||
|
||||
const result = $('#pasteurl')[0].href;
|
||||
clean();
|
||||
|
||||
return result === shortUrlString;
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
'extracts and updates URLs found in YOURLS API XML response',
|
||||
common.jscSchemas(false),
|
||||
common.jscUrl(),
|
||||
common.jscUrl(false),
|
||||
function (schema, longUrl, shortUrl) {
|
||||
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
|
||||
yourlsResponse = '<result>\n' +
|
||||
' <keyword>' + longUrl.address.join('') + '</keyword>\n' +
|
||||
' <shorturl>' + shortUrlString + '</shorturl>\n' +
|
||||
' <longurl>' + longUrlString + '</longurl>\n' +
|
||||
' <message>success</message>\n' +
|
||||
' <statusCode>200</statusCode>\n' +
|
||||
'</result>',
|
||||
clean = jsdom();
|
||||
|
||||
$('body').html('<div><div id="pastelink"></div></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
$.PrivateBin.PasteStatus.createPasteNotification('', '');
|
||||
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse);
|
||||
|
||||
const result = $('#pasteurl')[0].href;
|
||||
clean();
|
||||
|
||||
return result === shortUrlString;
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
'extracts and updates URLs found in YOURLS proxy HTML response',
|
||||
common.jscSchemas(false),
|
||||
common.jscUrl(),
|
||||
common.jscUrl(false),
|
||||
function (schema, longUrl, shortUrl) {
|
||||
const [longUrlString, shortUrlString] = urlStrings(schema, longUrl, shortUrl),
|
||||
yourlsResponse = '<!DOCTYPE html>\n' +
|
||||
'<html lang="en">\n' +
|
||||
'\t<head>\n' +
|
||||
'\t\t<meta charset="utf-8" />\n' +
|
||||
'\t\t<meta http-equiv="Content-Security-Policy" content="default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads">\n' +
|
||||
'\t\t<meta name="robots" content="noindex" />\n' +
|
||||
'\t\t<meta name="google" content="notranslate">\n' +
|
||||
'\t\t<title>PrivateBin</title>\n' +
|
||||
'\t</head>\n' +
|
||||
'\t<body>\n' +
|
||||
'\t\t<p>Your paste is <a id="pasteurl" href="' + shortUrlString + '">' + shortUrlString + '</a> <span id="copyhint">(Hit [Ctrl]+[c] to copy)</span></p>\n' +
|
||||
'\t</body>\n' +
|
||||
'</html>',
|
||||
clean = jsdom();
|
||||
|
||||
$('body').html('<div><div id="pastelink"></div></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
$.PrivateBin.PasteStatus.createPasteNotification('', '');
|
||||
$.PrivateBin.PasteStatus.extractUrl(yourlsResponse);
|
||||
|
||||
const result = $('#pasteurl')[0].href;
|
||||
clean();
|
||||
|
||||
return result === shortUrlString;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('showRemainingTime', function () {
|
||||
this.timeout(30000);
|
||||
|
||||
@@ -41,18 +180,9 @@ describe('PasteStatus', function () {
|
||||
'shows burn after reading message or remaining time v1',
|
||||
'bool',
|
||||
'nat',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscQueryString()),
|
||||
'string',
|
||||
function (
|
||||
burnafterreading, remainingTime,
|
||||
schema, address, query, fragment
|
||||
) {
|
||||
var clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + query.join('') + '#' + fragment
|
||||
}),
|
||||
common.jscUrl(),
|
||||
function (burnafterreading, remainingTime, url) {
|
||||
let clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result;
|
||||
$('body').html('<div id="remainingtime" class="hidden"></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
@@ -79,18 +209,9 @@ describe('PasteStatus', function () {
|
||||
'shows burn after reading message or remaining time v2',
|
||||
'bool',
|
||||
'nat',
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.nearray(common.jscQueryString()),
|
||||
'string',
|
||||
function (
|
||||
burnafterreading, remainingTime,
|
||||
schema, address, query, fragment
|
||||
) {
|
||||
var clean = jsdom('', {
|
||||
url: schema.join('') + '://' + address.join('') +
|
||||
'/?' + query.join('') + '#' + fragment
|
||||
}),
|
||||
common.jscUrl(),
|
||||
function (burnafterreading, remainingTime, url) {
|
||||
let clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result;
|
||||
$('body').html('<div id="remainingtime" class="hidden"></div>');
|
||||
$.PrivateBin.PasteStatus.init();
|
||||
|
||||
@@ -13,10 +13,9 @@ describe('UiHelper', function () {
|
||||
|
||||
jsc.property(
|
||||
'redirects to home, when the state is null',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
function (schema, address) {
|
||||
var expected = schema + '://' + address.join('') + '/',
|
||||
common.jscUrl(false, false),
|
||||
function (url) {
|
||||
const expected = common.urlToString(url),
|
||||
clean = jsdom('', {url: expected});
|
||||
|
||||
// make window.location.href writable
|
||||
@@ -34,13 +33,11 @@ describe('UiHelper', function () {
|
||||
|
||||
jsc.property(
|
||||
'does not redirect to home, when a new paste is created',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
common.jscUrl(false),
|
||||
jsc.nearray(common.jscBase64String()),
|
||||
function (schema, address, query, fragment) {
|
||||
var expected = schema + '://' + address.join('') + '/?' +
|
||||
query.join('') + '#' + fragment.join(''),
|
||||
function (url, fragment) {
|
||||
url.fragment = fragment.join('');
|
||||
const expected = common.urlToString(url),
|
||||
clean = jsdom('', {url: expected});
|
||||
|
||||
// make window.location.href writable
|
||||
@@ -67,15 +64,12 @@ describe('UiHelper', function () {
|
||||
|
||||
jsc.property(
|
||||
'redirects to home',
|
||||
common.jscSchemas(),
|
||||
jsc.nearray(common.jscA2zString()),
|
||||
jsc.array(common.jscQueryString()),
|
||||
jsc.nearray(common.jscBase64String()),
|
||||
function (schema, address, query, fragment) {
|
||||
var expected = schema + '://' + address.join('') + '/',
|
||||
clean = jsdom('', {
|
||||
url: expected + '?' + query.join('') + '#' + fragment.join('')
|
||||
});
|
||||
common.jscUrl(),
|
||||
function (url) {
|
||||
const clean = jsdom('', {url: common.urlToString(url)});
|
||||
delete(url.query);
|
||||
delete(url.fragment);
|
||||
const expected = common.urlToString(url);
|
||||
|
||||
// make window.location.href writable
|
||||
Object.defineProperty(window.location, 'href', {
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
|
||||
let buff;
|
||||
if (typeof fs === 'object') {
|
||||
buff = fs.readFileSync('zlib-1.2.13.wasm');
|
||||
buff = fs.readFileSync('zlib-1.3.wasm');
|
||||
} else {
|
||||
const resp = await fetch('js/zlib-1.2.13.wasm');
|
||||
const resp = await fetch('js/zlib-1.3.wasm');
|
||||
buff = await resp.arrayBuffer();
|
||||
}
|
||||
const module = await WebAssembly.compile(buff);
|
||||
Binary file not shown.
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -28,7 +28,7 @@ class Controller
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const VERSION = '1.6.2';
|
||||
const VERSION = '1.7.0';
|
||||
|
||||
/**
|
||||
* minimal required PHP version
|
||||
@@ -111,10 +111,12 @@ class Controller
|
||||
public function __construct()
|
||||
{
|
||||
if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) {
|
||||
throw new Exception(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION), 1);
|
||||
error_log(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION));
|
||||
return;
|
||||
}
|
||||
if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) {
|
||||
throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5);
|
||||
error_log(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR));
|
||||
return;
|
||||
}
|
||||
|
||||
// load config from ini file, initialize required classes
|
||||
@@ -250,7 +252,14 @@ class Controller
|
||||
}
|
||||
// The user posts a standard paste.
|
||||
else {
|
||||
$this->_model->purge();
|
||||
try {
|
||||
$this->_model->purge();
|
||||
} catch (Exception $e) {
|
||||
error_log('Error purging pastes: ' . $e->getMessage() . PHP_EOL .
|
||||
'Use the administration scripts statistics to find ' .
|
||||
'damaged paste IDs and either delete them or restore them ' .
|
||||
'from backup.');
|
||||
}
|
||||
$paste = $this->_model->getPaste();
|
||||
try {
|
||||
$paste->setData($data);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Data;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* an S3 compatible data backend for PrivateBin with CEPH/RadosGW in mind
|
||||
* see https://docs.ceph.com/en/latest/radosgw/s3/php/
|
||||
* based on lib/Data/GoogleCloudStorage.php from PrivateBin version 1.6.2
|
||||
* based on lib/Data/GoogleCloudStorage.php from PrivateBin version 1.7.0
|
||||
*
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2022 Felix J. Ogris (https://ogris.de/)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
@@ -315,10 +315,10 @@ class I18n
|
||||
*/
|
||||
protected static function _getPath($file = '')
|
||||
{
|
||||
if (strlen(self::$_path) == 0) {
|
||||
if (empty(self::$_path)) {
|
||||
self::$_path = PUBLIC_PATH . DIRECTORY_SEPARATOR . 'i18n';
|
||||
}
|
||||
return self::$_path . (strlen($file) ? DIRECTORY_SEPARATOR . $file : '');
|
||||
return self::$_path . (empty($file) ? '' : DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,6 +356,8 @@ class I18n
|
||||
return $n % 10 === 1 && $n % 100 !== 11 ? 0 : (($n % 10 >= 2 && $n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
|
||||
case 'pl':
|
||||
return $n === 1 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
|
||||
case 'ro':
|
||||
return $n === 1 ? 0 : (($n === 0 || ($n % 100 > 0 && $n % 100 < 20)) ? 1 : 2);
|
||||
case 'ru':
|
||||
case 'uk':
|
||||
return $n % 10 === 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Model;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin\Persistence;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* @link https://sebsauvage.net/wiki/doku.php?id=php:vizhash_gd
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 0.0.5 beta PrivateBin 1.6.2
|
||||
* @version 0.0.5 beta PrivateBin 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @link https://github.com/PrivateBin/PrivateBin
|
||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
* @version 1.6.2
|
||||
* @version 1.7.0
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
@@ -55,7 +55,7 @@ if ($ZEROBINCOMPATIBILITY) :
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.13-1.js" integrity="sha512-Ltu/5qZMapdRIy4B3/iuLessSK6CvAui1C8txOX0z/uAFDJXXSwjvHsiFg9peQ2Gg/ga2tneKBfS0+00ndFsjQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.3.1.js" integrity="sha512-VL3lLnt8EexTr22ze4a4HfVghpgY48c/Lhf4CcQa8bgYaIRRPjV1nP7EA8RiciGoXXQ1IeiU7tjrclPeVEfxOQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-4.0.0.js" integrity="sha512-nNPg5IGCwwrveZ8cA/yMGr5HiRS5Ps2H+s0J/mKTPjCPWUgFGGw7M5nqdnPD3VsRwCVysUh3Y8OWjeSKGkEQJQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/bootstrap-3.4.1.js" integrity="sha512-oBTprMeNEKCnqfuqKd6sbvFzmFQtlXS3e0C/RGFV0hD6QzhHV+ODfaQbAlmY6/q0ubbwlAM/nCJjkrgA3waLzg==" crossorigin="anonymous"></script>
|
||||
@@ -71,9 +71,9 @@ if ($MARKDOWN) :
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.8.js" integrity="sha512-wWBDKh5wYGtJ1Df+PPZIn59jHVBnJ4/Yb2W/pVnzaXab8cmlZnHVx+FEBGu5JX39s3P2Qlt+aNQou0XnjW86hg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-wkyB+e2HinOE+hZOEfBtNVlg2lneE09dnrDjZKd77dHowLEIxrys1La7neVov1HWcAQlwhqxnZF4yr19kDuunw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-w5YHEFVx8IcurkgsVsaRFwO+0u9HDNrwAE+BUS3mnFQLjJ6Ti3hzQdguL1exCG3rY85drvHcGSj/FJWmHFxudA==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png" sizes="32x32" />
|
||||
@@ -123,22 +123,32 @@ if (count($class)) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loadconfirmmodal" tabindex="-1" class="modal fade" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="<?php echo I18n::_('Close') ?>"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><?php echo I18n::_('Burn after reading pastes can only be displayed once upon loading it. Do you want to open it now?') ?></h4>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<button id="loadconfirm-open-now" type="button" class="btn btn-success" data-dismiss="modal"><span class="glyphicon glyphicon-download"></span> <?php echo I18n::_('Yes, load it') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if ($QRCODE) :
|
||||
?>
|
||||
<div id="qrcodemodal" tabindex="-1" class="modal fade" aria-labelledby="qrcodemodalTitle" role="dialog" aria-hidden="true">
|
||||
<div id="qrcodemodal" tabindex="-1" class="modal fade" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="<?php echo I18n::_('Close') ?>"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><?php echo I18n::_('QR code') ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mx-auto" id="qrcode-display"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="btn-group col-xs-12">
|
||||
<span class="col-xs-12">
|
||||
<button type="button" class="btn btn-primary btn-block" data-dismiss="modal"><?php echo I18n::_('Close') ?></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -146,23 +156,19 @@ if ($QRCODE) :
|
||||
endif;
|
||||
if ($EMAIL) :
|
||||
?>
|
||||
<div id="emailconfirmmodal" tabindex="-1" class="modal fade" aria-labelledby="emailconfirmmodalTitle" role="dialog" aria-hidden="true">
|
||||
<div id="emailconfirmmodal" tabindex="-1" class="modal fade" role="dialog" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body">
|
||||
<div id="emailconfirm-display"></div>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="<?php echo I18n::_('Close') ?>"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title"><?php echo I18n::_('Recipient may become aware of your timezone, convert time to UTC?') ?></h4>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="btn-group col-xs-12" data-toggle="buttons">
|
||||
<span class="col-xs-12 col-md-4">
|
||||
<button id="emailconfirm-timezone-current" type="button" class="btn btn-danger btn-block" data-dismiss="modal"><?php echo I18n::_('Use Current Timezone') ?></button>
|
||||
</span>
|
||||
<span class="col-xs-12 col-md-4">
|
||||
<button id="emailconfirm-timezone-utc" type="button" class="btn btn-default btn-block" data-dismiss="modal"><?php echo I18n::_('Convert To UTC') ?></button>
|
||||
</span>
|
||||
<span class="col-xs-12 col-md-4">
|
||||
<button type="button" class="btn btn-primary btn-block" data-dismiss="modal"><?php echo I18n::_('Close') ?></button>
|
||||
</span>
|
||||
<div class="modal-body row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<button id="emailconfirm-timezone-current" type="button" class="btn btn-danger"><span class="glyphicon glyphicon-time"></span> <?php echo I18n::_('Use Current Timezone') ?></button>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 text-right">
|
||||
<button id="emailconfirm-timezone-utc" type="button" class="btn btn-success"><span class="glyphicon glyphicon-globe"></span> <?php echo I18n::_('Convert To UTC') ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@ if ($ZEROBINCOMPATIBILITY):
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.2.13-1.js" integrity="sha512-Ltu/5qZMapdRIy4B3/iuLessSK6CvAui1C8txOX0z/uAFDJXXSwjvHsiFg9peQ2Gg/ga2tneKBfS0+00ndFsjQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/zlib-1.3.1.js" integrity="sha512-VL3lLnt8EexTr22ze4a4HfVghpgY48c/Lhf4CcQa8bgYaIRRPjV1nP7EA8RiciGoXXQ1IeiU7tjrclPeVEfxOQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/base-x-4.0.0.js" integrity="sha512-nNPg5IGCwwrveZ8cA/yMGr5HiRS5Ps2H+s0J/mKTPjCPWUgFGGw7M5nqdnPD3VsRwCVysUh3Y8OWjeSKGkEQJQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/rawinflate-0.3.js" integrity="sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==" crossorigin="anonymous"></script>
|
||||
<?php
|
||||
@@ -49,9 +49,9 @@ if ($MARKDOWN):
|
||||
<?php
|
||||
endif;
|
||||
?>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.6.js" integrity="sha512-N3y6/HOk3pbsw3lFh4O8CKKEVwu1B2CF8kinhjURf8Yqa5OfSUt+/arozxFW+TUPOPw3TsDCRT/0u7BGRTEVUw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/purify-3.0.8.js" integrity="sha512-wWBDKh5wYGtJ1Df+PPZIn59jHVBnJ4/Yb2W/pVnzaXab8cmlZnHVx+FEBGu5JX39s3P2Qlt+aNQou0XnjW86hg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/legacy.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-LYos+qXHIRqFf5ZPNphvtTB0cgzHUizu2wwcOwcwz/VIpRv9lpcBgPYz4uq6jx0INwCAj6Fbnl5HoKiLufS2jg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-wkyB+e2HinOE+hZOEfBtNVlg2lneE09dnrDjZKd77dHowLEIxrys1La7neVov1HWcAQlwhqxnZF4yr19kDuunw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" data-cfasync="false" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-w5YHEFVx8IcurkgsVsaRFwO+0u9HDNrwAE+BUS3mnFQLjJ6Ti3hzQdguL1exCG3rY85drvHcGSj/FJWmHFxudA==" crossorigin="anonymous"></script>
|
||||
<!-- icon -->
|
||||
<link rel="apple-touch-icon" href="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" sizes="180x180" />
|
||||
<link rel="icon" type="image/png" href="img/favicon-32x32.png?<?php echo rawurlencode($VERSION); ?>" sizes="32x32" />
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use PrivateBin\I18n;
|
||||
|
||||
class I18nMock extends I18n
|
||||
{
|
||||
public static function resetAvailableLanguages()
|
||||
{
|
||||
self::$_availableLanguages = array();
|
||||
}
|
||||
|
||||
public static function resetPath($path = '')
|
||||
{
|
||||
self::$_path = $path;
|
||||
}
|
||||
|
||||
public static function getPath($file = '')
|
||||
{
|
||||
return self::_getPath($file);
|
||||
}
|
||||
}
|
||||
|
||||
class I18nTest extends TestCase
|
||||
{
|
||||
private $_translations = array();
|
||||
@@ -167,6 +185,38 @@ class I18nTest extends TestCase
|
||||
$this->assertEquals($result . $result, I18n::_($input . '%s', $input), 'encodes message ID as well, when no link');
|
||||
}
|
||||
|
||||
public function testFallbackAlwaysPresent()
|
||||
{
|
||||
$path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_i18n';
|
||||
if (!is_dir($path)) {
|
||||
mkdir($path);
|
||||
}
|
||||
|
||||
$languageIterator = new AppendIterator();
|
||||
$languageIterator->append(new GlobIterator(I18nMock::getPath('??.json')));
|
||||
$languageIterator->append(new GlobIterator(I18nMock::getPath('???.json'))); // for jbo
|
||||
$languageCount = 0;
|
||||
foreach ($languageIterator as $file) {
|
||||
++$languageCount;
|
||||
$this->assertTrue(copy($file, $path . DIRECTORY_SEPARATOR . $file->getBasename()));
|
||||
}
|
||||
|
||||
I18nMock::resetPath($path);
|
||||
$languagesDevelopment = I18nMock::getAvailableLanguages();
|
||||
$this->assertEquals($languageCount, count($languagesDevelopment), 'all copied languages detected');
|
||||
$this->assertTrue(in_array('en', $languagesDevelopment), 'English fallback present');
|
||||
|
||||
unlink($path . DIRECTORY_SEPARATOR . 'en.json');
|
||||
I18nMock::resetAvailableLanguages();
|
||||
$languagesDeployed = I18nMock::getAvailableLanguages();
|
||||
$this->assertEquals($languageCount, count($languagesDeployed), 'all copied languages detected, plus fallback');
|
||||
$this->assertTrue(in_array('en', $languagesDeployed), 'English fallback still present');
|
||||
|
||||
I18nMock::resetAvailableLanguages();
|
||||
I18nMock::resetPath();
|
||||
Helper::rmDir($path);
|
||||
}
|
||||
|
||||
public function testMessageIdsExistInAllLanguages()
|
||||
{
|
||||
$messageIds = array();
|
||||
|
||||
Reference in New Issue
Block a user