Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20e30b6637 | ||
|
|
6929a6eb76 | ||
|
|
7f9d29b13f | ||
|
|
989a49852e | ||
|
|
d8efd4cd2a | ||
|
|
541426b400 | ||
|
|
4a4cbf1455 | ||
|
|
f68d3cc89c | ||
|
|
b8ee73eacf | ||
|
|
b03ca0cdd5 | ||
|
|
1d4c206a7e | ||
|
|
eac1824cb5 | ||
|
|
4df7684ee5 | ||
|
|
1a4ac8cd9b | ||
|
|
8b66002521 | ||
|
|
4162d0f6c9 | ||
|
|
09682de11e | ||
|
|
9437486b10 | ||
|
|
5c33ee094f | ||
|
|
21d94668a1 | ||
|
|
1285fa935f | ||
|
|
12832c05cc | ||
|
|
8823f30881 | ||
|
|
241bcc6d2d | ||
|
|
e02eb6f56f | ||
|
|
8c318658f8 | ||
|
|
ad5d5716c7 | ||
|
|
a42a5d467e | ||
|
|
b893e7c2b5 | ||
|
|
66d0b92ea1 | ||
|
|
0da6613cbb | ||
|
|
6a05598c9b | ||
|
|
fff9ab3e13 | ||
|
|
03e2b4f4ba | ||
|
|
1042a650ec | ||
|
|
1eb694ea27 | ||
|
|
32da95dafd | ||
|
|
ac894404c0 | ||
|
|
d6ef7a9dc3 | ||
|
|
c2a6f8fddb | ||
|
|
dfed599409 | ||
|
|
f9117ca483 | ||
|
|
fb75637dd0 | ||
|
|
dd99cad66c | ||
|
|
384ea22ba6 | ||
|
|
83a7ad6538 | ||
|
|
e409ad6880 | ||
|
|
4d38551c08 | ||
|
|
0e42ccdb6b | ||
|
|
c35729e23e | ||
|
|
72da9970cd | ||
|
|
1e4d3990b4 | ||
|
|
8180f538c5 | ||
|
|
a2a88f7c3e | ||
|
|
427f03e779 | ||
|
|
800ebcc616 | ||
|
|
4151a459ab | ||
|
|
d6fe310ded | ||
|
|
8076649965 | ||
|
|
d22b9302b1 | ||
|
|
a10fb589ca | ||
|
|
f1c4b79652 | ||
|
|
4cbd449d75 | ||
|
|
cdc0938044 | ||
|
|
8f5a6257a9 | ||
|
|
e626f609b0 | ||
|
|
8d59a37dde | ||
|
|
bcdd795c08 | ||
|
|
6563682fad | ||
|
|
331484b89f | ||
|
|
f7bb92d660 | ||
|
|
58d158b735 | ||
|
|
a2eb40946d | ||
|
|
3516bdbd12 | ||
|
|
a75e58ae8d | ||
|
|
eb3396ba9d | ||
|
|
93cd2ba38b | ||
|
|
de6423866e | ||
|
|
fc60d3bee9 | ||
|
|
d10634f3c1 | ||
|
|
c005f0954e | ||
|
|
ff52413e6b | ||
|
|
3cbade2325 | ||
|
|
869222821e | ||
|
|
3764fc1395 | ||
|
|
fd0b2379b3 | ||
|
|
389b215b2f | ||
|
|
a2ca2ecb37 | ||
|
|
d15ac590d7 | ||
|
|
964b4da50a | ||
|
|
d01c37c59d | ||
|
|
409af9b991 | ||
|
|
c7b9ce0bc2 | ||
|
|
f3d851b0ba | ||
|
|
d7b32f0219 | ||
|
|
c7f465fe8b | ||
|
|
9455fea799 | ||
|
|
44f8cfbfb8 | ||
|
|
4eb6555e3c | ||
|
|
d544d1281d | ||
|
|
91bfd2eec1 | ||
|
|
f49c042cc9 | ||
|
|
8d720e4990 | ||
|
|
34028229c8 | ||
|
|
8268a92ebd | ||
|
|
89c5ebb5f2 | ||
|
|
d778887cd1 | ||
|
|
3a1eb8d534 | ||
|
|
095a5be0b6 | ||
|
|
6ff08b6884 | ||
|
|
6dac586f41 | ||
|
|
5654ef2db8 | ||
|
|
dededc9935 | ||
|
|
c08a792f01 | ||
|
|
6347b6193e | ||
|
|
bace4695ac | ||
|
|
7a42f31aef | ||
|
|
e149e1b412 | ||
|
|
e091f88544 | ||
|
|
2460caddc8 | ||
|
|
95f232232e | ||
|
|
50a695b609 | ||
|
|
cfb609814a | ||
|
|
0199bed133 | ||
|
|
bce152c890 | ||
|
|
abe7874829 | ||
|
|
df1f742789 | ||
|
|
1f7f6c93b0 | ||
|
|
302921a811 | ||
|
|
d220ba7c2b | ||
|
|
9a9eb44102 | ||
|
|
10ee2fa2e4 | ||
|
|
e34a35bf30 | ||
|
|
75db58800c | ||
|
|
cbdd3d50a5 | ||
|
|
b1608e5119 | ||
|
|
f8df10d2e1 | ||
|
|
37871eac69 | ||
|
|
edb753b3d7 | ||
|
|
bb4a3a09b0 | ||
|
|
1c72380443 | ||
|
|
a4be833937 | ||
|
|
16129b610d | ||
|
|
31162e8011 | ||
|
|
bac849d98a | ||
|
|
6ee9563b27 | ||
|
|
797bf5eb6a | ||
|
|
abb49609e0 | ||
|
|
46c49e5455 | ||
|
|
8ad6300c1c | ||
|
|
c04a551215 | ||
|
|
5d4561bd0a | ||
|
|
7825471d70 | ||
|
|
629f263cf5 | ||
|
|
e67972417e | ||
|
|
c62a3fbd2d | ||
|
|
81e9c8672f | ||
|
|
24316f3e07 | ||
|
|
495a84b292 | ||
|
|
1d53f5a054 | ||
|
|
4d0fbfd02b | ||
|
|
dd6537b277 | ||
|
|
9221629d8d | ||
|
|
ebb9e231aa | ||
|
|
e68430e9c5 | ||
|
|
d0303dce9c | ||
|
|
9f82eb64cb | ||
|
|
a9ea962adc | ||
|
|
d89bc1b97b | ||
|
|
e0b6aee65a | ||
|
|
4e518b3fce | ||
|
|
bb98d5b6c1 | ||
|
|
478b79b7b7 | ||
|
|
57636ee6d4 | ||
|
|
bd3486eda7 | ||
|
|
ddc9de345d | ||
|
|
e511ab74b1 | ||
|
|
c32c74a630 | ||
|
|
412987ea5b | ||
|
|
b90967a14b | ||
|
|
654bff65cc | ||
|
|
95188eea94 | ||
|
|
6bab8b014f | ||
|
|
ac62d909b9 | ||
|
|
64b0e33574 | ||
|
|
e4d1f4a26d | ||
|
|
5b54f2cdb0 | ||
|
|
0eef8d62d7 | ||
|
|
bce449de10 | ||
|
|
edad5ba1e0 | ||
|
|
dce5a73444 | ||
|
|
a4b0a993c7 | ||
|
|
1c2c45ec2d | ||
|
|
ec5b72476e | ||
|
|
540172d0f7 | ||
|
|
3b6b97429d | ||
|
|
dc983621ac | ||
|
|
ac63b91a1b | ||
|
|
db15ae8b43 | ||
|
|
740fd90db9 | ||
|
|
aa8f77f673 | ||
|
|
0a37a884e2 | ||
|
|
bbf6ad76f4 | ||
|
|
c7a4ced9e1 | ||
|
|
f111def946 | ||
|
|
f88c643be9 | ||
|
|
ed3347e835 | ||
|
|
6ba9fe5a93 | ||
|
|
e8ae354c63 | ||
|
|
2809403893 | ||
|
|
aeba6e24cd | ||
|
|
f9ac763e62 | ||
|
|
d86f502136 | ||
|
|
c963ba4d7a | ||
|
|
cf14766d9f | ||
|
|
4b2443f3eb | ||
|
|
94ab727139 | ||
|
|
bbaee2c737 | ||
|
|
2a27c767a5 | ||
|
|
e25fe01d82 |
2
.github/workflows/release.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
actions: read
|
||||
id-token: write
|
||||
contents: write
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
base64-subjects: "${{ needs.release.outputs.hashes }}"
|
||||
draft-release: true
|
||||
|
||||
2
.github/workflows/test-results.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download and Extract Artifacts
|
||||
uses: dawidd6/action-download-artifact@20319c5641d495c8a52e688b7dc5fada6c3a9fbc
|
||||
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5
|
||||
with:
|
||||
run_id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
|
||||
2
.github/workflows/tests.yml
vendored
@@ -121,7 +121,7 @@ jobs:
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'js/package-lock.json'
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ RewriteEngine on
|
||||
RewriteCond !%{HTTP_USER_AGENT} "Let's Encrypt validation server" [NC]
|
||||
RewriteCond %{HTTP_USER_AGENT} ^.*(bot|spider|crawl|https?://|WhatsApp|SkypeUriPreview|facebookexternalhit) [NC]
|
||||
RewriteRule .* - [R=403,L]
|
||||
AddType application/wasm .wasm
|
||||
|
||||
<IfModule mod_php7.c>
|
||||
php_value max_execution_time 30
|
||||
|
||||
12
CHANGELOG.md
@@ -1,5 +1,17 @@
|
||||
# PrivateBin version history
|
||||
|
||||
## 1.7.7 (2025-06-28)
|
||||
* ADDED: Switching templates using the web ui (#1501)
|
||||
* ADDED: Show file name and size on download page (#603)
|
||||
* CHANGED: Passing large data structures by reference to reduce memory consumption (#858)
|
||||
* CHANGED: Removed use of ctype functions and polyfill library for ctype
|
||||
* CHANGED: Upgrading libraries to: DOMpurify 3.2.6, ip-lib 1.20.0
|
||||
* CHANGED: Support for multiple file uploads (#1060)
|
||||
* CHANGED: Documented CSP change necessary to allow PDF attachment preview (#1552)
|
||||
* FIXED: Hide Reply button in the discussions once clicked to avoid losing the text input (#1508)
|
||||
* FIXED: Bump zlib library suffix, ensuring cache refresh for WASM streaming change
|
||||
* FIXED: Handle undefined globals in file based persisted values (#1544)
|
||||
|
||||
## 1.7.6 (2025-02-01)
|
||||
* ADDED: Ability to copy the paste by clicking the copy icon button or using the keyboard shortcut ctrl+c/cmd+c (#1390 & #12)
|
||||
* CHANGED: Allow toggling tab-key-support using `[Ctrl]+[m]` or `[Esc]` in textarea for keyboard navigation (#1386)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
* Simon Rupf - current developer and maintainer
|
||||
* rugk - security review, doc improvment, JS refactoring & various other stuff
|
||||
* R4SAS - python client, compression, blob URI to support larger attachments
|
||||
* Mikhail Romanov - UI improvements, theme switching, clipboard support, multi-file upload, bugfixes, code refactoring
|
||||
|
||||
## Past contributions
|
||||
|
||||
@@ -33,7 +34,6 @@
|
||||
* Mounir Idrassi & J. Mozdzen - secure YOURLS integration
|
||||
* Felipe Nakandakari - enabled AWS SDK to use default credential provider chain in the S3 Storage backend
|
||||
* Aaron Sherber - cache control headers for API calls & use of `shortenviayourls` in query parameters
|
||||
* Mikhail Romanov - copying to clipboard, UI/UX improvements
|
||||
|
||||
## Translations
|
||||
* Hexalyse - French
|
||||
|
||||
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.7.6
|
||||
VERSION ?= 1.7.7
|
||||
CURRENT_VERSION = 1.7.7
|
||||
VERSION ?= 1.7.8
|
||||
VERSION_FILES = README.md SECURITY.md doc/Installation.md js/package*.json lib/Controller.php Makefile
|
||||
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.7.6*
|
||||
*Current version: 1.7.7*
|
||||
|
||||
**PrivateBin** is a minimalist, open source online
|
||||
[pastebin](https://en.wikipedia.org/wiki/Pastebin)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.7.6 | :heavy_check_mark: |
|
||||
| < 1.7.6 | :x: |
|
||||
| 1.7.7 | :heavy_check_mark: |
|
||||
| < 1.7.7 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
@@ -14,5 +14,8 @@ a response within a week (usually during the next weekend). The respondee will
|
||||
reply from their personal address and can offer you their GPG public key to
|
||||
support end-to-end encrypted communication on sensitive topics or attachments.
|
||||
|
||||
You can also [use the corresponding GitHub form](https://github.com/PrivateBin/PrivateBin/security/advisories/new)
|
||||
to report a new vulnerability directly on GitHub.
|
||||
|
||||
You can also contact us via the regular issue tracker if the risk of early
|
||||
publication is low or you would request input from other PrivateBin users.
|
||||
|
||||
@@ -72,6 +72,35 @@ class Administration
|
||||
exit("paste $pasteId successfully deleted" . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* lists all stored paste IDs
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function _list_ids()
|
||||
{
|
||||
$ids = $this->_store->getAllPastes();
|
||||
foreach ($ids as $pasteid) {
|
||||
echo $pasteid, PHP_EOL;
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes all stored pastes (regardless of expiration)
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function _delete_all()
|
||||
{
|
||||
$ids = $this->_store->getAllPastes();
|
||||
foreach ($ids as $pasteid) {
|
||||
echo "Deleting paste ID: $pasteid" . PHP_EOL;
|
||||
$this->_store->delete($pasteid);
|
||||
}
|
||||
exit("All pastes successfully deleted" . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes empty directories, if current storage model uses Filesystem
|
||||
*
|
||||
@@ -124,13 +153,15 @@ class Administration
|
||||
{
|
||||
echo <<<'EOT'
|
||||
Usage:
|
||||
administration [--delete <paste id> | --empty-dirs | --help | --purge | --statistics]
|
||||
administration [--delete <paste id> | --delete-all | --empty-dirs | --help | --list-ids | --purge | --statistics]
|
||||
|
||||
Options:
|
||||
-d, --delete deletes the requested paste ID
|
||||
--delete-all deletes all paste IDs
|
||||
-e, --empty-dirs removes empty directories (only if Filesystem storage is
|
||||
configured)
|
||||
-h, --help displays this help message
|
||||
-l, --list-ids lists all paste IDs
|
||||
-p, --purge purge all expired pastes
|
||||
-s, --statistics reads all stored pastes and comments and reports statistics
|
||||
EOT, PHP_EOL;
|
||||
@@ -177,7 +208,8 @@ EOT, PHP_EOL;
|
||||
self::_help(2);
|
||||
}
|
||||
|
||||
$this->_opts = getopt('hd:eps', array('help', 'delete:', 'empty-dirs', 'purge', 'statistics'));
|
||||
$this->_opts = getopt('hd:epsl', array('help', 'delete:', 'empty-dirs', 'purge', 'statistics', 'list-ids', 'delete-all'));
|
||||
|
||||
if (!$this->_opts) {
|
||||
self::_error_echo('unsupported arguments given');
|
||||
echo PHP_EOL;
|
||||
@@ -308,6 +340,12 @@ EOT, PHP_EOL;
|
||||
$class = 'PrivateBin\\Data\\' . $this->_conf->getKey('class', 'model');
|
||||
$this->_store = new $class($this->_conf->getSection('model_options'));
|
||||
|
||||
if ($this->_option('l', 'list-ids') !== null) {
|
||||
$this->_list_ids();
|
||||
}
|
||||
if ($this->_option(null, 'delete-all') !== null) {
|
||||
$this->_delete_all();
|
||||
}
|
||||
if (($pasteId = $this->_option('d', 'delete')) !== null) {
|
||||
$this->_delete($pasteId);
|
||||
}
|
||||
|
||||
@@ -42,13 +42,28 @@ defaultformatter = "plaintext"
|
||||
; size limit per paste or comment in bytes, defaults to 10 Mebibytes
|
||||
sizelimit = 10485760
|
||||
|
||||
; template to include, default is "bootstrap" (tpl/bootstrap.php), also
|
||||
; by default PrivateBin use "bootstrap" template (tpl/bootstrap.php).
|
||||
; Optionally you can enable the template selection menu, which uses
|
||||
; a session cookie to store the choice until the browser is closed.
|
||||
templateselection = false
|
||||
|
||||
; List of available for selection templates when "templateselection" option is enabled
|
||||
availabletemplates[] = "bootstrap"
|
||||
availabletemplates[] = "bootstrap-page"
|
||||
availabletemplates[] = "bootstrap-dark"
|
||||
availabletemplates[] = "bootstrap-dark-page"
|
||||
availabletemplates[] = "bootstrap-compact"
|
||||
availabletemplates[] = "bootstrap-compact-page"
|
||||
availabletemplates[] = "bootstrap5"
|
||||
availabletemplates[] = "page"
|
||||
|
||||
; set the template your installs defaults to, defaults to "bootstrap" (tpl/bootstrap.php), also
|
||||
; available are "page" (tpl/page.php), the classic ZeroBin style and several
|
||||
; bootstrap variants: "bootstrap-dark", "bootstrap-compact", "bootstrap-page",
|
||||
; which can be combined with "-dark" and "-compact" for "bootstrap-dark-page"
|
||||
; and finally "bootstrap-compact-page" - previews at:
|
||||
; which can be combined with "-dark" and "-compact" for "bootstrap-dark-page",
|
||||
; "bootstrap-compact-page" and finally "bootstrap5" (tpl/bootstrap5.php) - previews at:
|
||||
; https://privatebin.info/screenshots.html
|
||||
template = "bootstrap"
|
||||
; template = "bootstrap"
|
||||
|
||||
; (optional) info text to display
|
||||
; use single, instead of double quotes for HTML attributes
|
||||
@@ -103,7 +118,15 @@ languageselection = false
|
||||
; for details.
|
||||
; - The 'wasm-unsafe-eval' is used to enable webassembly support (used for zlib
|
||||
; compression). You can remove it if compression doesn't need to be supported.
|
||||
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-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"
|
||||
; - The 'unsafe-inline' style-src is used by Chrome when displaying PDF previews
|
||||
; and can be omitted if attachment upload is disabled (which is the default).
|
||||
; See https://issues.chromium.org/issues/343754409
|
||||
; - To allow displaying PDF previews in Firefox or Chrome, sandboxing must also
|
||||
; get turned off. The following CSP allows PDF previews:
|
||||
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; img-src 'self' data: blob:; media-src blob:; object-src blob:"
|
||||
;
|
||||
; The recommended and default used CSP is:
|
||||
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; 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"
|
||||
|
||||
; stay compatible with PrivateBin Alpha 0.19, less secure
|
||||
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of
|
||||
|
||||
@@ -26,9 +26,8 @@
|
||||
"require" : {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"jdenticon/jdenticon": "1.0.2",
|
||||
"mlocati/ip-lib": "1.18.1",
|
||||
"symfony/polyfill-ctype": "^1.31",
|
||||
"symfony/polyfill-php80": "^1.31",
|
||||
"mlocati/ip-lib": "1.20.0",
|
||||
"symfony/polyfill-php80": "1.31.0",
|
||||
"yzalis/identicon": "2.0.0"
|
||||
},
|
||||
"suggest" : {
|
||||
|
||||
105
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0fdf0f08646fa2a4cf9c076131f529f5",
|
||||
"content-hash": "6c7e6dea19e8bfd5641b220cb68c4b65",
|
||||
"packages": [
|
||||
{
|
||||
"name": "jdenticon/jdenticon",
|
||||
@@ -57,16 +57,16 @@
|
||||
},
|
||||
{
|
||||
"name": "mlocati/ip-lib",
|
||||
"version": "1.18.1",
|
||||
"version": "1.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mlocati/ip-lib.git",
|
||||
"reference": "08bb43b4949069c543ebdf099a6b2c322d0172ab"
|
||||
"reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/08bb43b4949069c543ebdf099a6b2c322d0172ab",
|
||||
"reference": "08bb43b4949069c543ebdf099a6b2c322d0172ab",
|
||||
"url": "https://api.github.com/repos/mlocati/ip-lib/zipball/fd45fc3bf08ed6c7e665e2e70562082ac954afd4",
|
||||
"reference": "fd45fc3bf08ed6c7e665e2e70562082ac954afd4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -112,7 +112,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/mlocati/ip-lib/issues",
|
||||
"source": "https://github.com/mlocati/ip-lib/tree/1.18.1"
|
||||
"source": "https://github.com/mlocati/ip-lib/tree/1.20.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -124,86 +124,7 @@
|
||||
"type": "other"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-29T15:44:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.31.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"provide": {
|
||||
"ext-ctype": "*"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-ctype": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/polyfill",
|
||||
"name": "symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Ctype\\": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gert de Pagter",
|
||||
"email": "BackEndTea@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for ctype functions",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"ctype",
|
||||
"polyfill",
|
||||
"portable"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-09T11:45:10+00:00"
|
||||
"time": "2025-02-04T17:30:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
@@ -416,16 +337,16 @@
|
||||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.12.1",
|
||||
"version": "1.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -464,7 +385,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -472,7 +393,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-08T17:47:46+00:00"
|
||||
"time": "2025-02-12T12:17:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
||||
*/
|
||||
|
||||
#attachmentPreview {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#attachmentPreview img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
|
||||
@@ -22,7 +22,6 @@ for more information.
|
||||
### Minimal Requirements
|
||||
|
||||
- PHP version 7.3 or above
|
||||
- ctype extension
|
||||
- GD extension (when using identicon or vizhash icons, jdenticon works without it)
|
||||
- zlib extension
|
||||
- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php)
|
||||
@@ -201,7 +200,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.7.6');
|
||||
INSERT INTO prefix_config VALUES('VERSION', '1.7.7');
|
||||
```
|
||||
|
||||
In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns
|
||||
|
||||
41
i18n/ar.json
@@ -164,17 +164,25 @@
|
||||
"EiB": "إكسابايت",
|
||||
"ZiB": "زيتابايت",
|
||||
"YiB": "يوتابايت",
|
||||
"kB": "كيلوبايت",
|
||||
"MB": "ميجابايت",
|
||||
"GB": "جيجابايت",
|
||||
"TB": "تيرابايت",
|
||||
"PB": "بيتابايت",
|
||||
"EB": "إكسابايت",
|
||||
"ZB": "زيتابايت",
|
||||
"YB": "يوتابايت",
|
||||
"Format": "التنسيق",
|
||||
"Plain Text": "نص عادي",
|
||||
"Source Code": "كود مصدر",
|
||||
"Markdown": "ماركداون",
|
||||
"Download attachment": "تنزيل المرفقات",
|
||||
"Download attachment": "نزّل المرفق",
|
||||
"Cloned: '%s'": "مستنسخ: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "تم إرفاق المِلَفّ المستنسخ '%s' بهذا اللصق.",
|
||||
"Attach a file": "إرفاق مِلَفّ",
|
||||
"The cloned file '%s' was attached to this paste.": "تم إرفاق الملف المستنسخ '%s' بهذا اللصق.",
|
||||
"Attach a file": "أرفق ملف",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "بدلاً من ذلك، اسحب ملفًا وأسقطه أو الصق صورة من الحافظة",
|
||||
"File too large, to display a preview. Please download the attachment.": "المِلَفّ كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.",
|
||||
"Remove attachment": "إزالة المرفق",
|
||||
"File too large, to display a preview. Please download the attachment.": "الملف كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.",
|
||||
"Remove attachment": "أزِل المرفق",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "متصفحك لا يدعم رفع الملفات المشفرة. الرجاء استخدام متصفح أحدث.",
|
||||
"Invalid attachment.": "مرفق غير صحيح.",
|
||||
"Options": "الخيارات",
|
||||
@@ -215,16 +223,17 @@
|
||||
"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.",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "لا يمكن عرض اللصق احرقه بعد قراءته إلا مرة واحدة عند تحميله. هل تريد فتحه الآن؟",
|
||||
"Yes, see it": "نعم، حمله",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "يمكن عرض هذه الرسالة السرية مرة واحدة فقط. هل ترغب في رؤيتها الآن؟",
|
||||
"Yes, see it": "نعم، دعني اراها",
|
||||
"Dark Mode": "الوضع الداكن",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Error compressing paste, due to missing WebAssembly support.": "خطأ في ضغط اللصق، بسبب فقدان دعم WebAssembly.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "خطأ في فك ضغط اللصق، متصفحك لا يدعم WebAssembly. الرجاء استخدام متصفح آخر لعرض هذه اللصقة.",
|
||||
"Start over": "ابدأ من جديد",
|
||||
"Paste copied to clipboard": "نُسخ اللصق إلى الحافظة",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "لنسخ اللصق انقر على زر النسخ أو استخدم اختصار الحافظة <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "نسخ الرابط",
|
||||
"Link copied to clipboard": "نُسخ الرابط إلى الحافظة",
|
||||
"Paste text": "لصق النص",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "مفتاح التبويب يعمل كشخصية (انقر <kbd>Ctrl</kbd>+<kbd>m</kbd> أو <kbd>Esc</kbd> للتبديل)",
|
||||
"Theme": "السمة"
|
||||
}
|
||||
|
||||
11
i18n/bg.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Формат",
|
||||
"Plain Text": "Чист текст",
|
||||
"Source Code": "Изходен код",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ca.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Text sense format",
|
||||
"Source Code": "Codi font",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/co.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "Eo",
|
||||
"ZiB": "Zo",
|
||||
"YiB": "Yo",
|
||||
"kB": "ko",
|
||||
"MB": "Mo",
|
||||
"GB": "Go",
|
||||
"TB": "To",
|
||||
"PB": "Po",
|
||||
"EB": "Eo",
|
||||
"ZB": "Zo",
|
||||
"YB": "Yo",
|
||||
"Format": "Furmatu",
|
||||
"Plain Text": "Testu in chjaru",
|
||||
"Source Code": "Codice di fonte",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Cupià u liame",
|
||||
"Link copied to clipboard": "U liame hè statu cupiatu in u preme’papei",
|
||||
"Paste text": "Testu di l’appiccicu",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cum’è un caratteru (Appughjate nant’à <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cum’è un caratteru (Appughjate nant’à <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)",
|
||||
"Theme": "Tema"
|
||||
}
|
||||
|
||||
11
i18n/cs.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formát",
|
||||
"Plain Text": "Prostý text",
|
||||
"Source Code": "Zdrojový kód",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/de.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Nur Text",
|
||||
"Source Code": "Quellcode",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Verknüpfung kopieren",
|
||||
"Link copied to clipboard": "Verknüpfung wurde in die Zwischenablage kopiert.",
|
||||
"Paste text": "Text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/el.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Μορφοποίηση",
|
||||
"Plain Text": "Απλό κείμενο",
|
||||
"Source Code": "Πηγαίος Κώδικας",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/en.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/es.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formato",
|
||||
"Plain Text": "Texto sin formato",
|
||||
"Source Code": "Código fuente",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
25
i18n/et.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formaat",
|
||||
"Plain Text": "Lihttekst",
|
||||
"Source Code": "Lähtekood",
|
||||
@@ -215,16 +223,17 @@
|
||||
"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.",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?",
|
||||
"Yes, see it": "Yes, see it",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "Seda turvalist sõnumit saab kuvada vaid ühe korra. \nKas soovid seda näha nüüd?",
|
||||
"Yes, see it": "Jah, vaata seda",
|
||||
"Dark Mode": "Tume režiim",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"Start over": "Alusta uuesti",
|
||||
"Paste copied to clipboard": "Kleebe kopeeriti lõikelauale",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Copy link": "Kopeeri link",
|
||||
"Link copied to clipboard": "Link kopeeriti lõikelauale",
|
||||
"Paste text": "Kleebi tekst",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Teema"
|
||||
}
|
||||
|
||||
27
i18n/fi.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formaatti",
|
||||
"Plain Text": "Perusteksti",
|
||||
"Source Code": "Lähdekoodi",
|
||||
@@ -218,13 +226,14 @@
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "Tämä salainen viesti voidaan näyttää vain kerran. Haluatko nähdä sen nyt?",
|
||||
"Yes, see it": "Kyllä, näet sen",
|
||||
"Dark Mode": "Tumma tila",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Virhe pakattaessa pastea, koska WebAssembly-tuki puuttuu.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Virhe pasten purkamisessa, selaimesi ei tue WebAssemblyä. Ole hyvä ja käytä toista selainta nähdäksesi tämä paste.",
|
||||
"Start over": "Aloita alusta",
|
||||
"Paste copied to clipboard": "Paste kopioitu leikepöydälle",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Voit kopioida pasten painamalla kopioi-painiketta tai käyttämällä leikepöydän oikotietä <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Kopioi linkki",
|
||||
"Link copied to clipboard": "Linkki kopioitu leikepöydälle",
|
||||
"Paste text": "Liitä teksti",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulaattori toimii merkkinä (Paina <kbd>Ctrl</kbd>+<kbd>m</kbd> tai <kbd>Esc</kbd> vaihtaaksesi)",
|
||||
"Theme": "Teema"
|
||||
}
|
||||
|
||||
19
i18n/fr.json
@@ -151,7 +151,7 @@
|
||||
"server error or not responding": "Le serveur ne répond pas ou a rencontré une erreur",
|
||||
"Could not post comment: %s": "Impossible de poster le commentaire : %s",
|
||||
"Sending paste…": "Envoi du paste…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur <kbd>Ctrl</kbd>+<kbd>c</kbd> pour copier)</span>",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd> pour copier)</span>",
|
||||
"Delete data": "Supprimer les données du paste",
|
||||
"Could not create paste: %s": "Impossible de créer le paste : %s",
|
||||
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Impossible de déchiffrer le paste : Clé de déchiffrement manquante dans l'URL (Avez-vous utilisé un redirecteur ou un site de réduction d'URL qui supprime une partie de l'URL ?)",
|
||||
@@ -164,6 +164,14 @@
|
||||
"EiB": "Eio",
|
||||
"ZiB": "Zio",
|
||||
"YiB": "Yio",
|
||||
"kB": "ko",
|
||||
"MB": "Mo",
|
||||
"GB": "Go",
|
||||
"TB": "To",
|
||||
"PB": "Po",
|
||||
"EB": "Eo",
|
||||
"ZB": "Zo",
|
||||
"YB": "Yo",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Texte brut",
|
||||
"Source Code": "Code source",
|
||||
@@ -221,10 +229,11 @@
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Erreur lors de la compression du paste, en raison du support de WebAssembly manquant.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Erreur lors de la décompression du paste, votre navigateur ne supporte pas WebAssembly. Veuillez utiliser un autre navigateur pour voir ce paste.",
|
||||
"Start over": "Recommencer",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Paste copied to clipboard": "Paste copié dans le presse-papier",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Pour copier-coller appuyer sur le bouton To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copier le lien",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Link copied to clipboard": "Lien copié dans le presse-papier",
|
||||
"Paste text": "Texte du paste",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "La touche de tabulation sert de caractère (Presser <kbd>Ctrl</kbd>+<kbd>m</kbd> ou <kbd>Esc</kbd> pour basculer)",
|
||||
"Theme": "Thème"
|
||||
}
|
||||
|
||||
137
i18n/he.json
@@ -1,7 +1,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 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.",
|
||||
"More information on the <a href=\"https://privatebin.info/\">project page</a>.": "More information on the <a href=\"https://privatebin.info/\">project page</a>.",
|
||||
"%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 הוא שירות pastebin מקוון, מינימליסטי וקוד פתוח, שבו לשרת אין כל ידע על המידע שהודבק. הנתונים מוצפנים ומפוענחים %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": "כיוון שבורות היא ברכה",
|
||||
"Paste does not exist, has expired or has been deleted.": "ההדבקה לא קיימת, פגה או נמחקה.",
|
||||
"%s requires php %s or above to work. Sorry.": "%s דורש PHP %s כדי לפעול.",
|
||||
@@ -29,7 +29,7 @@
|
||||
"Create": "צור",
|
||||
"Clone": "שכפול",
|
||||
"Raw text": "טקסט גולמי",
|
||||
"Expires": "Expires",
|
||||
"Expires": "יפוג ב",
|
||||
"Burn after reading": "קוראים-שורפים",
|
||||
"Open discussion": "פתיחת דיון",
|
||||
"Password (recommended)": "ססמה (מומלץ)",
|
||||
@@ -53,11 +53,11 @@
|
||||
],
|
||||
"%d hours": [
|
||||
"שעה אחת",
|
||||
"%d hours (1st plural)",
|
||||
"%d hours (2nd plural)",
|
||||
"%d hours (3rd plural)",
|
||||
"%d hours (4th plural)",
|
||||
"%d hours (5th plural)"
|
||||
"%d שעות",
|
||||
"%d שעות",
|
||||
"%d שעות",
|
||||
"%d שעות",
|
||||
"%d שעות"
|
||||
],
|
||||
"%d days": [
|
||||
"יום אחד",
|
||||
@@ -94,44 +94,44 @@
|
||||
"Never": "לעולם לא",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "הערה: זהו שירות בדקה: המידע לא ישמר.",
|
||||
"This document will expire in %d seconds.": [
|
||||
"This document will expire in %d second. (singular)",
|
||||
"This document will expire in %d seconds. (1st plural)",
|
||||
"This document will expire in %d seconds. (2nd plural)",
|
||||
"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)"
|
||||
"מסמך זה יפוג בעוד שנייה אחת.",
|
||||
"מסמך זה יפוג בעוד %d שניות.",
|
||||
"מסמך זה יפוג בעוד %d שניות.",
|
||||
"מסמך זה יפוג בעוד %d שניות.",
|
||||
"מסמך זה יפוג בעוד %d שניות.",
|
||||
"מסמך זה יפוג בעוד %d שניות."
|
||||
],
|
||||
"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)"
|
||||
"מסמך זה יפוג בעוד דקה אחת.",
|
||||
"מסמך זה יפוג בעוד %d דקות.",
|
||||
"מסמך זה יפוג בעוד %d דקות.",
|
||||
"מסמך זה יפוג בעוד %d דקות.",
|
||||
"מסמך זה יפוג בעוד %d דקות.",
|
||||
"מסמך זה יפוג בעוד %d דקות."
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"This document will expire in %d hour. (singular)",
|
||||
"This document will expire in %d hours. (1st plural)",
|
||||
"This document will expire in %d hours. (2nd plural)",
|
||||
"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)"
|
||||
"מסמך זה יפוג בעוד שעה אחת.",
|
||||
"מסמך זה יפוג בעוד %d שעות.",
|
||||
"מסמך זה יפוג בעוד %d שעות.",
|
||||
"מסמך זה יפוג בעוד %d שעות.",
|
||||
"מסמך זה יפוג בעוד %d שעות.",
|
||||
"מסמך זה יפוג בעוד %d שעות."
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"This document will expire in %d day. (singular)",
|
||||
"This document will expire in %d days. (1st plural)",
|
||||
"This document will expire in %d days. (2nd plural)",
|
||||
"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)"
|
||||
"מסמך זה יפוג בעוד יום אחד.",
|
||||
"מסמך זה יפוג בעוד %d ימים.",
|
||||
"מסמך זה יפוג בעוד %d ימים.",
|
||||
"מסמך זה יפוג בעוד %d ימים.",
|
||||
"מסמך זה יפוג בעוד %d ימים.",
|
||||
"מסמך זה יפוג בעוד %d ימים."
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"This document will expire in %d month. (singular)",
|
||||
"This document will expire in %d months. (1st plural)",
|
||||
"This document will expire in %d months. (2nd plural)",
|
||||
"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)"
|
||||
"מסמך זה יפוג בעוד חודש אחד.",
|
||||
"מסמך זה יפוג בעוד %d חודשים.",
|
||||
"מסמך זה יפוג בעוד %d חודשים.",
|
||||
"מסמך זה יפוג בעוד %d חודשים.",
|
||||
"מסמך זה יפוג בעוד %d חודשים.",
|
||||
"מסמך זה יפוג בעוד %d חודשים."
|
||||
],
|
||||
"Please enter the password for this paste:": "נא למלא את הססמה להדבקה הזו:",
|
||||
"Could not decrypt data (Wrong key?)": "לא ניתן לפענח את הנתונים (מפתח שגוי?)",
|
||||
@@ -164,35 +164,43 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "פורמט",
|
||||
"Plain Text": "טקסט פשוט",
|
||||
"Source Code": "קוד מקור",
|
||||
"Markdown": "Markdown",
|
||||
"Download attachment": "הורדת קובץ מצורף",
|
||||
"Cloned: '%s'": "שוכפל: '%s'",
|
||||
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
|
||||
"The cloned file '%s' was attached to this paste.": "הקובץ '%s' שהועתק צורף להדבקה זו.",
|
||||
"Attach a file": "צירוף קובץ",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard",
|
||||
"File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.",
|
||||
"Remove attachment": "Remove attachment",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "Your browser does not support uploading encrypted files. Please use a newer browser.",
|
||||
"alternatively drag & drop a file or paste an image from the clipboard": "לחלופין, ניתן לגרור ולשחרר קובץ או להדביק תמונה מהלוח.",
|
||||
"File too large, to display a preview. Please download the attachment.": "הקובץ גדול מדי כדי להציג תצוגה מקדימה. אנא הורד את הקובץ המצורף.",
|
||||
"Remove attachment": "הסר קובץ מצורף",
|
||||
"Your browser does not support uploading encrypted files. Please use a newer browser.": "הדפדפן שלך אינו תומך בהעלאת קבצים מוצפנים. אנא השתמש בדפדפן עדכני יותר.",
|
||||
"Invalid attachment.": "קובץ מצורף שגוי.",
|
||||
"Options": "אפשרויות",
|
||||
"Shorten URL": "קיצור כתובת",
|
||||
"Editor": "עורך",
|
||||
"Preview": "תצוגה מקדימה",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
|
||||
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s דורש שה-PATH יסתיים ב-\"%s\". אנא עדכן את ה-PATH בקובץ index.php שלך.",
|
||||
"Decrypt": "פענוח",
|
||||
"Enter password": "נא למלא ססמה",
|
||||
"Loading…": "בטעינה…",
|
||||
"Decrypting paste…": "ההדבקה מפוענחת…",
|
||||
"Preparing new paste…": "ההדבקה החדשה בהכנות…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "אם הודעה זו לא נעלמת, אנא עיין ב- <a href=\"%s\">שאלות נפוצות אלה למידע לפתרון בעיות</a>.",
|
||||
"+++ no paste text +++": "+++ אין טקסט להדבקה +++",
|
||||
"Could not get paste data: %s": "לא ניתן לקבל את נתוני ההדבקה: %s",
|
||||
"QR code": "קוד QR",
|
||||
"This website is using an insecure HTTP connection! Please use it only for testing.": "האתר הזה משתמש בחיבור HTTP בלתי מאובטח! נא להשתמש בזה לבדיקות בלבד.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "יש מידע נוסף <a href=\"%s\">ברשומה הזאת בשו״ת</a>.",
|
||||
"For more information <a href=\"%s\">see this FAQ entry</a>.": "לפרטים נוספים <a href=\"%s\">עיין ברשומת שאלות נפוצות זו</a>.",
|
||||
"Your browser may require an HTTPS connection to support the WebCrypto API. Try <a href=\"%s\">switching to HTTPS</a>.": "יכול להיות שהדפדפן שלך ידרוש חיבור HTTPS כדי לתמוך ב־API של WebCrypto. כדי לנסות <a href=\"%s\">לעבור ל־HTTPS</a>.",
|
||||
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "הדפדפן שלך לא תומך ב־WebAssembly שמשמש לדחיסת zlib. אפשר ליצור מסמכים בלתי מוצפנים אך אין אפשרות לקרוא מסמכים מוצפנים.",
|
||||
"waiting on user to provide a password": "בהמתנה למילוי הססמה מצד המשתמש",
|
||||
@@ -209,22 +217,23 @@
|
||||
"Close": "סגירה",
|
||||
"Encrypted note on %s": "%sהערה מוצפנת ב־",
|
||||
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.",
|
||||
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
|
||||
"Save paste": "Save paste",
|
||||
"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.",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?",
|
||||
"Yes, see it": "Yes, see it",
|
||||
"Dark Mode": "Dark Mode",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Error compressing paste, due to missing WebAssembly support.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"URL shortener may expose your decrypt key in URL.": "שירות קיצור כתובת URL עשוי לחשוף את מפתח הפענוח שלך בכתובת ה-URL.",
|
||||
"Save paste": "שמור הדבקה",
|
||||
"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.",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "ההודעה הסודית הזו יכולה להוצג רק פעם אחת. האם תרצה לראות אותה עכשיו?",
|
||||
"Yes, see it": "כן, ראה אותה",
|
||||
"Dark Mode": "מצב כהה",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "שגיאה בדחיסת ההדבקה, עקב חוסר תמיכה ב-WebAssembly.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "שגיאה בהפקת ההדבקה, הדפדפן שלך לא תומך ב-WebAssembly. אנא השתמש בדפדפן אחר כדי לצפות בהדבקה זו.",
|
||||
"Start over": "להתחיל מחדש",
|
||||
"Paste copied to clipboard": "ההדבקה הועתקה ללוח",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "על מנת להעתיק את ההדבקה, לחץ על כפתור ההעתקה או השתמש בקיצור הדרך ללוח <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "העתק קישור",
|
||||
"Link copied to clipboard": "הקישור הועתק ללוח",
|
||||
"Paste text": "הדבק טקסט",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "משטח ה-tab פועל כמקש תו (לחץ על <kbd>Ctrl</kbd>+<kbd>m</kbd> או <kbd>Esc</kbd> להחלפה)",
|
||||
"Theme": "נושא"
|
||||
}
|
||||
|
||||
11
i18n/hi.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/hu.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formátum",
|
||||
"Plain Text": "Egyszerű szöveg",
|
||||
"Source Code": "Forráskód",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/id.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Teks Biasa",
|
||||
"Source Code": "Kode Sumber",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/it.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formato",
|
||||
"Plain Text": "Solo Testo",
|
||||
"Source Code": "Codice Sorgente",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copia il link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Testo del messaggio",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ja.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "形式",
|
||||
"Plain Text": "プレーンテキスト",
|
||||
"Source Code": "ソースコード",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ko.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ku.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/la.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/lt.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formatas",
|
||||
"Plain Text": "Grynasis tekstas",
|
||||
"Source Code": "Pirminis kodas",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
25
i18n/nl.json
@@ -143,7 +143,7 @@
|
||||
"Avatar generated from IP address": "Anonieme avatar (van het IP-adres)",
|
||||
"Add comment": "Commentaar toevoegen",
|
||||
"Optional nickname…": "Optionele bijnaam…",
|
||||
"Post comment": "Plaats een commentaar",
|
||||
"Post comment": "Plaats commentaar",
|
||||
"Sending comment…": "Commentaar verzenden…",
|
||||
"Comment posted.": "Commentaar geplaatst.",
|
||||
"Could not refresh display: %s": "Kon de weergave niet vernieuwen: %s",
|
||||
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formaat",
|
||||
"Plain Text": "Platte tekst",
|
||||
"Source Code": "Broncode",
|
||||
@@ -220,11 +228,12 @@
|
||||
"Dark Mode": "Donkere modus",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Fout bij het comprimeren van notitie door ontbrekende ondersteuning voor WebAssembly.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Fout bij het decomprimeren van de notitie, uw browser ondersteunt WebAssembly niet. Gebruik een andere browser om deze notitie te bekijken.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Start over": "Opnieuw beginnen",
|
||||
"Paste copied to clipboard": "Notitie gekopieerd naar klembord",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Om te kopiëren en plakken druk je op de knop Kopiëren of gebruik je de sneltoets op het klembord <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Kopieer link",
|
||||
"Link copied to clipboard": "Link gekopieerd naar klembord",
|
||||
"Paste text": "Tekst plakken",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortoets dient als teken (gebruik <kbd>Ctrl</kbd>+<kbd>m</kbd> of <kbd>Esc</kbd> om te schakelen)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/no.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Ren Tekst",
|
||||
"Source Code": "Kildekode",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/oc.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "Eio",
|
||||
"ZiB": "Zio",
|
||||
"YiB": "Yio",
|
||||
"kB": "ko",
|
||||
"MB": "Mo",
|
||||
"GB": "Go",
|
||||
"TB": "To",
|
||||
"PB": "Po",
|
||||
"EB": "Eo",
|
||||
"ZB": "Zo",
|
||||
"YB": "Yo",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Tèxte brut",
|
||||
"Source Code": "Còdi font",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
23
i18n/pl.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Czysty tekst",
|
||||
"Source Code": "Kod źródłowy",
|
||||
@@ -220,11 +228,12 @@
|
||||
"Dark Mode": "Ciemny motyw",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "Błąd kompresowania wklejenia przez brak obsługi WebAssembly.",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "Błąd dekompresowania wklejenia przez brak obsługi WebAssembly przez przeglądarkę. Użyj innej przeglądarki, aby zobaczyć to wklejenie.",
|
||||
"Start over": "Start over",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Start over": "Zacznij od nowa",
|
||||
"Paste copied to clipboard": "Wklejka skopiowana do schowka",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "Aby skopiować wklejkę, naciśnij przycisk kopiowania lub użyj skrótu schowka <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Kopiuj link",
|
||||
"Link copied to clipboard": "Link został skopiowany do schowka",
|
||||
"Paste text": "Wklej tekst",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Klawisz Tabulatora służy jako znak (przytrzymaj <kbd>Ctrl</kbd>+<kbd>m</kbd> lub <kbd>Esc</kbd> aby przełączać)",
|
||||
"Theme": "Motyw"
|
||||
}
|
||||
|
||||
11
i18n/pt.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formato",
|
||||
"Plain Text": "Texto sem formato",
|
||||
"Source Code": "Código fonte",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ro.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formatare",
|
||||
"Plain Text": "Text neformatat",
|
||||
"Source Code": "Cod sursă",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/ru.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "Эбайт",
|
||||
"ZiB": "Збайт",
|
||||
"YiB": "Йбайт",
|
||||
"kB": "кбайт",
|
||||
"MB": "Мбайт",
|
||||
"GB": "Гбайт",
|
||||
"TB": "Тбайт",
|
||||
"PB": "Пбайт",
|
||||
"EB": "Эбайт",
|
||||
"ZB": "Збайт",
|
||||
"YB": "Йбайт",
|
||||
"Format": "Формат",
|
||||
"Plain Text": "Обычный текст",
|
||||
"Source Code": "Исходный код",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Скопировать ссылку",
|
||||
"Link copied to clipboard": "Ссылка скопирована в буфер обмена",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Тема"
|
||||
}
|
||||
|
||||
11
i18n/sk.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Formát",
|
||||
"Plain Text": "Čistý text",
|
||||
"Source Code": "Zdrojový kód",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/sl.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EB",
|
||||
"ZiB": "ZB",
|
||||
"YiB": "YB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Surov tekst",
|
||||
"Source Code": "Odprta koda",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/sv.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Plain Text",
|
||||
"Source Code": "Source Code",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/th.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "รูปแบบ",
|
||||
"Plain Text": "ข้อความล้วน",
|
||||
"Source Code": "ซอร์สโค้ด",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/tr.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "Format",
|
||||
"Plain Text": "Düz Yazı",
|
||||
"Source Code": "Kaynak Kodu",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
11
i18n/uk.json
@@ -164,6 +164,14 @@
|
||||
"EiB": "Ебайт",
|
||||
"ZiB": "Збайт",
|
||||
"YiB": "Йбайт",
|
||||
"kB": "кбайт",
|
||||
"MB": "Мбайт",
|
||||
"GB": "Гбайт",
|
||||
"TB": "Тбайт",
|
||||
"PB": "Пбайт",
|
||||
"EB": "Ебайт",
|
||||
"ZB": "Збайт",
|
||||
"YB": "Йбайт",
|
||||
"Format": "Формат",
|
||||
"Plain Text": "Звичайний текст",
|
||||
"Source Code": "Вихідний код",
|
||||
@@ -226,5 +234,6 @@
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
|
||||
"Theme": "Theme"
|
||||
}
|
||||
|
||||
63
i18n/zh.json
@@ -7,12 +7,12 @@
|
||||
"%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] 部分。",
|
||||
"Please wait %d seconds between each post.": [
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。",
|
||||
"每 %d 秒只能粘贴一次。"
|
||||
"每 %d 秒只能创建一次粘贴。",
|
||||
"每 %d 秒只能创建一次粘贴。",
|
||||
"每 %d 秒只能创建一次粘贴。",
|
||||
"每 %d 秒只能创建一次粘贴。",
|
||||
"每 %d 秒只能创建一次粘贴。",
|
||||
"每 %d 秒只能创建一次粘贴。"
|
||||
],
|
||||
"Paste is limited to %s of encrypted data.": "对于加密数据,上限为 %s。",
|
||||
"Invalid data.": "无效的数据。",
|
||||
@@ -46,10 +46,10 @@
|
||||
"%d minutes": [
|
||||
"%d 分钟",
|
||||
"%d 分钟",
|
||||
"%d 秒",
|
||||
"%d 秒",
|
||||
"%d 秒",
|
||||
"%d 秒"
|
||||
"%d 分钟",
|
||||
"%d 分钟",
|
||||
"%d 分钟",
|
||||
"%d 分钟"
|
||||
],
|
||||
"%d hours": [
|
||||
"%d 小时",
|
||||
@@ -94,7 +94,7 @@
|
||||
"Never": "永不过期",
|
||||
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "注意:这是一个测试服务,数据随时可能被删除。如果你滥用这个服务的话,小猫咪会死的。",
|
||||
"This document will expire in %d seconds.": [
|
||||
"这份文档将在一秒后过期。",
|
||||
"这份文档将在 %d 秒后过期。",
|
||||
"这份文档将在 %d 秒后过期。",
|
||||
"这份文档将在 %d 秒后过期。",
|
||||
"这份文档将在 %d 秒后过期。",
|
||||
@@ -102,7 +102,7 @@
|
||||
"这份文档将在 %d 秒后过期。"
|
||||
],
|
||||
"This document will expire in %d minutes.": [
|
||||
"这份文档将在一分钟后过期。",
|
||||
"这份文档将在 %d 分钟后过期。",
|
||||
"这份文档将在 %d 分钟后过期。",
|
||||
"这份文档将在 %d 分钟后过期。",
|
||||
"这份文档将在 %d 分钟后过期。",
|
||||
@@ -110,7 +110,7 @@
|
||||
"这份文档将在 %d 分钟后过期。"
|
||||
],
|
||||
"This document will expire in %d hours.": [
|
||||
"这份文档将在一小时后过期。",
|
||||
"这份文档将在 %d 小时后过期。",
|
||||
"这份文档将在 %d 小时后过期。",
|
||||
"这份文档将在 %d 小时后过期。",
|
||||
"这份文档将在 %d 小时后过期。",
|
||||
@@ -118,7 +118,7 @@
|
||||
"这份文档将在 %d 小时后过期。"
|
||||
],
|
||||
"This document will expire in %d days.": [
|
||||
"这份文档将在一天后过期。",
|
||||
"这份文档将在 %d 天后过期。",
|
||||
"这份文档将在 %d 天后过期。",
|
||||
"这份文档将在 %d 天后过期。",
|
||||
"这份文档将在 %d 天后过期。",
|
||||
@@ -126,7 +126,7 @@
|
||||
"这份文档将在 %d 天后过期。"
|
||||
],
|
||||
"This document will expire in %d months.": [
|
||||
"这份文档将在一个月后过期。",
|
||||
"这份文档将在 %d 个月后过期。",
|
||||
"这份文档将在 %d 个月后过期。",
|
||||
"这份文档将在 %d 个月后过期。",
|
||||
"这份文档将在 %d 个月后过期。",
|
||||
@@ -136,7 +136,7 @@
|
||||
"Please enter the password for this paste:": "请输入这份粘贴内容的密码:",
|
||||
"Could not decrypt data (Wrong key?)": "无法解密数据(密钥错误?)",
|
||||
"Could not delete the paste, it was not stored in burn after reading mode.": "无法删除此粘贴内容,它没有以阅后即焚模式保存。",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "看!仔!细!了!不要关闭窗口,否则你再也见不到这条消息了。",
|
||||
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "睁大眼睛看清楚!不要关闭窗口,否则你再也见不到这条消息了。",
|
||||
"Could not decrypt comment; Wrong key?": "无法解密评论;密钥错误?",
|
||||
"Reply": "回复",
|
||||
"Anonymous": "匿名",
|
||||
@@ -150,7 +150,7 @@
|
||||
"unknown status": "未知状态",
|
||||
"server error or not responding": "服务器错误或无回应",
|
||||
"Could not post comment: %s": "无法发送评论: %s",
|
||||
"Sending paste…": "粘贴内容提交中…",
|
||||
"Sending paste…": "正在发送粘贴内容…",
|
||||
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit <kbd>Ctrl</kbd>+<kbd>c</kbd> to copy)</span>": "您粘贴内容的链接是 <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 <kbd>Ctrl</kbd>+<kbd>c</kbd> 以复制)</span>",
|
||||
"Delete data": "删除数据",
|
||||
"Could not create paste: %s": "无法创建粘贴:%s",
|
||||
@@ -164,6 +164,14 @@
|
||||
"EiB": "EiB",
|
||||
"ZiB": "ZiB",
|
||||
"YiB": "YiB",
|
||||
"kB": "kB",
|
||||
"MB": "MB",
|
||||
"GB": "GB",
|
||||
"TB": "TB",
|
||||
"PB": "PB",
|
||||
"EB": "EB",
|
||||
"ZB": "ZB",
|
||||
"YB": "YB",
|
||||
"Format": "格式",
|
||||
"Plain Text": "纯文本",
|
||||
"Source Code": "源代码",
|
||||
@@ -185,8 +193,8 @@
|
||||
"Decrypt": "解密",
|
||||
"Enter password": "输入密码",
|
||||
"Loading…": "载入中…",
|
||||
"Decrypting paste…": "正在解密",
|
||||
"Preparing new paste…": "正在准备新的粘贴内容",
|
||||
"Decrypting paste…": "正在解密…",
|
||||
"Preparing new paste…": "正在准备新的粘贴内容…",
|
||||
"In case this message never disappears please have a look at <a href=\"%s\">this FAQ for information to troubleshoot</a>.": "如果此消息一直存在,请参考 <a href=\"%s\">这里的 FAQ(英文版)</a>排除故障。",
|
||||
"+++ no paste text +++": "+++ 无粘贴内容 +++",
|
||||
"Could not get paste data: %s": "无法获取粘贴数据:%s",
|
||||
@@ -200,7 +208,7 @@
|
||||
"Retry": "重试",
|
||||
"Showing raw text…": "显示原始文字…",
|
||||
"Notice:": "注意:",
|
||||
"This link will expire after %s.": "这个链接将会在 %s 过期。",
|
||||
"This link will expire after %s.": "此链接将会在 %s 过期。",
|
||||
"This link can only be accessed once, do not use back or refresh button in your browser.": "此链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。",
|
||||
"Link:": "链接:",
|
||||
"Recipient may become aware of your timezone, convert time to UTC?": "收件人可能会知道您的时区,将时间转换为 UTC?",
|
||||
@@ -217,14 +225,15 @@
|
||||
"Error parsing YOURLS response.": "解析 YOURLS 响应时出错。",
|
||||
"This secret message can only be displayed once. Would you like to see it now?": "读取粘贴后只能在加载时显示一次。您想现在打开吗?",
|
||||
"Yes, see it": "是的,加载它",
|
||||
"Dark Mode": "暗黑模式",
|
||||
"Dark Mode": "夜间模式",
|
||||
"Error compressing paste, due to missing WebAssembly support.": "由于缺少 WebAssembly 支持,在压缩粘贴时出错。",
|
||||
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "解压粘贴时出错,您的浏览器不支持 WebAssembly。请使用其他浏览器查看此粘贴。",
|
||||
"Start over": "重新开始",
|
||||
"Paste copied to clipboard": "Paste copied to clipboard",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
|
||||
"Copy link": "Copy link",
|
||||
"Link copied to clipboard": "Link copied to clipboard",
|
||||
"Paste text": "Paste text",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
|
||||
"Paste copied to clipboard": "粘贴内容已复制到剪贴板",
|
||||
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "要复制粘贴内容,请点击复制按钮或使用快捷键 <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>。",
|
||||
"Copy link": "复制链接",
|
||||
"Link copied to clipboard": "链接已复制到剪贴板",
|
||||
"Paste text": "粘贴文本",
|
||||
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tab 键可作为字符(按 <kbd>Ctrl</kbd>+<kbd>m</kbd> 或 <kbd>Esc</kbd> 切换开关)",
|
||||
"Theme": "主题"
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 749 B After Width: | Height: | Size: 741 B |
|
Before Width: | Height: | Size: 266 B After Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 253 B After Width: | Height: | Size: 217 B |
BIN
img/icon_new.png
|
Before Width: | Height: | Size: 157 B After Width: | Height: | Size: 104 B |
BIN
img/icon_qr.png
|
Before Width: | Height: | Size: 299 B After Width: | Height: | Size: 200 B |
BIN
img/icon_raw.png
|
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 174 B |
|
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 144 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 149 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
17
js/common.js
@@ -5,19 +5,18 @@ global.assert = require('assert');
|
||||
global.jsc = require('jsverify');
|
||||
global.jsdom = require('jsdom-global');
|
||||
global.cleanup = global.jsdom();
|
||||
global.URL = require('jsdom-url').URL;
|
||||
global.fs = require('fs');
|
||||
global.WebCrypto = require('@peculiar/webcrypto').Crypto;
|
||||
|
||||
// application libraries to test
|
||||
global.$ = global.jQuery = require('./jquery-3.7.1');
|
||||
global.RawDeflate = require('./rawinflate-0.3').RawDeflate;
|
||||
global.zlib = require('./zlib-1.3.1').zlib;
|
||||
global.zlib = require('./zlib-1.3.1-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.2.4');
|
||||
global.DOMPurify = require('./purify-3.2.6');
|
||||
global.baseX = require('./base-x-4.0.0').baseX;
|
||||
global.Legacy = require('./legacy').Legacy;
|
||||
require('./bootstrap-3.4.1');
|
||||
@@ -79,8 +78,16 @@ function parseMime(line) {
|
||||
}
|
||||
|
||||
// common testing helper functions
|
||||
exports.atob = atob;
|
||||
exports.btoa = btoa;
|
||||
// as of jsDOM 22 the base64 functions provided in the DOM are more restrictive
|
||||
// than browser implementation and throw when being passed invalid unicode
|
||||
// codepoints - as we use these in the encryption with binary data, we need
|
||||
// these to be character encoding agnostic
|
||||
exports.atob = function(encoded) {
|
||||
return Buffer.from(encoded, 'base64').toString('binary');
|
||||
}
|
||||
exports.btoa = function(text) {
|
||||
return Buffer.from(text, 'binary').toString('base64');
|
||||
}
|
||||
|
||||
// provides random lowercase characters from a to z
|
||||
exports.jscA2zString = function() {
|
||||
|
||||
2016
js/package-lock.json
generated
@@ -1,18 +1,16 @@
|
||||
{
|
||||
"name": "privatebin",
|
||||
"version": "1.7.6",
|
||||
"version": "1.7.7",
|
||||
"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": {
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"jsdom": "^9.12.0",
|
||||
"jsdom-global": "^2.1.1",
|
||||
"jsdom-url": "^2.2.1",
|
||||
"jsverify": "^0.8.3",
|
||||
"@peculiar/webcrypto": "^1.1.1"
|
||||
"@peculiar/webcrypto": "^1.5.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"jsverify": "^0.8.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha",
|
||||
|
||||
417
js/privatebin.js
@@ -58,7 +58,6 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
const purifyHtmlConfig = {
|
||||
ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
|
||||
SAFE_FOR_JQUERY: true,
|
||||
USE_PROFILES: {
|
||||
html: true
|
||||
}
|
||||
@@ -592,6 +591,32 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
return expirationDate;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert Bytes to KiB/MiB/GiB
|
||||
*
|
||||
* @name Helper.formatBytes
|
||||
* @function
|
||||
*
|
||||
* @param {number} bytes
|
||||
* @return {string}
|
||||
*/
|
||||
me.formatBytes = function (bytes)
|
||||
{
|
||||
let result = '';
|
||||
const kilobyte = 1024;
|
||||
const decimalPoint = 2;
|
||||
const sizes = [I18n._('B'), I18n._('KiB'), I18n._('MiB'), I18n._('GiB')];
|
||||
const index = Math.floor(Math.log(bytes) / Math.log(kilobyte));
|
||||
|
||||
if (bytes > 0) {
|
||||
result = parseFloat((bytes / Math.pow(kilobyte, index)).toFixed(decimalPoint)) + ' ' + sizes[index];
|
||||
} else {
|
||||
result = `0 ${I18n._('B')}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* resets state, used for unit testing
|
||||
*
|
||||
@@ -2537,11 +2562,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// show preview
|
||||
PasteViewer.setText($message.val());
|
||||
if (AttachmentViewer.hasAttachmentData()) {
|
||||
const attachment = AttachmentViewer.getAttachment();
|
||||
AttachmentViewer.handleBlobAttachmentPreview(
|
||||
AttachmentViewer.getAttachmentPreview(),
|
||||
attachment[0], attachment[1]
|
||||
);
|
||||
const attachmentsData = AttachmentViewer.getAttachmentsData();
|
||||
|
||||
attachmentsData.forEach(attachmentData => {
|
||||
const mimeType = AttachmentViewer.getAttachmentMimeType(attachmentData);
|
||||
|
||||
AttachmentViewer.handleBlobAttachmentPreview(
|
||||
AttachmentViewer.getAttachmentPreview(),
|
||||
attachmentData, mimeType
|
||||
);
|
||||
});
|
||||
|
||||
AttachmentViewer.showAttachment();
|
||||
}
|
||||
PasteViewer.run();
|
||||
|
||||
@@ -2926,14 +2958,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
const AttachmentViewer = (function () {
|
||||
const me = {};
|
||||
|
||||
let $attachmentLink,
|
||||
$attachmentPreview,
|
||||
let $attachmentPreview,
|
||||
$attachment,
|
||||
attachmentData,
|
||||
file,
|
||||
attachmentsData = [],
|
||||
files,
|
||||
$fileInput,
|
||||
$dragAndDropFileName,
|
||||
attachmentHasPreview = false,
|
||||
$dragAndDropFileNames,
|
||||
$dropzone;
|
||||
|
||||
/**
|
||||
@@ -2975,26 +3005,30 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
me.setAttachment = function(attachmentData, fileName)
|
||||
{
|
||||
// skip, if attachments got disabled
|
||||
if (!$attachmentLink || !$attachmentPreview) return;
|
||||
if (!$attachment || !$attachmentPreview) return;
|
||||
|
||||
// data URI format: data:[<mimeType>][;base64],<data>
|
||||
|
||||
const template = Model.getTemplate('attachment');
|
||||
const attachmentLink = template.find('a');
|
||||
|
||||
// position in data URI string of where data begins
|
||||
const base64Start = attachmentData.indexOf(',') + 1;
|
||||
// position in data URI string of where mimeType ends
|
||||
const mimeTypeEnd = attachmentData.indexOf(';');
|
||||
|
||||
// extract mimeType
|
||||
const mimeType = attachmentData.substring(5, mimeTypeEnd);
|
||||
const mimeType = me.getAttachmentMimeType(attachmentData);
|
||||
|
||||
// extract data and convert to binary
|
||||
const rawData = attachmentData.substring(base64Start);
|
||||
const decodedData = rawData.length > 0 ? atob(rawData) : '';
|
||||
|
||||
let blobUrl = getBlobUrl(decodedData, mimeType);
|
||||
$attachmentLink.attr('href', blobUrl);
|
||||
attachmentLink.attr('href', blobUrl);
|
||||
|
||||
if (typeof fileName !== 'undefined') {
|
||||
$attachmentLink.attr('download', fileName);
|
||||
attachmentLink.attr('download', fileName);
|
||||
|
||||
const fileSize = Helper.formatBytes(decodedData.length);
|
||||
template.append(`(${fileName}, ${fileSize})`);
|
||||
}
|
||||
|
||||
// sanitize SVG preview
|
||||
@@ -3009,6 +3043,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
blobUrl = getBlobUrl(sanitizedData, mimeType);
|
||||
}
|
||||
|
||||
template.removeClass('hidden');
|
||||
$attachment.append(template);
|
||||
|
||||
me.handleBlobAttachmentPreview($attachmentPreview, blobUrl, mimeType);
|
||||
};
|
||||
|
||||
@@ -3025,7 +3062,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
|
||||
$attachment.removeClass('hidden');
|
||||
|
||||
if (attachmentHasPreview) {
|
||||
if (me.hasAttachmentPreview()) {
|
||||
$attachmentPreview.removeClass('hidden');
|
||||
}
|
||||
};
|
||||
@@ -3046,11 +3083,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
}
|
||||
me.hideAttachment();
|
||||
me.hideAttachmentPreview();
|
||||
$attachmentLink.removeAttr('href');
|
||||
$attachmentLink.removeAttr('download');
|
||||
$attachmentLink.off('click');
|
||||
$attachment.html('');
|
||||
$attachmentPreview.html('');
|
||||
$dragAndDropFileName.text('');
|
||||
$dragAndDropFileNames.html('');
|
||||
|
||||
AttachmentViewer.removeAttachmentData();
|
||||
};
|
||||
@@ -3065,8 +3100,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
me.removeAttachmentData = function()
|
||||
{
|
||||
file = undefined;
|
||||
attachmentData = undefined;
|
||||
files = undefined;
|
||||
attachmentsData = [];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3077,9 +3112,21 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
me.clearDragAndDrop = function()
|
||||
{
|
||||
$dragAndDropFileName.text('');
|
||||
$dragAndDropFileNames.html('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Print file names added via drag & drop
|
||||
*
|
||||
* @name AttachmentViewer.printDragAndDropFileNames
|
||||
* @private
|
||||
* @function
|
||||
* @param {array} fileNames
|
||||
*/
|
||||
function printDragAndDropFileNames(fileNames) {
|
||||
$dragAndDropFileNames.html(fileNames.join("<br>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* hides the attachment
|
||||
*
|
||||
@@ -3108,6 +3155,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* checks if has any attachment preview
|
||||
*
|
||||
* @name AttachmentViewer.hasAttachmentPreview
|
||||
* @function
|
||||
* @return {JQuery}
|
||||
*/
|
||||
me.hasAttachmentPreview = function()
|
||||
{
|
||||
return $attachmentPreview.children().length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if there is an attachment displayed
|
||||
*
|
||||
@@ -3119,8 +3178,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
if (!$attachment.length) {
|
||||
return false;
|
||||
}
|
||||
const link = $attachmentLink.prop('href');
|
||||
return (typeof link !== 'undefined' && link !== '');
|
||||
return [...$attachment.children()].length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3140,20 +3198,38 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
};
|
||||
|
||||
/**
|
||||
* return the attachment
|
||||
* return the attachments
|
||||
*
|
||||
* @name AttachmentViewer.getAttachment
|
||||
* @name AttachmentViewer.getAttachments
|
||||
* @function
|
||||
* @returns {array}
|
||||
*/
|
||||
me.getAttachment = function()
|
||||
me.getAttachments = function()
|
||||
{
|
||||
return [
|
||||
$attachmentLink.prop('href'),
|
||||
$attachmentLink.prop('download')
|
||||
];
|
||||
return [...$attachment.find('a')].map(link => (
|
||||
[
|
||||
$(link).prop('href'),
|
||||
$(link).prop('download')
|
||||
]
|
||||
));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get attachment mime type
|
||||
*
|
||||
* @name AttachmentViewer.getAttachmentMimeType
|
||||
* @function
|
||||
* @param {string} attachmentData - Base64 string
|
||||
*/
|
||||
me.getAttachmentMimeType = function(attachmentData)
|
||||
{
|
||||
// position in data URI string of where mimeType ends
|
||||
const mimeTypeEnd = attachmentData.indexOf(';');
|
||||
|
||||
// extract mimeType
|
||||
return attachmentData.substring(5, mimeTypeEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* moves the attachment link to another element
|
||||
*
|
||||
@@ -3162,27 +3238,33 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
* @name AttachmentViewer.moveAttachmentTo
|
||||
* @function
|
||||
* @param {jQuery} $element - the wrapper/container element where this should be moved to
|
||||
* @param {array} attachment - attachment data
|
||||
* @param {string} label - the text to show (%s will be replaced with the file name), will automatically be translated
|
||||
*/
|
||||
me.moveAttachmentTo = function($element, label)
|
||||
me.moveAttachmentTo = function($element, attachment, label)
|
||||
{
|
||||
const attachmentLink = $(document.createElement('a'))
|
||||
.addClass('alert-link')
|
||||
.prop('href', attachment[0])
|
||||
.prop('download', attachment[1]);
|
||||
|
||||
// move elemement to new place
|
||||
$attachmentLink.appendTo($element);
|
||||
attachmentLink.appendTo($element);
|
||||
|
||||
// update text - ensuring no HTML is inserted into the text node
|
||||
I18n._($attachmentLink, label, $attachmentLink.attr('download'));
|
||||
I18n._(attachmentLink, label, attachment[1]);
|
||||
};
|
||||
|
||||
/**
|
||||
* read file data as data URL using the FileReader API
|
||||
* read files data as data URL using the FileReader API
|
||||
*
|
||||
* @name AttachmentViewer.readFileData
|
||||
* @private
|
||||
* @function
|
||||
* @param {object} loadedFile (optional) loaded file object
|
||||
* @param {FileList[]} loadedFiles (optional) loaded files array
|
||||
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/FileReader#readAsDataURL()}
|
||||
*/
|
||||
function readFileData(loadedFile) {
|
||||
function readFileData(loadedFiles) {
|
||||
if (typeof FileReader === 'undefined') {
|
||||
// revert loading status…
|
||||
me.hideAttachment();
|
||||
@@ -3191,28 +3273,35 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
if (loadedFile === undefined) {
|
||||
loadedFile = $fileInput[0].files[0];
|
||||
$dragAndDropFileName.text('');
|
||||
if (loadedFiles === undefined) {
|
||||
loadedFiles = [...$fileInput[0].files];
|
||||
me.clearDragAndDrop();
|
||||
} else {
|
||||
$dragAndDropFileName.text(loadedFile.name);
|
||||
const fileNames = loadedFiles.map((loadedFile => loadedFile.name));
|
||||
printDragAndDropFileNames(fileNames);
|
||||
}
|
||||
|
||||
if (typeof loadedFile !== 'undefined') {
|
||||
file = loadedFile;
|
||||
fileReader.onload = function (event) {
|
||||
const dataURL = event.target.result;
|
||||
attachmentData = dataURL;
|
||||
if (typeof loadedFiles !== 'undefined') {
|
||||
files = loadedFiles;
|
||||
loadedFiles.forEach(loadedFile => {
|
||||
const fileReader = new FileReader();
|
||||
|
||||
if (Editor.isPreview()) {
|
||||
me.handleAttachmentPreview($attachmentPreview, dataURL);
|
||||
$attachmentPreview.removeClass('hidden');
|
||||
}
|
||||
fileReader.onload = function (event) {
|
||||
const dataURL = event.target.result;
|
||||
if (dataURL) {
|
||||
attachmentsData.push(dataURL);
|
||||
}
|
||||
|
||||
TopNav.highlightFileupload();
|
||||
};
|
||||
fileReader.readAsDataURL(loadedFile);
|
||||
if (Editor.isPreview()) {
|
||||
me.handleAttachmentPreview($attachmentPreview, dataURL);
|
||||
$attachmentPreview.removeClass('hidden');
|
||||
}
|
||||
|
||||
TopNav.highlightFileupload();
|
||||
};
|
||||
|
||||
fileReader.readAsDataURL(loadedFile);
|
||||
});
|
||||
} else {
|
||||
me.removeAttachmentData();
|
||||
}
|
||||
@@ -3228,16 +3317,17 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
* @argument {string} mime type
|
||||
*/
|
||||
me.handleBlobAttachmentPreview = function ($targetElement, blobUrl, mimeType) {
|
||||
if (blobUrl) {
|
||||
attachmentHasPreview = true;
|
||||
const alreadyIncludesCurrentAttachment = $targetElement.find(`[src='${blobUrl}']`).length > 0;
|
||||
|
||||
if (blobUrl && !alreadyIncludesCurrentAttachment) {
|
||||
if (mimeType.match(/^image\//i)) {
|
||||
$targetElement.html(
|
||||
$targetElement.append(
|
||||
$(document.createElement('img'))
|
||||
.attr('src', blobUrl)
|
||||
.attr('class', 'img-thumbnail')
|
||||
);
|
||||
} else if (mimeType.match(/^video\//i)) {
|
||||
$targetElement.html(
|
||||
$targetElement.append(
|
||||
$(document.createElement('video'))
|
||||
.attr('controls', 'true')
|
||||
.attr('autoplay', 'true')
|
||||
@@ -3248,7 +3338,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
.attr('src', blobUrl))
|
||||
);
|
||||
} else if (mimeType.match(/^audio\//i)) {
|
||||
$targetElement.html(
|
||||
$targetElement.append(
|
||||
$(document.createElement('audio'))
|
||||
.attr('controls', 'true')
|
||||
.attr('autoplay', 'true')
|
||||
@@ -3261,15 +3351,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// Fallback for browsers, that don't support the vh unit
|
||||
const clientHeight = $(window).height();
|
||||
|
||||
$targetElement.html(
|
||||
$targetElement.append(
|
||||
$(document.createElement('embed'))
|
||||
.attr('src', blobUrl)
|
||||
.attr('type', 'application/pdf')
|
||||
.attr('class', 'pdfPreview')
|
||||
.css('height', clientHeight)
|
||||
);
|
||||
} else {
|
||||
attachmentHasPreview = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -3302,14 +3390,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
}
|
||||
|
||||
if ($fileInput) {
|
||||
const file = evt.dataTransfer.files[0];
|
||||
const files = [...evt.dataTransfer.files];
|
||||
//Clear the file input:
|
||||
$fileInput.wrap('<form>').closest('form').get(0).reset();
|
||||
$fileInput.unwrap();
|
||||
//Only works in Chrome:
|
||||
//fileInput[0].files = e.dataTransfer.files;
|
||||
|
||||
readFileData(file);
|
||||
readFileData(files);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3363,23 +3451,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
/**
|
||||
* getter for attachment data
|
||||
*
|
||||
* @name AttachmentViewer.getAttachmentData
|
||||
* @name AttachmentViewer.getAttachmentsData
|
||||
* @function
|
||||
* @return {jQuery}
|
||||
* @return {string[]}
|
||||
*/
|
||||
me.getAttachmentData = function () {
|
||||
return attachmentData;
|
||||
};
|
||||
|
||||
/**
|
||||
* getter for attachment link
|
||||
*
|
||||
* @name AttachmentViewer.getAttachmentLink
|
||||
* @function
|
||||
* @return {jQuery}
|
||||
*/
|
||||
me.getAttachmentLink = function () {
|
||||
return $attachmentLink;
|
||||
me.getAttachmentsData = function () {
|
||||
return attachmentsData;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3394,14 +3471,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
};
|
||||
|
||||
/**
|
||||
* getter for file data, returns the file contents
|
||||
* getter for files data, returns the file list
|
||||
*
|
||||
* @name AttachmentViewer.getFile
|
||||
* @name AttachmentViewer.getFiles
|
||||
* @function
|
||||
* @return {string}
|
||||
* @return {FileList[]}
|
||||
*/
|
||||
me.getFile = function () {
|
||||
return file;
|
||||
me.getFiles = function () {
|
||||
return files;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3415,9 +3492,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
me.init = function()
|
||||
{
|
||||
$attachment = $('#attachment');
|
||||
$dragAndDropFileName = $('#dragAndDropFileName');
|
||||
$dragAndDropFileNames = $('#dragAndDropFileName');
|
||||
$dropzone = $('#dropzone');
|
||||
$attachmentLink = $('#attachment a') || $('<a>');
|
||||
if($attachment.length) {
|
||||
$attachmentPreview = $('#attachmentPreview');
|
||||
|
||||
@@ -3478,6 +3554,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
{
|
||||
const $source = $(event.target);
|
||||
|
||||
// show all reply buttons
|
||||
$commentContainer.find('button').removeClass('hidden');
|
||||
|
||||
// hide the current reply button
|
||||
$source.addClass('hidden');
|
||||
|
||||
// clear input
|
||||
$replyMessage.val('');
|
||||
$replyNickname.val('');
|
||||
@@ -3933,7 +4015,26 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
*/
|
||||
function setLanguage(event)
|
||||
{
|
||||
document.cookie = 'lang=' + $(event.target).data('lang') + '; SameSite=Lax; Secure';
|
||||
let lang = $(event.target).data('lang') || event.target.value;
|
||||
|
||||
document.cookie = 'lang=' + lang + '; SameSite=Lax; Secure';
|
||||
window.location.reload();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* save the template in a cookie and reloads the page
|
||||
*
|
||||
* @name TopNav.setTemplate
|
||||
* @private
|
||||
* @function
|
||||
* @param {Event} event
|
||||
*/
|
||||
function setTemplate(event)
|
||||
{
|
||||
let template = $(event.target).data('template') || event.target.value;
|
||||
|
||||
document.cookie = 'template=' + template + '; SameSite=Lax; Secure';
|
||||
window.location.reload();
|
||||
event.preventDefault();
|
||||
}
|
||||
@@ -4625,7 +4726,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// bootstrap template drop down
|
||||
$('#language ul.dropdown-menu li a').click(setLanguage);
|
||||
// page template drop down
|
||||
$('#language select option').click(setLanguage);
|
||||
$('#language select').change(setLanguage);
|
||||
|
||||
// bootstrap template drop down
|
||||
$('#template ul.dropdown-menu li a').click(setTemplate);
|
||||
// page template drop down
|
||||
$('#template select').change(setTemplate);
|
||||
|
||||
// bind events
|
||||
$burnAfterReading.change(changeBurnAfterReading);
|
||||
@@ -5106,7 +5212,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
const plainText = Editor.getText(),
|
||||
format = PasteViewer.getFormat(),
|
||||
// the methods may return different values if no files are attached (null, undefined or false)
|
||||
files = TopNav.getFileList() || AttachmentViewer.getFile() || AttachmentViewer.hasAttachment();
|
||||
files = TopNav.getFileList() || AttachmentViewer.getFiles() || AttachmentViewer.hasAttachment();
|
||||
|
||||
// do not send if there is no data
|
||||
if (plainText.length === 0 && !files) {
|
||||
@@ -5146,62 +5252,64 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
PasteViewer.setFormat(format);
|
||||
|
||||
// prepare cypher message
|
||||
let file = AttachmentViewer.getAttachmentData(),
|
||||
let attachmentsData = AttachmentViewer.getAttachmentsData(),
|
||||
cipherMessage = {
|
||||
'paste': plainText
|
||||
};
|
||||
if (typeof file !== 'undefined' && file !== null) {
|
||||
cipherMessage['attachment'] = file;
|
||||
cipherMessage['attachment_name'] = AttachmentViewer.getFile().name;
|
||||
if (attachmentsData.length) {
|
||||
cipherMessage['attachment'] = attachmentsData;
|
||||
cipherMessage['attachment_name'] = AttachmentViewer.getFiles().map((fileInfo => fileInfo.name));
|
||||
} else if (AttachmentViewer.hasAttachment()) {
|
||||
// fall back to cloned part
|
||||
let attachment = AttachmentViewer.getAttachment();
|
||||
cipherMessage['attachment'] = attachment[0];
|
||||
cipherMessage['attachment_name'] = attachment[1];
|
||||
let attachments = AttachmentViewer.getAttachments();
|
||||
cipherMessage['attachment'] = attachments.map(attachment => attachment[0]);
|
||||
cipherMessage['attachment_name'] = attachments.map(attachment => attachment[1]);
|
||||
|
||||
// we need to retrieve data from blob if browser already parsed it in memory
|
||||
if (typeof attachment[0] === 'string' && attachment[0].startsWith('blob:')) {
|
||||
Alert.showStatus(
|
||||
[
|
||||
'Retrieving cloned file \'%s\' from memory...',
|
||||
attachment[1]
|
||||
],
|
||||
'copy'
|
||||
);
|
||||
try {
|
||||
const blobData = await $.ajax({
|
||||
type: 'GET',
|
||||
url: `${attachment[0]}`,
|
||||
processData: false,
|
||||
timeout: 10000,
|
||||
xhrFields: {
|
||||
withCredentials: false,
|
||||
responseType: 'blob'
|
||||
}
|
||||
});
|
||||
if (blobData instanceof window.Blob) {
|
||||
const fileReading = new Promise(function(resolve, reject) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = function (event) {
|
||||
resolve(event.target.result);
|
||||
};
|
||||
fileReader.onerror = function (error) {
|
||||
reject(error);
|
||||
cipherMessage['attachment'] = await Promise.all(cipherMessage['attachment'].map(async (attachment) => {
|
||||
// we need to retrieve data from blob if browser already parsed it in memory
|
||||
if (typeof attachment === 'string' && attachment.startsWith('blob:')) {
|
||||
Alert.showStatus(
|
||||
[
|
||||
'Retrieving cloned file \'%s\' from memory...',
|
||||
attachment[1]
|
||||
],
|
||||
'copy'
|
||||
);
|
||||
try {
|
||||
const blobData = await $.ajax({
|
||||
type: 'GET',
|
||||
url: `${attachment}`,
|
||||
processData: false,
|
||||
timeout: 10000,
|
||||
xhrFields: {
|
||||
withCredentials: false,
|
||||
responseType: 'blob'
|
||||
}
|
||||
fileReader.readAsDataURL(blobData);
|
||||
});
|
||||
cipherMessage['attachment'] = await fileReading;
|
||||
} else {
|
||||
const error = 'Cannot process attachment data.';
|
||||
Alert.showError(error);
|
||||
throw new TypeError(error);
|
||||
if (blobData instanceof window.Blob) {
|
||||
const fileReading = new Promise(function(resolve, reject) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = function (event) {
|
||||
resolve(event.target.result);
|
||||
};
|
||||
fileReader.onerror = function (error) {
|
||||
reject(error);
|
||||
}
|
||||
fileReader.readAsDataURL(blobData);
|
||||
});
|
||||
|
||||
return await fileReading;
|
||||
} else {
|
||||
const error = 'Cannot process attachment data.';
|
||||
Alert.showError(error);
|
||||
throw new TypeError(error);
|
||||
}
|
||||
} catch (error) {
|
||||
Alert.showError('Cannot retrieve attachment.');
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
Alert.showError('Cannot retrieve attachment.');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
// encrypt message
|
||||
@@ -5296,7 +5404,15 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
// version 2 paste
|
||||
const pasteMessage = JSON.parse(pastePlain);
|
||||
if (pasteMessage.hasOwnProperty('attachment') && pasteMessage.hasOwnProperty('attachment_name')) {
|
||||
AttachmentViewer.setAttachment(pasteMessage.attachment, pasteMessage.attachment_name);
|
||||
if (Array.isArray(pasteMessage.attachment) && Array.isArray(pasteMessage.attachment_name)) {
|
||||
pasteMessage.attachment.forEach((attachment, key) => {
|
||||
const attachment_name = pasteMessage.attachment_name[key];
|
||||
AttachmentViewer.setAttachment(attachment, attachment_name);
|
||||
});
|
||||
} else {
|
||||
// Continue to process attachment parameters as strings to ensure backward compatibility
|
||||
AttachmentViewer.setAttachment(pasteMessage.attachment, pasteMessage.attachment_name);
|
||||
}
|
||||
AttachmentViewer.showAttachment();
|
||||
}
|
||||
pastePlain = pasteMessage.paste;
|
||||
@@ -5387,6 +5503,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
me.run = function(paste)
|
||||
{
|
||||
Alert.hideMessages();
|
||||
Alert.setCustomHandler(null);
|
||||
Alert.showLoading('Decrypting paste…', 'cloud-download');
|
||||
|
||||
if (typeof paste === 'undefined' || paste.type === 'click') {
|
||||
@@ -5778,10 +5895,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
history.pushState({type: 'clone'}, document.title, Helper.baseUri());
|
||||
|
||||
if (AttachmentViewer.hasAttachment()) {
|
||||
AttachmentViewer.moveAttachmentTo(
|
||||
TopNav.getCustomAttachment(),
|
||||
'Cloned: \'%s\''
|
||||
);
|
||||
const attachments = AttachmentViewer.getAttachments();
|
||||
attachments.forEach(attachment => {
|
||||
AttachmentViewer.moveAttachmentTo(
|
||||
TopNav.getCustomAttachment(),
|
||||
attachment,
|
||||
'Cloned: \'%s\''
|
||||
);
|
||||
});
|
||||
TopNav.hideFileSelector();
|
||||
AttachmentViewer.hideAttachment();
|
||||
// NOTE: it also looks nice without removing the attachment
|
||||
@@ -5789,12 +5910,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
|
||||
AttachmentViewer.hideAttachmentPreview();
|
||||
TopNav.showCustomAttachment();
|
||||
|
||||
// show another status message to make the user aware that the
|
||||
// file was cloned too!
|
||||
// show another status messages to make the user aware that the
|
||||
// files were cloned too!
|
||||
Alert.showStatus(
|
||||
[
|
||||
'The cloned file \'%s\' was attached to this paste.',
|
||||
AttachmentViewer.getAttachment()[1]
|
||||
attachments.map(attachment => attachment[1]).join(', '),
|
||||
],
|
||||
'copy'
|
||||
);
|
||||
|
||||
2
js/purify-3.2.6.js
Normal file
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
var common = require('../common');
|
||||
const common = require('../common');
|
||||
|
||||
describe('AttachmentViewer', function () {
|
||||
describe('setAttachment, showAttachment, removeAttachment, hideAttachment, hideAttachmentPreview, hasAttachment, getAttachment & moveAttachmentTo', function () {
|
||||
@@ -14,11 +14,12 @@ describe('AttachmentViewer', function () {
|
||||
'string',
|
||||
function (mimeType, rawdata, filename, prefix, postfix) {
|
||||
let clean = jsdom(),
|
||||
data = 'data:' + mimeType + ';base64,' + btoa(rawdata),
|
||||
data = 'data:' + mimeType + ';base64,' + common.btoa(rawdata),
|
||||
mimePrefix = mimeType.substring(0, 6),
|
||||
previewSupported = (
|
||||
mimeType.substring(0, 6) === 'image/' ||
|
||||
mimeType.substring(0, 6) === 'audio/' ||
|
||||
mimeType.substring(0, 6) === 'video/' ||
|
||||
mimePrefix === 'image/' ||
|
||||
mimePrefix === 'audio/' ||
|
||||
mimePrefix === 'video/' ||
|
||||
mimeType.match(/\/pdf/i)
|
||||
),
|
||||
results = [],
|
||||
@@ -26,11 +27,14 @@ describe('AttachmentViewer', function () {
|
||||
prefix = prefix.replace(/%(s|d)/g, '%%');
|
||||
postfix = postfix.replace(/%(s|d)/g, '%%');
|
||||
$('body').html(
|
||||
'<div id="attachment" role="alert" class="hidden alert ' +
|
||||
'alert-info"><span class="glyphicon glyphicon-download-' +
|
||||
'alt" aria-hidden="true"></span> <a class="alert-link">' +
|
||||
'Download attachment</a></div><div id="attachmentPrevie' +
|
||||
'w" class="hidden"></div>'
|
||||
'<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>' +
|
||||
'<div id="attachment" class="hidden"></div>' +
|
||||
'<div id="templates">' +
|
||||
'<div id="attachmenttemplate" role="alert" class="attachment hidden alert alert-info">' +
|
||||
'<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>' +
|
||||
'<a class="alert-link">Download attachment</a>' +
|
||||
'</div>' +
|
||||
'</div>'
|
||||
);
|
||||
// mock createObjectURL for jsDOM
|
||||
if (typeof window.URL.createObjectURL === 'undefined') {
|
||||
@@ -43,29 +47,35 @@ describe('AttachmentViewer', function () {
|
||||
)
|
||||
}
|
||||
$.PrivateBin.AttachmentViewer.init();
|
||||
$.PrivateBin.Model.init();
|
||||
results.push(
|
||||
!$.PrivateBin.AttachmentViewer.hasAttachment() &&
|
||||
$('#attachment').hasClass('hidden') &&
|
||||
$('#attachment').children().length === 0 &&
|
||||
$('#attachmenttemplate').hasClass('hidden') &&
|
||||
$('#attachmentPreview').hasClass('hidden')
|
||||
);
|
||||
global.atob = common.atob;
|
||||
if (filename.length) {
|
||||
$.PrivateBin.AttachmentViewer.setAttachment(data, filename);
|
||||
} else {
|
||||
$.PrivateBin.AttachmentViewer.setAttachment(data);
|
||||
}
|
||||
// beyond this point we will get the blob URL instead of the data
|
||||
// // beyond this point we will get the blob URL instead of the data
|
||||
data = window.URL.createObjectURL(data);
|
||||
const attachment = $.PrivateBin.AttachmentViewer.getAttachment();
|
||||
const attachment = $.PrivateBin.AttachmentViewer.getAttachments();
|
||||
results.push(
|
||||
$.PrivateBin.AttachmentViewer.hasAttachment() &&
|
||||
$('#attachment').hasClass('hidden') &&
|
||||
$('#attachment').children().length > 0 &&
|
||||
$('#attachmentPreview').hasClass('hidden') &&
|
||||
attachment[0] === data &&
|
||||
attachment[1] === filename
|
||||
attachment[0][0] === data &&
|
||||
attachment[0][1] === filename
|
||||
);
|
||||
$.PrivateBin.AttachmentViewer.showAttachment();
|
||||
results.push(
|
||||
!$('#attachment').hasClass('hidden') &&
|
||||
$('#attachment').children().length > 0 &&
|
||||
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
|
||||
);
|
||||
$.PrivateBin.AttachmentViewer.hideAttachment();
|
||||
@@ -83,7 +93,7 @@ describe('AttachmentViewer', function () {
|
||||
(previewSupported ? !$('#attachmentPreview').hasClass('hidden') : $('#attachmentPreview').hasClass('hidden'))
|
||||
);
|
||||
let element = $('<div>');
|
||||
$.PrivateBin.AttachmentViewer.moveAttachmentTo(element, prefix + '%s' + postfix);
|
||||
$.PrivateBin.AttachmentViewer.moveAttachmentTo(element, attachment[0], prefix + '%s' + postfix);
|
||||
// messageIDs with links get a relaxed treatment
|
||||
if (prefix.indexOf('<a') === -1 && postfix.indexOf('<a') === -1) {
|
||||
result = $('<textarea>').text((prefix + filename + postfix)).text();
|
||||
@@ -97,16 +107,17 @@ describe('AttachmentViewer', function () {
|
||||
}
|
||||
if (filename.length) {
|
||||
results.push(
|
||||
element.children()[0].href === data &&
|
||||
element.children()[0].getAttribute('download') === filename &&
|
||||
element.children()[0].text === result
|
||||
element.find('a')[0].href === data &&
|
||||
element.find('a')[0].getAttribute('download') === filename &&
|
||||
element.find('a')[0].text === result
|
||||
);
|
||||
} else {
|
||||
results.push(element.children()[0].href === data);
|
||||
results.push(element.find('a')[0].href === data);
|
||||
}
|
||||
$.PrivateBin.AttachmentViewer.removeAttachment();
|
||||
results.push(
|
||||
$('#attachment').hasClass('hidden') &&
|
||||
$('#attachment').children().length === 0 &&
|
||||
$('#attachmentPreview').hasClass('hidden')
|
||||
);
|
||||
clean();
|
||||
|
||||
@@ -19,10 +19,9 @@ describe('Check', function () {
|
||||
}
|
||||
);
|
||||
Legacy.Check.init();
|
||||
const result1 = Legacy.Check.getInit() && !Legacy.Check.getStatus(),
|
||||
result2 = (document.getElementById('errormessage').className !== 'hidden');
|
||||
const result = Legacy.Check.getInit() && !Legacy.Check.getStatus();
|
||||
clean();
|
||||
return result1 && result2;
|
||||
return result;
|
||||
}
|
||||
),
|
||||
{tests: 10});
|
||||
@@ -67,7 +66,10 @@ describe('Check', function () {
|
||||
'url': (secureProtocol ? 'https' : 'http' ) + '://' + domain.join('') + '/'
|
||||
}
|
||||
);
|
||||
window.crypto = new WebCrypto();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
Legacy.Check.init();
|
||||
const result1 = Legacy.Check.getInit() && Legacy.Check.getStatus(),
|
||||
result2 = secureProtocol === (document.getElementById('httpnotice').className === 'hidden');
|
||||
|
||||
@@ -4,120 +4,136 @@ const common = require('../common');
|
||||
describe('CopyToClipboard', function() {
|
||||
this.timeout(30000);
|
||||
|
||||
describe ('Copy paste co clipboard', function () {
|
||||
jsc.property('Copy with button click', common.jscFormats(), 'nestring', async function (format, text) {
|
||||
var clean = jsdom();
|
||||
common.enableClipboard();
|
||||
|
||||
$('body').html(
|
||||
'<div id="placeholder" class="hidden">+++ no paste text ' +
|
||||
'+++</div><div id="prettymessage" class="hidden">' +
|
||||
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
|
||||
'<svg id="copySuccessIcon"></svg></button><pre ' +
|
||||
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
|
||||
'</div><div id="plaintext" class="hidden"></div>'
|
||||
);
|
||||
|
||||
$.PrivateBin.PasteViewer.init();
|
||||
$.PrivateBin.PasteViewer.setFormat(format);
|
||||
$.PrivateBin.PasteViewer.setText(text);
|
||||
$.PrivateBin.PasteViewer.run();
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
|
||||
$('#prettyMessageCopyBtn').trigger('click');
|
||||
describe ('Copy paste to clipboard', function () {
|
||||
jsc.property('Copy with button click',
|
||||
common.jscFormats(),
|
||||
'nestring',
|
||||
async function (format, text) {
|
||||
var clean = jsdom();
|
||||
common.enableClipboard();
|
||||
|
||||
const savedToClipboardText = await navigator.clipboard.readText();
|
||||
|
||||
clean();
|
||||
|
||||
return text === savedToClipboardText;
|
||||
});
|
||||
$('body').html(
|
||||
'<div id="placeholder" class="hidden">+++ no paste text ' +
|
||||
'+++</div><div id="prettymessage" class="hidden">' +
|
||||
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
|
||||
'<svg id="copySuccessIcon"></svg></button><pre ' +
|
||||
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
|
||||
'</div><div id="plaintext" class="hidden"></div>'
|
||||
);
|
||||
|
||||
$.PrivateBin.PasteViewer.init();
|
||||
$.PrivateBin.PasteViewer.setFormat(format);
|
||||
$.PrivateBin.PasteViewer.setText(text);
|
||||
$.PrivateBin.PasteViewer.run();
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
|
||||
$('#prettyMessageCopyBtn').trigger('click');
|
||||
|
||||
const savedToClipboardText = await navigator.clipboard.readText();
|
||||
|
||||
clean();
|
||||
|
||||
return text === savedToClipboardText;
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Unfortunately in JSVerify impossible to check if copy with shortcut when user selected some text on the page
|
||||
* (the copy paste to clipboard should not work in this case) due to lucking window.getSelection() in jsdom.
|
||||
* (the copy paste to clipboard should not work in this case) due to lacking window.getSelection() in jsdom.
|
||||
*/
|
||||
jsc.property('Copy with keyboard shortcut', common.jscFormats(), 'nestring', async function (format, text) {
|
||||
jsc.property('Copy with keyboard shortcut',
|
||||
common.jscFormats(),
|
||||
'nestring',
|
||||
async function (format, text) {
|
||||
var clean = jsdom();
|
||||
common.enableClipboard();
|
||||
|
||||
$('body').html(
|
||||
'<div id="placeholder">+++ no paste text ' +
|
||||
'+++</div><div id="prettymessage" class="hidden">' +
|
||||
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
|
||||
'<svg id="copySuccessIcon"></svg></button><pre ' +
|
||||
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
|
||||
'</div><div id="plaintext" class="hidden"></div>'
|
||||
);
|
||||
|
||||
$.PrivateBin.PasteViewer.init();
|
||||
$.PrivateBin.PasteViewer.setFormat(format);
|
||||
$.PrivateBin.PasteViewer.setText(text);
|
||||
$.PrivateBin.PasteViewer.run();
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
|
||||
$('body').trigger('copy');
|
||||
|
||||
const copiedTextWithoutSelectedText = await navigator.clipboard.readText();
|
||||
|
||||
clean();
|
||||
|
||||
return copiedTextWithoutSelectedText === text;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
jsc.property('Copy link to clipboard',
|
||||
'nestring',
|
||||
async function (text) {
|
||||
var clean = jsdom();
|
||||
common.enableClipboard();
|
||||
|
||||
$('body').html(
|
||||
'<div id="placeholder">+++ no paste text ' +
|
||||
'+++</div><div id="prettymessage" class="hidden">' +
|
||||
'<button type="button" id="prettyMessageCopyBtn"><svg id="copyIcon"></svg>' +
|
||||
'<svg id="copySuccessIcon"></svg></button><pre ' +
|
||||
'id="prettyprint" class="prettyprint linenums:1"></pre>' +
|
||||
'</div><div id="plaintext" class="hidden"></div>'
|
||||
);
|
||||
|
||||
$.PrivateBin.PasteViewer.init();
|
||||
$.PrivateBin.PasteViewer.setFormat(format);
|
||||
$.PrivateBin.PasteViewer.setText(text);
|
||||
$.PrivateBin.PasteViewer.run();
|
||||
|
||||
|
||||
$('body').html('<button id="copyLink"></button>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.setUrl(text);
|
||||
|
||||
$('body').trigger('copy');
|
||||
$('#copyLink').trigger('click');
|
||||
|
||||
const copiedTextWithoutSelectedText = await navigator.clipboard.readText();
|
||||
const copiedText = await navigator.clipboard.readText();
|
||||
|
||||
clean();
|
||||
|
||||
return copiedTextWithoutSelectedText === text;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
jsc.property('Copy link to clipboard', 'nestring', async function (text) {
|
||||
var clean = jsdom();
|
||||
common.enableClipboard();
|
||||
|
||||
$('body').html('<button id="copyLink"></button>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.setUrl(text);
|
||||
|
||||
$('#copyLink').trigger('click');
|
||||
|
||||
const copiedText = await navigator.clipboard.readText();
|
||||
|
||||
clean();
|
||||
|
||||
return text === copiedText;
|
||||
});
|
||||
return text === copiedText;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
describe('Keyboard shortcut hint', function () {
|
||||
jsc.property('Show hint', 'nestring', function (text) {
|
||||
var clean = jsdom();
|
||||
|
||||
$('body').html('<small id="copyShortcutHintText"></small>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.showKeyboardShortcutHint();
|
||||
|
||||
const keyboardShortcutHint = $('#copyShortcutHintText').text();
|
||||
|
||||
clean();
|
||||
|
||||
return keyboardShortcutHint.length > 0;
|
||||
});
|
||||
jsc.property('Show hint',
|
||||
'nestring',
|
||||
function (text) {
|
||||
var clean = jsdom();
|
||||
|
||||
jsc.property('Hide hint', 'nestring', function (text) {
|
||||
var clean = jsdom();
|
||||
|
||||
$('body').html('<small id="copyShortcutHintText">' + text + '</small>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
|
||||
|
||||
const keyboardShortcutHint = $('#copyShortcutHintText').text();
|
||||
|
||||
clean();
|
||||
|
||||
return keyboardShortcutHint.length === 0;
|
||||
});
|
||||
$('body').html('<small id="copyShortcutHintText"></small>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.showKeyboardShortcutHint();
|
||||
|
||||
const keyboardShortcutHint = $('#copyShortcutHintText').text();
|
||||
|
||||
clean();
|
||||
|
||||
return keyboardShortcutHint.length > 0;
|
||||
}
|
||||
);
|
||||
|
||||
jsc.property('Hide hint',
|
||||
'nestring',
|
||||
function (text) {
|
||||
var clean = jsdom();
|
||||
|
||||
$('body').html('<small id="copyShortcutHintText">' + text + '</small>');
|
||||
|
||||
$.PrivateBin.CopyToClipboard.init();
|
||||
$.PrivateBin.CopyToClipboard.hideKeyboardShortcutHint();
|
||||
|
||||
const keyboardShortcutHint = $('#copyShortcutHintText').text();
|
||||
|
||||
clean();
|
||||
|
||||
return keyboardShortcutHint.length === 0;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const common = require('../common');
|
||||
|
||||
describe('CryptTool', function () {
|
||||
describe('cipher & decipher', function () {
|
||||
@@ -15,21 +15,26 @@ describe('CryptTool', function () {
|
||||
'string',
|
||||
'string',
|
||||
async function (key, password, message) {
|
||||
// pause to let async functions conclude
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
let clean = jsdom();
|
||||
const clean = jsdom();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.Controller.initZ();
|
||||
window.crypto = new WebCrypto();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
global.atob = common.atob;
|
||||
global.btoa = common.btoa;
|
||||
message = message.trim();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
const cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
key, password, message, []
|
||||
),
|
||||
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||
key, password, cipherMessage
|
||||
);
|
||||
clean();
|
||||
return message === plaintext;
|
||||
const result = (message === plaintext);
|
||||
if (!result) console.log(plaintext, cipherMessage);
|
||||
return result;
|
||||
}
|
||||
),
|
||||
{tests: 3});
|
||||
@@ -38,15 +43,19 @@ describe('CryptTool', function () {
|
||||
// The below static unit tests are included to ensure deciphering of "classic"
|
||||
// SJCL based pastes still works
|
||||
it(
|
||||
'supports PrivateBin v1 ciphertext (SJCL & browser atob)',
|
||||
function () {
|
||||
'supports PrivateBin v1 ciphertext with password (SJCL & browser atob)',
|
||||
async function () {
|
||||
delete global.Base64;
|
||||
let clean = jsdom();
|
||||
window.crypto = new WebCrypto();
|
||||
const clean = jsdom();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
global.atob = common.atob;
|
||||
|
||||
// Of course you can easily decipher the following texts, if you like.
|
||||
// Bonus points for finding their sources and hidden meanings.
|
||||
return $.PrivateBin.CryptTool.decipher(
|
||||
const paste = await $.PrivateBin.CryptTool.decipher(
|
||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||
// -- "That's amazing. I've got the same combination on my luggage."
|
||||
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||
@@ -77,53 +86,76 @@ describe('CryptTool', function () {
|
||||
'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' +
|
||||
'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
|
||||
'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
|
||||
).then(function (paste1) {
|
||||
$.PrivateBin.CryptTool.decipher(
|
||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||
'', // no password
|
||||
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||
'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
|
||||
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
|
||||
'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
|
||||
'/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
|
||||
'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
|
||||
'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
|
||||
'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
|
||||
'+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
|
||||
'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
|
||||
'24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
|
||||
'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
|
||||
'8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
|
||||
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
|
||||
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
|
||||
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
|
||||
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
|
||||
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
|
||||
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
|
||||
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
|
||||
'MZtmnYpGAtAPg7AUG"}'
|
||||
).then(function (paste2) {
|
||||
clean();
|
||||
assert.ok(
|
||||
paste1.includes('securely packed in iron') &&
|
||||
paste2.includes('Sol is right')
|
||||
);
|
||||
});
|
||||
});
|
||||
);
|
||||
clean();
|
||||
const result = typeof paste === 'string' && paste.includes('securely packed in iron');
|
||||
if (!result) console.log(paste);
|
||||
assert.ok(result);
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'supports ZeroBin ciphertext (SJCL & Base64 1.7)',
|
||||
function () {
|
||||
global.Base64 = require('../base64-1.7').Base64;
|
||||
var clean = jsdom();
|
||||
window.crypto = new WebCrypto();
|
||||
'supports PrivateBin v1 ciphertext no password (SJCL & browser atob)',
|
||||
async function () {
|
||||
delete global.Base64;
|
||||
const clean = jsdom();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.Controller.initZ();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
global.atob = common.atob;
|
||||
|
||||
// Of course you can easily decipher the following texts, if you like.
|
||||
// Bonus points for finding their sources and hidden meanings.
|
||||
return $.PrivateBin.CryptTool.decipher(
|
||||
const paste = await $.PrivateBin.CryptTool.decipher(
|
||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||
'', // no password
|
||||
'{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
|
||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||
'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
|
||||
'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
|
||||
'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
|
||||
'/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
|
||||
'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
|
||||
'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
|
||||
'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
|
||||
'+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
|
||||
'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
|
||||
'24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
|
||||
'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
|
||||
'8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
|
||||
'//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
|
||||
'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
|
||||
'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
|
||||
'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
|
||||
'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
|
||||
'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
|
||||
'99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
|
||||
'MZtmnYpGAtAPg7AUG"}'
|
||||
);
|
||||
clean();
|
||||
const result = typeof paste === 'string' && paste.includes('Sol is right');
|
||||
if (!result) console.log(paste);
|
||||
assert.ok(result);
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'supports ZeroBin ciphertext with password (SJCL & Base64 1.7)',
|
||||
async function () {
|
||||
global.Base64 = require('../base64-1.7').Base64;
|
||||
const clean = jsdom();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
global.atob = common.atob;
|
||||
|
||||
// Of course you can easily decipher the following texts, if you like.
|
||||
// Bonus points for finding their sources and hidden meanings.
|
||||
const paste = await $.PrivateBin.CryptTool.decipher(
|
||||
'6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
|
||||
// -- "That's amazing. I've got the same combination on my luggage."
|
||||
Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
|
||||
@@ -146,54 +178,74 @@ describe('CryptTool', function () {
|
||||
'7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' +
|
||||
'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
|
||||
'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
|
||||
).then(function (paste1) {
|
||||
$.PrivateBin.CryptTool.decipher(
|
||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||
'', // no password
|
||||
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||
'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
|
||||
'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
|
||||
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
|
||||
'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
|
||||
'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
|
||||
'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
|
||||
'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
|
||||
'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
|
||||
'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
|
||||
'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
|
||||
'7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
|
||||
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
|
||||
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
|
||||
).then(function (paste2) {
|
||||
clean();
|
||||
delete global.Base64;
|
||||
assert.ok(
|
||||
paste1.includes('securely packed in iron') &&
|
||||
paste2.includes('Sol is right')
|
||||
);
|
||||
});
|
||||
);
|
||||
clean();
|
||||
delete global.Base64;
|
||||
const result = typeof paste === 'string' && paste.includes('securely packed in iron');
|
||||
if (!result) console.log(paste);
|
||||
assert.ok(result);
|
||||
}
|
||||
);
|
||||
|
||||
it(
|
||||
'supports ZeroBin ciphertext no password (SJCL & Base64 1.7)',
|
||||
async function () {
|
||||
global.Base64 = require('../base64-1.7').Base64;
|
||||
const clean = jsdom();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
global.atob = common.atob;
|
||||
|
||||
const paste = await $.PrivateBin.CryptTool.decipher(
|
||||
's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
|
||||
'', // no password
|
||||
'{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
|
||||
':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
|
||||
'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
|
||||
'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
|
||||
'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
|
||||
'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
|
||||
'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
|
||||
'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
|
||||
'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
|
||||
'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
|
||||
'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
|
||||
'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
|
||||
'7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
|
||||
'7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
|
||||
'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
|
||||
);
|
||||
clean();
|
||||
delete global.Base64;
|
||||
const result = typeof paste === 'string' && paste.includes('Sol is right');
|
||||
if (!result) console.log(paste);
|
||||
assert.ok(result);
|
||||
}
|
||||
);
|
||||
|
||||
it('does not truncate messages', async function () {
|
||||
let message = fs.readFileSync('test/compression-sample.txt', 'utf8'),
|
||||
const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(),
|
||||
clean = jsdom();
|
||||
window.crypto = new WebCrypto();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.Controller.initZ();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
global.atob = common.atob;
|
||||
global.btoa = common.btoa;
|
||||
const cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
'foo', 'bar', message, []
|
||||
),
|
||||
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||
'foo', 'bar', cipherMessage
|
||||
'foo', 'bar', cipherMessage
|
||||
);
|
||||
clean();
|
||||
assert.strictEqual(
|
||||
message,
|
||||
plaintext
|
||||
);
|
||||
const result = (message === plaintext);
|
||||
if (!result) console.log(plaintext, cipherMessage);
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
it('can en- and decrypt a particular message (#260)', function () {
|
||||
@@ -201,8 +253,6 @@ describe('CryptTool', function () {
|
||||
'string',
|
||||
'string',
|
||||
async function (key, password) {
|
||||
// pause to let async functions conclude
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
const message = `
|
||||
1 subgoal
|
||||
|
||||
@@ -225,18 +275,23 @@ isWhile : interp (while expr sBody) (MemElem mem) =
|
||||
======================== ( 1 / 1 )
|
||||
conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
|
||||
`;
|
||||
let clean = jsdom();
|
||||
const clean = jsdom();
|
||||
// ensure zlib is getting loaded
|
||||
$.PrivateBin.Controller.initZ();
|
||||
window.crypto = new WebCrypto();
|
||||
let cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
const cipherMessage = await $.PrivateBin.CryptTool.cipher(
|
||||
key, password, message, []
|
||||
),
|
||||
plaintext = await $.PrivateBin.CryptTool.decipher(
|
||||
key, password, cipherMessage
|
||||
);
|
||||
clean();
|
||||
return message === plaintext;
|
||||
const result = (message === plaintext);
|
||||
if (!result) console.log(plaintext, cipherMessage);
|
||||
return result;
|
||||
}
|
||||
),
|
||||
{tests: 3});
|
||||
@@ -244,23 +299,27 @@ conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
|
||||
});
|
||||
|
||||
describe('getSymmetricKey', function () {
|
||||
this.timeout(30000);
|
||||
var keys = [];
|
||||
this.timeout(10000);
|
||||
let keys = [];
|
||||
|
||||
// the parameter is used to ensure the test is run more then one time
|
||||
jsc.property(
|
||||
'returns random, non-empty keys',
|
||||
'integer',
|
||||
function(counter) {
|
||||
var clean = jsdom();
|
||||
window.crypto = new WebCrypto();
|
||||
var key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
||||
result = (key !== '' && keys.indexOf(key) === -1);
|
||||
keys.push(key);
|
||||
clean();
|
||||
return result;
|
||||
}
|
||||
);
|
||||
it('returns random, non-empty keys', function () {
|
||||
jsc.assert(jsc.forall(
|
||||
'integer',
|
||||
function(counter) {
|
||||
const clean = jsdom();
|
||||
Object.defineProperty(window, 'crypto', {
|
||||
value: new WebCrypto(),
|
||||
writeable: false,
|
||||
});
|
||||
const key = $.PrivateBin.CryptTool.getSymmetricKey(),
|
||||
result = (key !== '' && keys.indexOf(key) === -1);
|
||||
keys.push(key);
|
||||
clean();
|
||||
return result;
|
||||
}
|
||||
),
|
||||
{tests: 10});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -229,30 +229,30 @@ describe('Helper', function () {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
/* TODO test fails since jsDOM version 17 - document.cookie remains empty
|
||||
jsc.property(
|
||||
'returns the requested cookie',
|
||||
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||
jsc.nearray(jsc.nearray(common.jscAlnumString())),
|
||||
function (labels, values) {
|
||||
var selectedKey = '', selectedValue = '',
|
||||
cookieArray = [];
|
||||
let selectedKey = '', selectedValue = '';
|
||||
const clean = jsdom();
|
||||
labels.forEach(function(item, i) {
|
||||
var key = item.join(''),
|
||||
const key = item.join(''),
|
||||
value = (values[i] || values[0]).join('');
|
||||
cookieArray.push(key + '=' + value);
|
||||
document.cookie = key + '=' + value;
|
||||
if (Math.random() < 1 / i || selectedKey === key)
|
||||
{
|
||||
selectedKey = key;
|
||||
selectedValue = value;
|
||||
}
|
||||
});
|
||||
var clean = jsdom('', {cookie: cookieArray}),
|
||||
result = $.PrivateBin.Helper.getCookie(selectedKey);
|
||||
const result = $.PrivateBin.Helper.getCookie(selectedKey);
|
||||
$.PrivateBin.Helper.reset();
|
||||
clean();
|
||||
return result === selectedValue;
|
||||
}
|
||||
);
|
||||
); */
|
||||
});
|
||||
|
||||
describe('baseUri', function () {
|
||||
@@ -290,5 +290,31 @@ describe('Helper', function () {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('formatBytes', function () {
|
||||
jsc.property('returns 0 B for 0 bytes', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(0) === '0 B';
|
||||
});
|
||||
|
||||
jsc.property('formats bytes < 1000 as B', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(500) === '500 B';
|
||||
});
|
||||
|
||||
jsc.property('formats kibibytes correctly', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(1500) === '1.46 KiB';
|
||||
});
|
||||
|
||||
jsc.property('formats mebibytes correctly', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(2 * 1000 * 1000) === '1.91 MiB';
|
||||
});
|
||||
|
||||
jsc.property('formats gibibytes correctly', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(3.45 * 1000 * 1000 * 1000) === '3.21 GiB';
|
||||
});
|
||||
|
||||
jsc.property('rounds to two decimal places', function () {
|
||||
return $.PrivateBin.Helper.formatBytes(1234567) === '1.18 MiB';
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ describe('Model', function () {
|
||||
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();
|
||||
@@ -107,7 +106,6 @@ describe('Model', function () {
|
||||
function (url) {
|
||||
let clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = false;
|
||||
global.URL = require('jsdom-url').URL;
|
||||
try {
|
||||
$.PrivateBin.Model.getPasteId();
|
||||
}
|
||||
@@ -131,7 +129,7 @@ describe('Model', function () {
|
||||
'returns the fragment of a v1 URL',
|
||||
common.jscUrl(),
|
||||
function (url) {
|
||||
url.fragment = common.btoa(url.fragment.padStart(32, '\u0000'));
|
||||
url.fragment = '0OIl'; // any non-base58 string
|
||||
const clean = jsdom('', {url: common.urlToString(url)}),
|
||||
result = $.PrivateBin.Model.getPasteKey();
|
||||
$.PrivateBin.Model.reset();
|
||||
@@ -140,17 +138,17 @@ describe('Model', function () {
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
'returns the v1 fragment stripped of trailing query parts',
|
||||
'returns the fragment stripped of trailing query parts',
|
||||
common.jscUrl(),
|
||||
jsc.array(common.jscHashString()),
|
||||
function (url, trail) {
|
||||
const fragmentString = common.btoa(url.fragment.padStart(32, '\u0000'));
|
||||
url.fragment = 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();
|
||||
return fragmentString === result;
|
||||
return fragment === result;
|
||||
}
|
||||
);
|
||||
jsc.property(
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('Prompt', function () {
|
||||
'returns the password fed into the dialog',
|
||||
'string',
|
||||
function (password) {
|
||||
password = password.replace(/\r+/g, '');
|
||||
password = password.replace(/\r+|\n+/g, '');
|
||||
var clean = jsdom('', {url: 'ftp://example.com/?0000000000000000'});
|
||||
$('body').html(
|
||||
'<div id="passwordmodal" class="modal fade" role="dialog">' +
|
||||
@@ -32,7 +32,8 @@ describe('Prompt', function () {
|
||||
//var result = $.PrivateBin.Prompt.getPassword();
|
||||
var result = $('#passworddecrypt').val();
|
||||
$.PrivateBin.Model.reset();
|
||||
clean();
|
||||
// TODO triggers error messages in jsDOM since version 11
|
||||
//clean();
|
||||
return result === password;
|
||||
}
|
||||
);
|
||||
|
||||
@@ -621,7 +621,7 @@ describe('TopNav', function () {
|
||||
'returns the contents of the password input',
|
||||
'string',
|
||||
function (password) {
|
||||
password = password.replace(/\r+/g, '');
|
||||
password = password.replace(/\r+|\n+/g, '');
|
||||
var results = [];
|
||||
$('body').html(
|
||||
'<nav><div id="navbar"><ul><li><div id="password" ' +
|
||||
@@ -727,11 +727,11 @@ describe('TopNav', function () {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
// TODO triggers error messages in jsDOM since version 12, but passes
|
||||
it(
|
||||
'displays raw text view correctly',
|
||||
function () {
|
||||
const clean = jsdom('', {url: 'https://privatebin.net/?0123456789abcdef#0'});
|
||||
global.URL = require('jsdom-url').URL;
|
||||
$('body').html('<button id="rawtextbutton"></button>');
|
||||
const sample = 'example';
|
||||
$.PrivateBin.PasteViewer.setText(sample);
|
||||
|
||||
@@ -18,11 +18,6 @@ describe('UiHelper', function () {
|
||||
const expected = common.urlToString(url),
|
||||
clean = jsdom('', {url: expected});
|
||||
|
||||
// make window.location.href writable
|
||||
Object.defineProperty(window.location, 'href', {
|
||||
writable: true,
|
||||
value: window.location.href
|
||||
});
|
||||
$.PrivateBin.UiHelper.mockHistoryChange();
|
||||
$.PrivateBin.Helper.reset();
|
||||
var result = window.location.href;
|
||||
@@ -40,11 +35,6 @@ describe('UiHelper', function () {
|
||||
const expected = common.urlToString(url),
|
||||
clean = jsdom('', {url: expected});
|
||||
|
||||
// make window.location.href writable
|
||||
Object.defineProperty(window.location, 'href', {
|
||||
writable: true,
|
||||
value: window.location.href
|
||||
});
|
||||
$.PrivateBin.UiHelper.mockHistoryChange([
|
||||
{type: 'newpaste'}, '', expected
|
||||
]);
|
||||
@@ -57,6 +47,8 @@ describe('UiHelper', function () {
|
||||
});
|
||||
|
||||
describe('reloadHome', function () {
|
||||
// TODO triggers error messages in jsDOM since version 11
|
||||
/*
|
||||
this.timeout(30000);
|
||||
before(function () {
|
||||
$.PrivateBin.Helper.reset();
|
||||
@@ -71,11 +63,6 @@ describe('UiHelper', function () {
|
||||
delete(url.fragment);
|
||||
const expected = common.urlToString(url);
|
||||
|
||||
// make window.location.href writable
|
||||
Object.defineProperty(window.location, 'href', {
|
||||
writable: true,
|
||||
value: window.location.href
|
||||
});
|
||||
$.PrivateBin.UiHelper.reloadHome();
|
||||
$.PrivateBin.Helper.reset();
|
||||
var result = window.location.href;
|
||||
@@ -83,6 +70,7 @@ describe('UiHelper', function () {
|
||||
return expected === result;
|
||||
}
|
||||
);
|
||||
*/
|
||||
});
|
||||
|
||||
describe('isVisible', function () {
|
||||
|
||||
@@ -45,7 +45,18 @@ class Configuration
|
||||
'defaultformatter' => 'plaintext',
|
||||
'syntaxhighlightingtheme' => '',
|
||||
'sizelimit' => 10485760,
|
||||
'templateselection' => false,
|
||||
'template' => 'bootstrap',
|
||||
'availabletemplates' => array(
|
||||
'bootstrap5',
|
||||
'bootstrap',
|
||||
'bootstrap-page',
|
||||
'bootstrap-dark',
|
||||
'bootstrap-dark-page',
|
||||
'bootstrap-compact',
|
||||
'bootstrap-compact-page',
|
||||
'page',
|
||||
),
|
||||
'info' => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.',
|
||||
'notice' => '',
|
||||
'languageselection' => false,
|
||||
@@ -54,7 +65,7 @@ class Configuration
|
||||
'qrcode' => true,
|
||||
'email' => true,
|
||||
'icon' => 'identicon',
|
||||
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-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',
|
||||
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; frame-src blob:; 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',
|
||||
'zerobincompatibility' => false,
|
||||
'httpwarning' => true,
|
||||
'compression' => 'zlib',
|
||||
@@ -108,11 +119,11 @@ class Configuration
|
||||
'js/kjua-0.9.0.js' => 'sha512-CVn7af+vTMBd9RjoS4QM5fpLFEOtBCoB0zPtaqIDC7sF4F8qgUSRFQQpIyEDGsr6yrjbuOLzdf20tkHHmpaqwQ==',
|
||||
'js/legacy.js' => 'sha512-UxW/TOZKon83n6dk/09GsYKIyeO5LeBHokxyIq+r7KFS5KMBeIB/EM7NrkVYIezwZBaovnyNtY2d9tKFicRlXg==',
|
||||
'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
|
||||
'js/privatebin.js' => 'sha512-POa+8KNXFFwJFsqp7r9APmR5Rc1w2l363y+OScSzLCySrHN7UhOOgt1VH/o8mVddFvvUozj3FZVmdkTxRlrS5g==',
|
||||
'js/purify-3.2.4.js' => 'sha512-Mu9BqoHURMeycg6AgqTpokUv9guq88pajfaFqz53fx1OxohyROkydXPLEIbdKCQ7EdDs9hgcrYeZ9zTiPQQ4CA==',
|
||||
'js/privatebin.js' => 'sha512-zvJ6Feu2NvROB236BBxbP+8eYbUTJ5GCfhOJVL/RI6pJQpR3AS4ps0d1cVDqgUFW8wY0tiwE7JTE13gPWO3lHA==',
|
||||
'js/purify-3.2.6.js' => 'sha512-zqwL4OoBLFx89QPewkz4Lz5CSA2ktU+f31fuECkF0iK3Id5qd3Zpq5dMby8KwHjIEpsUgOqwF58cnmcaNem0EA==',
|
||||
'js/rawinflate-0.3.js' => 'sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==',
|
||||
'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',
|
||||
'js/zlib-1.3.1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==',
|
||||
'js/zlib-1.3.1-1.js' => 'sha512-5bU9IIP4PgBrOKLZvGWJD4kgfQrkTz8Z3Iqeu058mbQzW3mCumOU6M3UVbVZU9rrVoVwaW4cZK8U8h5xjF88eQ==',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class Controller
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const VERSION = '1.7.6';
|
||||
const VERSION = '1.7.7';
|
||||
|
||||
/**
|
||||
* minimal required PHP version
|
||||
@@ -112,10 +112,12 @@ class Controller
|
||||
*
|
||||
* initializes and runs PrivateBin
|
||||
*
|
||||
* @param ?Configuration $config
|
||||
*
|
||||
* @access public
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct(?Configuration $config = null)
|
||||
{
|
||||
if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) {
|
||||
error_log(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION));
|
||||
@@ -126,7 +128,8 @@ class Controller
|
||||
return;
|
||||
}
|
||||
|
||||
// load config from ini file, initialize required classes
|
||||
// load config (using ini file by default) & initialize required classes
|
||||
$this->_conf = $config ?? new Configuration();
|
||||
$this->_init();
|
||||
|
||||
switch ($this->_request->getOperation()) {
|
||||
@@ -174,12 +177,21 @@ class Controller
|
||||
*/
|
||||
private function _init()
|
||||
{
|
||||
$this->_conf = new Configuration;
|
||||
$this->_model = new Model($this->_conf);
|
||||
$this->_request = new Request;
|
||||
$this->_urlBase = $this->_request->getRequestUri();
|
||||
|
||||
// set default language
|
||||
$this->_setDefaultLanguage();
|
||||
$this->_setDefaultTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default language
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function _setDefaultLanguage()
|
||||
{
|
||||
$lang = $this->_conf->getKey('languagedefault');
|
||||
I18n::setLanguageFallback($lang);
|
||||
// force default language, if language selection is disabled and a default is set
|
||||
@@ -189,6 +201,25 @@ class Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default template
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function _setDefaultTemplate()
|
||||
{
|
||||
$templates = $this->_conf->getKey('availabletemplates');
|
||||
$template = $this->_conf->getKey('template');
|
||||
TemplateSwitcher::setAvailableTemplates($templates);
|
||||
TemplateSwitcher::setTemplateFallback($template);
|
||||
|
||||
// force default template, if template selection is disabled and a default is set
|
||||
if (!$this->_conf->getKey('templateselection') && !empty($template)) {
|
||||
$_COOKIE['template'] = $template;
|
||||
setcookie('template', $template, array('SameSite' => 'Lax', 'Secure' => true));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn off browser caching
|
||||
*
|
||||
@@ -400,6 +431,13 @@ class Controller
|
||||
setcookie('lang', $languageselection, array('SameSite' => 'Lax', 'Secure' => true));
|
||||
}
|
||||
|
||||
// set template cookie if that functionality was enabled
|
||||
$templateselection = '';
|
||||
if ($this->_conf->getKey('templateselection')) {
|
||||
$templateselection = TemplateSwitcher::getTemplate();
|
||||
setcookie('template', $templateselection, array('SameSite' => 'Lax', 'Secure' => true));
|
||||
}
|
||||
|
||||
// strip policies that are unsupported in meta tag
|
||||
$metacspheader = str_replace(
|
||||
array(
|
||||
@@ -438,6 +476,8 @@ class Controller
|
||||
$page->assign('ZEROBINCOMPATIBILITY', $this->_conf->getKey('zerobincompatibility'));
|
||||
$page->assign('LANGUAGESELECTION', $languageselection);
|
||||
$page->assign('LANGUAGES', I18n::getLanguageLabels(I18n::getAvailableLanguages()));
|
||||
$page->assign('TEMPLATESELECTION', $templateselection);
|
||||
$page->assign('TEMPLATES', TemplateSwitcher::getAvailableTemplates());
|
||||
$page->assign('EXPIRE', $expire);
|
||||
$page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
|
||||
$page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
|
||||
@@ -447,7 +487,7 @@ class Controller
|
||||
$page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri());
|
||||
$page->assign('COMPRESSION', $this->_conf->getKey('compression'));
|
||||
$page->assign('SRI', $this->_conf->getSection('sri'));
|
||||
$page->draw($this->_conf->getKey('template'));
|
||||
$page->draw(TemplateSwitcher::getTemplate());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,7 +34,7 @@ abstract class AbstractData
|
||||
* @param array $paste
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function create($pasteid, array $paste);
|
||||
abstract public function create($pasteid, array &$paste);
|
||||
|
||||
/**
|
||||
* Read a paste.
|
||||
@@ -72,7 +72,7 @@ abstract class AbstractData
|
||||
* @param array $comment
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function createComment($pasteid, $parentid, $commentid, array $comment);
|
||||
abstract public function createComment($pasteid, $parentid, $commentid, array &$comment);
|
||||
|
||||
/**
|
||||
* Read all comments of paste.
|
||||
@@ -199,7 +199,7 @@ abstract class AbstractData
|
||||
* @param array $paste
|
||||
* @return array
|
||||
*/
|
||||
protected static function upgradePreV1Format(array $paste)
|
||||
protected static function upgradePreV1Format(array &$paste)
|
||||
{
|
||||
if (array_key_exists('attachment', $paste['meta'])) {
|
||||
$paste['attachment'] = $paste['meta']['attachment'];
|
||||
|
||||
@@ -140,7 +140,7 @@ class Database extends AbstractData
|
||||
* @param array $paste
|
||||
* @return bool
|
||||
*/
|
||||
public function create($pasteid, array $paste)
|
||||
public function create($pasteid, array &$paste)
|
||||
{
|
||||
$expire_date = 0;
|
||||
$opendiscussion = $burnafterreading = false;
|
||||
@@ -297,14 +297,18 @@ class Database extends AbstractData
|
||||
* @param array $comment
|
||||
* @return bool
|
||||
*/
|
||||
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||
public function createComment($pasteid, $parentid, $commentid, array &$comment)
|
||||
{
|
||||
if (array_key_exists('data', $comment)) {
|
||||
$version = 1;
|
||||
$data = $comment['data'];
|
||||
} else {
|
||||
$version = 2;
|
||||
$data = Json::encode($comment);
|
||||
try {
|
||||
$version = 2;
|
||||
$data = Json::encode($comment);
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
list($createdKey, $iconKey) = $this->_getVersionedKeys($version);
|
||||
$meta = $comment['meta'];
|
||||
|
||||
@@ -85,7 +85,7 @@ class Filesystem extends AbstractData
|
||||
* @param array $paste
|
||||
* @return bool
|
||||
*/
|
||||
public function create($pasteid, array $paste)
|
||||
public function create($pasteid, array &$paste)
|
||||
{
|
||||
$storagedir = $this->_dataid2path($pasteid);
|
||||
$file = $storagedir . $pasteid . '.php';
|
||||
@@ -188,7 +188,7 @@ class Filesystem extends AbstractData
|
||||
* @param array $comment
|
||||
* @return bool
|
||||
*/
|
||||
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||
public function createComment($pasteid, $parentid, $commentid, array &$comment)
|
||||
{
|
||||
$storagedir = $this->_dataid2discussionpath($pasteid);
|
||||
$file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid . '.php';
|
||||
@@ -276,7 +276,7 @@ class Filesystem extends AbstractData
|
||||
case 'purge_limiter':
|
||||
return $this->_storeString(
|
||||
$this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
|
||||
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
|
||||
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . var_export($value, true) . ';'
|
||||
);
|
||||
case 'salt':
|
||||
return $this->_storeString(
|
||||
@@ -308,7 +308,9 @@ class Filesystem extends AbstractData
|
||||
$file = $this->_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
|
||||
if (is_readable($file)) {
|
||||
require $file;
|
||||
return $GLOBALS['purge_limiter'];
|
||||
if (array_key_exists('purge_limiter', $GLOBALS)) {
|
||||
return $GLOBALS['purge_limiter'];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'salt':
|
||||
@@ -324,9 +326,11 @@ class Filesystem extends AbstractData
|
||||
$file = $this->_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
|
||||
if (is_readable($file)) {
|
||||
require $file;
|
||||
$this->_last_cache = $GLOBALS['traffic_limiter'];
|
||||
if (array_key_exists($key, $this->_last_cache)) {
|
||||
return $this->_last_cache[$key];
|
||||
if (array_key_exists('traffic_limiter', $GLOBALS)) {
|
||||
$this->_last_cache = $GLOBALS['traffic_limiter'];
|
||||
if (array_key_exists($key, $this->_last_cache)) {
|
||||
return $this->_last_cache[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -343,12 +347,11 @@ class Filesystem extends AbstractData
|
||||
*/
|
||||
private function _get($filename)
|
||||
{
|
||||
return Json::decode(
|
||||
substr(
|
||||
file_get_contents($filename),
|
||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||
)
|
||||
$data = substr(
|
||||
file_get_contents($filename),
|
||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||
);
|
||||
return Json::decode($data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -105,7 +105,7 @@ class GoogleCloudStorage extends AbstractData
|
||||
* @param $payload array to store
|
||||
* @return bool true if successful, otherwise false.
|
||||
*/
|
||||
private function _upload($key, $payload)
|
||||
private function _upload($key, &$payload)
|
||||
{
|
||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
||||
@@ -136,7 +136,7 @@ class GoogleCloudStorage extends AbstractData
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function create($pasteid, array $paste)
|
||||
public function create($pasteid, array &$paste)
|
||||
{
|
||||
if ($this->exists($pasteid)) {
|
||||
return false;
|
||||
@@ -201,7 +201,7 @@ class GoogleCloudStorage extends AbstractData
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||
public function createComment($pasteid, $parentid, $commentid, array &$comment)
|
||||
{
|
||||
if ($this->existsComment($pasteid, $parentid, $commentid)) {
|
||||
return false;
|
||||
@@ -219,7 +219,8 @@ class GoogleCloudStorage extends AbstractData
|
||||
$prefix = $this->_getKey($pasteid) . '/discussion/';
|
||||
try {
|
||||
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
|
||||
$comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
|
||||
$data = $this->_bucket->object($key->name())->downloadAsString();
|
||||
$comment = Json::decode($data);
|
||||
$comment['id'] = basename($key->name());
|
||||
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||
$comments[$slot] = $comment;
|
||||
|
||||
@@ -165,7 +165,7 @@ class S3Storage extends AbstractData
|
||||
* @param $payload array to store
|
||||
* @return bool true if successful, otherwise false.
|
||||
*/
|
||||
private function _upload($key, $payload)
|
||||
private function _upload($key, &$payload)
|
||||
{
|
||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
||||
@@ -191,7 +191,7 @@ class S3Storage extends AbstractData
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function create($pasteid, array $paste)
|
||||
public function create($pasteid, array &$paste)
|
||||
{
|
||||
if ($this->exists($pasteid)) {
|
||||
return false;
|
||||
@@ -263,7 +263,7 @@ class S3Storage extends AbstractData
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||
public function createComment($pasteid, $parentid, $commentid, array &$comment)
|
||||
{
|
||||
if ($this->existsComment($pasteid, $parentid, $commentid)) {
|
||||
return false;
|
||||
|
||||
@@ -29,7 +29,7 @@ class FormatV2
|
||||
* @param bool $isComment
|
||||
* @return bool
|
||||
*/
|
||||
public static function isValid($message, $isComment = false)
|
||||
public static function isValid(&$message, $isComment = false)
|
||||
{
|
||||
$required_keys = array('adata', 'v', 'ct');
|
||||
if ($isComment) {
|
||||
|
||||
12
lib/I18n.php
@@ -183,9 +183,12 @@ class I18n
|
||||
|
||||
// load translations
|
||||
self::$_language = $match;
|
||||
self::$_translations = ($match == 'en') ? array() : Json::decode(
|
||||
file_get_contents(self::_getPath($match . '.json'))
|
||||
);
|
||||
if ($match == 'en') {
|
||||
self::$_translations = array();
|
||||
} else {
|
||||
$data = file_get_contents(self::_getPath($match . '.json'));
|
||||
self::$_translations = Json::decode($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +276,8 @@ class I18n
|
||||
{
|
||||
$file = self::_getPath('languages.json');
|
||||
if (count(self::$_languageLabels) == 0 && is_readable($file)) {
|
||||
self::$_languageLabels = Json::decode(file_get_contents($file));
|
||||
$data = file_get_contents($file);
|
||||
self::$_languageLabels = Json::decode($data);
|
||||
}
|
||||
if (count($languages) == 0) {
|
||||
return self::$_languageLabels;
|
||||
|
||||
@@ -29,7 +29,7 @@ class Json
|
||||
* @throws Exception
|
||||
* @return string
|
||||
*/
|
||||
public static function encode($input)
|
||||
public static function encode(&$input)
|
||||
{
|
||||
$jsonString = json_encode($input);
|
||||
self::_detectError();
|
||||
@@ -45,7 +45,7 @@ class Json
|
||||
* @throws Exception
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode($input)
|
||||
public static function decode(&$input)
|
||||
{
|
||||
$output = json_decode($input, true);
|
||||
self::_detectError();
|
||||
|
||||
@@ -100,9 +100,9 @@ abstract class AbstractModel
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setData(array $data)
|
||||
public function setData(array &$data)
|
||||
{
|
||||
$data = $this->_sanitize($data);
|
||||
$this->_sanitize($data);
|
||||
$this->_validate($data);
|
||||
$this->_data = $data;
|
||||
|
||||
@@ -155,7 +155,7 @@ abstract class AbstractModel
|
||||
*/
|
||||
public static function isValidId($id)
|
||||
{
|
||||
return (bool) preg_match('#\A[a-f\d]{16}\z#', (string) $id);
|
||||
return (bool) preg_match('#\A[a-f0-9]{16}\z#', (string) $id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,9 +163,8 @@ abstract class AbstractModel
|
||||
*
|
||||
* @access protected
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
abstract protected function _sanitize(array $data);
|
||||
abstract protected function _sanitize(array &$data);
|
||||
|
||||
/**
|
||||
* Validate data.
|
||||
@@ -174,7 +173,7 @@ abstract class AbstractModel
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _validate(array $data)
|
||||
protected function _validate(array &$data)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class Comment extends AbstractModel
|
||||
* @param Paste $paste
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setPaste(Paste $paste)
|
||||
public function setPaste(Paste &$paste)
|
||||
{
|
||||
$this->_paste = $paste;
|
||||
$this->_data['pasteid'] = $paste->getId();
|
||||
@@ -155,9 +155,8 @@ class Comment extends AbstractModel
|
||||
*
|
||||
* @access protected
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function _sanitize(array $data)
|
||||
protected function _sanitize(array &$data)
|
||||
{
|
||||
// we generate an icon based on a SHA512 HMAC of the users IP, if configured
|
||||
$icon = $this->_conf->getKey('icon');
|
||||
@@ -190,6 +189,5 @@ class Comment extends AbstractModel
|
||||
$data['meta']['icon'] = $pngdata;
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,11 +219,10 @@ class Paste extends AbstractModel
|
||||
*
|
||||
* @access protected
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
protected function _sanitize(array $data)
|
||||
protected function _sanitize(array &$data)
|
||||
{
|
||||
$expiration = $data['meta']['expire'];
|
||||
$expiration = $data['meta']['expire'] ?? 0;
|
||||
unset($data['meta']['expire']);
|
||||
$expire_options = $this->_conf->getSection('expire_options');
|
||||
if (array_key_exists($expiration, $expire_options)) {
|
||||
@@ -235,7 +234,6 @@ class Paste extends AbstractModel
|
||||
if ($expire > 0) {
|
||||
$data['meta']['expire_date'] = time() + $expire;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +243,7 @@ class Paste extends AbstractModel
|
||||
* @param array $data
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function _validate(array $data)
|
||||
protected function _validate(array &$data)
|
||||
{
|
||||
// reject invalid or disabled formatters
|
||||
if (!array_key_exists($data['adata'][1], $this->_conf->getSection('formatter_options'))) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace PrivateBin;
|
||||
|
||||
use Exception;
|
||||
use PrivateBin\Model\Paste;
|
||||
|
||||
/**
|
||||
* Request
|
||||
@@ -84,7 +85,7 @@ class Request
|
||||
foreach ($_GET as $key => $value) {
|
||||
// only return if value is empty and key is 16 hex chars
|
||||
$key = (string) $key;
|
||||
if (($value === '') && strlen($key) === 16 && ctype_xdigit($key)) {
|
||||
if (empty($value) && Paste::isValidId($key)) {
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
@@ -110,9 +111,8 @@ class Request
|
||||
// it might be a creation or a deletion, the latter is detected below
|
||||
$this->_operation = 'create';
|
||||
try {
|
||||
$this->_params = Json::decode(
|
||||
file_get_contents(self::$_inputStream)
|
||||
);
|
||||
$data = file_get_contents(self::$_inputStream);
|
||||
$this->_params = Json::decode($data);
|
||||
} catch (Exception $e) {
|
||||
// ignore error, $this->_params will remain empty
|
||||
}
|
||||
|
||||
132
lib/TemplateSwitcher.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php declare(strict_types=1);
|
||||
/**
|
||||
* PrivateBin
|
||||
*
|
||||
* a zero-knowledge paste bin
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
namespace PrivateBin;
|
||||
|
||||
/**
|
||||
* TemplateSwitcher
|
||||
*
|
||||
* Provides tool to change application template
|
||||
*/
|
||||
class TemplateSwitcher
|
||||
{
|
||||
/**
|
||||
* template fallback
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var string
|
||||
*/
|
||||
protected static $_templateFallback = 'bootstrap';
|
||||
|
||||
/**
|
||||
* available templates
|
||||
*
|
||||
* @access protected
|
||||
* @static
|
||||
* @var array
|
||||
*/
|
||||
protected static $_availableTemplates = array();
|
||||
|
||||
/**
|
||||
* set available templates
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param array $templates
|
||||
*/
|
||||
public static function setAvailableTemplates(array $templates)
|
||||
{
|
||||
self::$_availableTemplates = $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the default template
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @param string $template
|
||||
*/
|
||||
public static function setTemplateFallback(string $template)
|
||||
{
|
||||
if (self::isTemplateAvailable($template)) {
|
||||
self::$_templateFallback = $template;
|
||||
|
||||
if (!in_array($template, self::getAvailableTemplates())) {
|
||||
// Add custom template to the available templates list
|
||||
self::$_availableTemplates[] = $template;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get currently loaded template
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return string
|
||||
*/
|
||||
public static function getTemplate(): string
|
||||
{
|
||||
$selectedTemplate = self::getSelectedByUserTemplate();
|
||||
return $selectedTemplate ?? self::$_templateFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* get list of available templates
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return array
|
||||
*/
|
||||
public static function getAvailableTemplates(): array
|
||||
{
|
||||
return self::$_availableTemplates;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if the provided template is available
|
||||
*
|
||||
* @access public
|
||||
* @static
|
||||
* @return bool
|
||||
*/
|
||||
public static function isTemplateAvailable(string $template): bool
|
||||
{
|
||||
$available = in_array($template, self::getAvailableTemplates());
|
||||
|
||||
if (!$available && !View::isBootstrapTemplate($template)) {
|
||||
$path = View::getTemplateFilePath($template);
|
||||
$available = file_exists($path);
|
||||
}
|
||||
|
||||
return $available;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the template selected by user
|
||||
*
|
||||
* @access private
|
||||
* @static
|
||||
* @return string|null
|
||||
*/
|
||||
private static function getSelectedByUserTemplate(): ?string
|
||||
{
|
||||
$selectedTemplate = null;
|
||||
$templateCookieValue = $_COOKIE['template'] ?? '';
|
||||
|
||||
if (self::isTemplateAvailable($templateCookieValue)) {
|
||||
$selectedTemplate = $templateCookieValue;
|
||||
}
|
||||
|
||||
return $selectedTemplate;
|
||||
}
|
||||
}
|
||||
30
lib/View.php
@@ -49,8 +49,7 @@ class View
|
||||
*/
|
||||
public function draw($template)
|
||||
{
|
||||
$file = substr($template, 0, 10) === 'bootstrap-' ? 'bootstrap' : $template;
|
||||
$path = PATH . 'tpl' . DIRECTORY_SEPARATOR . $file . '.php';
|
||||
$path = self::getTemplateFilePath($template);
|
||||
if (!file_exists($path)) {
|
||||
throw new Exception('Template ' . $template . ' not found!', 80);
|
||||
}
|
||||
@@ -58,6 +57,31 @@ class View
|
||||
include $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template file path
|
||||
*
|
||||
* @access public
|
||||
* @param string $template
|
||||
* @return string
|
||||
*/
|
||||
public static function getTemplateFilePath(string $template): string
|
||||
{
|
||||
$file = self::isBootstrapTemplate($template) ? 'bootstrap' : $template;
|
||||
return PATH . 'tpl' . DIRECTORY_SEPARATOR . $file . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the template a variation of the bootstrap template
|
||||
*
|
||||
* @access public
|
||||
* @param string $template
|
||||
* @return bool
|
||||
*/
|
||||
public static function isBootstrapTemplate(string $template): bool
|
||||
{
|
||||
return substr($template, 0, 10) === 'bootstrap-';
|
||||
}
|
||||
|
||||
/**
|
||||
* echo script tag incl. SRI hash for given script file
|
||||
*
|
||||
@@ -70,7 +94,7 @@ class View
|
||||
$sri = array_key_exists($file, $this->_variables['SRI']) ?
|
||||
' integrity="' . $this->_variables['SRI'][$file] . '"' : '';
|
||||
// if the file isn't versioned (ends in a digit), add our own version
|
||||
$cacheBuster = ctype_digit(substr($file, -4, 1)) ?
|
||||
$cacheBuster = (bool) preg_match('#[0-9]\.js$#', (string) $file) ?
|
||||
'' : '?' . rawurlencode($this->_variables['VERSION']);
|
||||
echo '<script ', $attributes,
|
||||
' type="text/javascript" data-cfasync="false" src="', $file,
|
||||
|
||||