Merge branch 'master' into attachment-handling

# Conflicts:
#	js/privatebin.js
#	tpl/bootstrap.php
#	tpl/page.php
This commit is contained in:
thororm 2017-04-02 13:30:52 +02:00
commit 096f07f86e
60 changed files with 4651 additions and 2100 deletions

View File

@ -15,7 +15,9 @@ globals:
# http://eslint.org/docs/rules/ # http://eslint.org/docs/rules/
rules: rules:
# Possible Errors # Possible Errors
comma-dangle: [2, never] comma-dangle:
- error
- never
no-cond-assign: 2 no-cond-assign: 2
no-console: 0 no-console: 0
no-constant-condition: 2 no-constant-condition: 2
@ -31,7 +33,9 @@ rules:
no-extra-parens: 0 no-extra-parens: 0
no-extra-semi: 2 no-extra-semi: 2
no-func-assign: 2 no-func-assign: 2
no-inner-declarations: [2, functions] no-inner-declarations:
- error
- functions
no-invalid-regexp: 2 no-invalid-regexp: 2
no-irregular-whitespace: 2 no-irregular-whitespace: 2
no-negated-in-lhs: 2 no-negated-in-lhs: 2
@ -47,7 +51,9 @@ rules:
# Best Practices # Best Practices
accessor-pairs: 2 accessor-pairs: 2
block-scoped-var: 0 block-scoped-var: 0
complexity: [2, 6] complexity:
- error
- 20
consistent-return: 0 consistent-return: 0
curly: 0 curly: 0
default-case: 0 default-case: 0
@ -99,7 +105,7 @@ rules:
no-with: 2 no-with: 2
radix: 2 radix: 2
vars-on-top: 0 vars-on-top: 0
wrap-iife: 2 wrap-iife: 0
yoda: 0 yoda: 0
# Strict # Strict
@ -152,7 +158,9 @@ rules:
max-len: 0 max-len: 0
max-nested-callbacks: 0 max-nested-callbacks: 0
max-params: 0 max-params: 0
max-statements: [2, 30] max-statements:
- error
- 60
new-cap: 0 new-cap: 0
new-parens: 0 new-parens: 0
newline-after-var: 0 newline-after-var: 0

2
.gitignore vendored
View File

@ -1,6 +1,7 @@
# Ignore server files for safety # Ignore server files for safety
.htaccess .htaccess
.htpasswd .htpasswd
cfg/conf.ini
# Ignore data/ # Ignore data/
data/ data/
@ -30,6 +31,7 @@ vendor/**/build_phar.php
# Ignore local node modules, unit testing logs, api docs and eclipse project files # Ignore local node modules, unit testing logs, api docs and eclipse project files
js/node_modules/ js/node_modules/
tst/log/ tst/log/
tst/ConfigurationCombinationsTest.php
.settings .settings
.buildpath .buildpath
.project .project

View File

@ -1,15 +1,26 @@
language: php language: php
sudo: false sudo: false
php: php:
- 5.5 - '5.4'
- 5.6 - '5.5'
- 7.0 - '5.6'
- '7.0'
- '7.1'
# as this is a php project, node.js v4 (for JS unit testing) isn't installed
install:
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install 4
before_script: before_script:
- composer install -n - composer install -n
- npm install -g mocha
- cd js
- npm install jsverify jsdom jsdom-global
- cd ..
script: script:
- cd tst && phpunit - cd tst && ../vendor/bin/phpunit
- cd ../js && mocha
after_script: after_script:
- cd .. - cd ..

View File

@ -1,8 +1,10 @@
# PrivateBin version history # PrivateBin version history
* **next (not yet released)** * **next (not yet released)**
* ADDED: Translations for Spanish, Occitan and Norwegian * ADDED: Translations for Spanish, Occitan, Norwegian and Portuguese
* ADDED: Option in configuration to change the default "PrivateBin" title of the site * ADDED: Option in configuration to change the default "PrivateBin" title of the site
* CHANGED: Minimum required PHP version is 5.4 (#186)
* CHANGED: Shipped .htaccess files were updated for Apache 2.4 (#192)
* CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory * CHANGED: Cleanup of bootstrap template variants and moved icons to `img` directory
* **1.1 (2016-12-26)** * **1.1 (2016-12-26)**
* ADDED: Translations for Italian and Russian * ADDED: Translations for Italian and Russian

View File

@ -35,3 +35,4 @@ Sébastien Sauvage - original idea and main developer
* Alfredo Fabián Altamirano Tena - Spanish * Alfredo Fabián Altamirano Tena - Spanish
* Quent-in - Occitan * Quent-in - Occitan
* idarlund - Norwegian * idarlund - Norwegian
* Tulio Leao - Portuguese

View File

@ -10,7 +10,7 @@ check the options and adjust them as you see fit.
### Requirements ### Requirements
- PHP version 5.3 or above - PHP version 5.4 or above
- _one_ of the following sources of cryptographically safe randomness is required: - _one_ of the following sources of cryptographically safe randomness is required:
- PHP 7 or higher - PHP 7 or higher
- [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium) - [Libsodium](https://download.libsodium.org/libsodium/content/installation/) and it's [PHP extension](https://paragonie.com/book/pecl-libsodium/read/00-intro.md#installing-libsodium)

View File

@ -16,10 +16,10 @@ Data is encrypted/decrypted in the browser using 256bit AES in [Galois Counter m
This is a fork of ZeroBin, originally developed by This is a fork of ZeroBin, originally developed by
[Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). It was refactored [Sébastien Sauvage](https://github.com/sebsauvage/ZeroBin). It was refactored
to allow easier and cleaner extensions and has now much more features than the to allow easier and cleaner extensions and has now many more features than the
original. It is however still fully compatible to the original ZeroBin 0.19 original. It is however still fully compatible to the original ZeroBin 0.19
data storage scheme. Therefore such installations can be upgraded to this fork data storage scheme. Therefore such installations can be upgraded to this fork
without loosing any data. without losing any data.
## What PrivateBin provides ## What PrivateBin provides

1
cfg/.gitignore vendored
View File

@ -1 +0,0 @@
/conf.ini

View File

@ -1,2 +1 @@
Allow from none Require all denied
Deny from all

View File

@ -18,13 +18,14 @@
} }
], ],
"require": { "require": {
"php": "^5.3.0 || ^7.0", "php": "^5.4.0 || ^7.0",
"paragonie/random_compat": "2.0.4", "paragonie/random_compat": "2.0.4",
"yzalis/identicon": "1.1.0" "yzalis/identicon": "1.1.0"
}, },
"require-dev": { "require-dev": {
"codacy/coverage": "dev-master", "codacy/coverage": "dev-master",
"codeclimate/php-test-reporter": "dev-master" "codeclimate/php-test-reporter": "dev-master",
"phpunit/phpunit": "^4.6 || ^5.0"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

View File

@ -17,6 +17,10 @@ body.navbar-spacing {
padding-top: 70px; padding-top: 70px;
} }
body.loading {
cursor: wait;
}
.buttondisabled { .buttondisabled {
opacity: 0.3; opacity: 0.3;
} }
@ -115,6 +119,12 @@ body.navbar-spacing {
border-left: 1px solid #ccc; border-left: 1px solid #ccc;
padding: 5px 0 5px 10px; padding: 5px 0 5px 10px;
white-space: pre-wrap; white-space: pre-wrap;
transition: background-color 0.75s ease-out;
}
.comment.highlight {
background-color: #ffdd86;
transition: background-color 0.2s ease-in;
} }
footer h4 { footer h4 {

View File

@ -7,8 +7,8 @@
"en": "de", "en": "de",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.", "Diesen Text gibt es nicht, er ist abgelaufen oder wurde gelöscht.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"%s benötigt PHP 5.3.0 oder höher, um zu funktionieren. Sorry.", "%s benötigt PHP %s oder höher, um zu funktionieren. Sorry.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.", "%s benötigt den Konfigurationsabschnitt [%s] in der Konfigurationsdatei um zu funktionieren.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -83,25 +83,25 @@
"Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)":
"Konnte Daten nicht entschlüsseln (Falscher Schlüssel?)", "Konnte Daten nicht entschlüsseln (Falscher Schlüssel?)",
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.":
"Konnte den Text nicht löschen, er wurde nicht im Einmal-Modus gespeichert.", "Konnte das Paste nicht löschen, es wurde nicht im Einmal-Modus gespeichert.",
"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.":
"DIESER TEXT IST NUR FÜR DICH GEDACHT. Schliesse das Fenster nicht, diese Nachricht kann nur einmal geöffnet werden.", "DIESER TEXT IST NUR FÜR DICH GEDACHT. Schließe das Fenster nicht, diese Nachricht kann nur einmal geöffnet werden.",
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?":
"Konnte Kommentar nicht entschlüsseln; Falscher Schlüssel?", "Konnte Kommentar nicht entschlüsseln; Falscher Schlüssel?",
"Reply": "Reply":
"Antworten", "Antworten",
"Anonymous": "Anonymous":
"Anonym", "Anonym",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Anonymer Avatar (Vizhash der IP-Addresse)", "Avatar (generiert aus der IP-Adresse)",
"Add comment": "Add comment":
"Kommentar hinzufügen", "Kommentar hinzufügen",
"Optional nickname...": "Optional nickname":
"Optionales Pseudonym...", "Optionales Pseudonym",
"Post comment": "Post comment":
"Kommentar absenden", "Kommentar absenden",
"Sending comment...": "Sending comment":
"Sende Kommentar...", "Sende Kommentar",
"Comment posted.": "Comment posted.":
"Kommentar gesendet.", "Kommentar gesendet.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,24 +112,25 @@
"Fehler auf dem Server oder keine Antwort vom Server", "Fehler auf dem Server oder keine Antwort vom Server",
"Could not post comment: %s": "Could not post comment: %s":
"Konnte Kommentar nicht senden: %s", "Konnte Kommentar nicht senden: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Sende Text (Bitte bewege Deine Maus um die Entropie zu erhöhen)...", "Bitte bewege Deine Maus um die Entropie zu erhöhen",
"Sending paste...": "Sending paste":
"Sende Text...", "Sende Paste…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Dein Text ist unter <a id=\"pasteurl\" href=\"%s\">%s</a> zu finden <span id=\"copyhint\">(Drücke [Strg]+[c] um den Link zu kopieren)</span>", "Dein Paste ist unter <a id=\"pasteurl\" href=\"%s\">%s</a> zu finden <span id=\"copyhint\">(Drücke [Strg]+[c] um den Link zu kopieren)</span>",
"Delete data": "Delete data":
"Lösche Daten", "Lösche Daten",
"Could not create paste: %s": "Could not create paste: %s":
"Konnte Text nicht erstellen: %s", "Konnte Paste nicht erstellen: %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
"Konnte Text nicht entschlüsseln: Der Schlüssel fehlt in der Adresse (Hast du eine Umleitung oder einen URL-Verkürzer benutzt, der Teile der Adresse entfernt?)", "Konnte Paste nicht entschlüsseln: Der Schlüssel fehlt in der Adresse (Hast du eine Umleitung oder einen URL-Verkürzer benutzt, der Teile der Adresse entfernt?)",
"Format": "Format", "Format": "Format",
"Plain Text": "Nur Text", "Plain Text": "Nur Text",
"Source Code": "Quellcode", "Source Code": "Quellcode",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Anhang herunterladen", "Download attachment": "Anhang herunterladen",
"Cloned file attached.": "Kopierte Datei angehängt.", "Cloned: '%s'": "Geklont: '%s'",
"The cloned file '%s' was attached to this paste.": "Die geklonte Datei '%s' wurde angehängt.",
"Attach a file": "Datei anhängen", "Attach a file": "Datei anhängen",
"or drag & drop file": "oder per Drag & Drop einfügen", "or drag & drop file": "oder per Drag & Drop einfügen",
"File too large, to display a preview. Please download the attachment.": "Datei zu groß, um als Vorschau angezeigt zu werden. Bitte Anhang herunterladen.", "File too large, to display a preview. Please download the attachment.": "Datei zu groß, um als Vorschau angezeigt zu werden. Bitte Anhang herunterladen.",
@ -148,6 +149,9 @@
"Enter password": "Enter password":
"Passwort eingeben", "Passwort eingeben",
"Loading…": "Lädt…", "Loading…": "Lädt…",
"Decrypting paste…": "Entschlüssle Paste…",
"Preparing new paste…": "Bereite neues Paste vor…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Wenn diese Nachricht nicht mehr verschwindet, schau bitte in <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">die FAQ</a> (englisch), um zu sehen, wie der Fehler behoben werden kann." "Wenn diese Nachricht nicht mehr verschwindet, schau bitte in <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">die FAQ</a> (englisch), um zu sehen, wie der Fehler behoben werden kann.",
"+++ no paste text +++": "+++ kein Paste-Text +++"
} }

View File

@ -7,10 +7,10 @@
"en": "es", "en": "es",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"El texto no existe, ha caducado o ha sido eliminado.", "El texto no existe, ha caducado o ha sido eliminado.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"%s requiere php 5.3.0 o superior para funcionar. Lo siento.", "%s requiere php %s o superior para funcionar. Lo siento.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s requiere que la sección de configuración [% s] esté presente en el archivo de configuración.", "%s requiere que la sección de configuración [%s] esté presente en el archivo de configuración.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
"Por favor espere %d segundos entre cada publicación.", "Por favor espere %d segundos entre cada publicación.",
"Paste is limited to %s of encrypted data.": "Paste is limited to %s of encrypted data.":
@ -92,16 +92,16 @@
"Responder", "Responder",
"Anonymous": "Anonymous":
"Anónimo", "Anónimo",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Avatar anónimo (Vizhash de la dirección IP)", "Avatar anónimo (Vizhash de la dirección IP)",
"Add comment": "Add comment":
"Añadir comentario", "Añadir comentario",
"Optional nickname...": "Optional nickname":
"Seudónimo opcional...", "Seudónimo opcional",
"Post comment": "Post comment":
"Publicar comentario", "Publicar comentario",
"Sending comment...": "Sending comment":
"Enviando comentario...", "Enviando comentario",
"Comment posted.": "Comment posted.":
"Comentario publicado.", "Comentario publicado.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"Error del servidor o el servidor no responde", "Error del servidor o el servidor no responde",
"Could not post comment: %s": "Could not post comment: %s":
"No fue posible publicar comentario: %s", "No fue posible publicar comentario: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Enviando texto (Por favor, mueva el ratón para mayor entropía)...", "Por favor, mueva el ratón para mayor entropía…",
"Sending paste...": "Sending paste":
"Enviando texto...", "Enviando texto",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Su texto está en <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Presione [Ctrl]+[c] para copiar)</span>", "Su texto está en <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Presione [Ctrl]+[c] para copiar)</span>",
"Delete data": "Delete data":
@ -129,7 +129,8 @@
"Source Code": "Código fuente", "Source Code": "Código fuente",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Descargar adjunto", "Download attachment": "Descargar adjunto",
"Cloned file attached.": "Archivo clonado adjunto.", "Cloned: '%s'": "Clonado: '%s'.",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Adjuntar archivo", "Attach a file": "Adjuntar archivo",
"Remove attachment": "Remover adjunto", "Remove attachment": "Remover adjunto",
"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.":
@ -146,6 +147,9 @@
"Enter password": "Enter password":
"Ingrese contraseña", "Ingrese contraseña",
"Loading…": "Cargando…", "Loading…": "Cargando…",
"Decrypting paste…": "Decrypting paste…",
"Preparing new paste…": "Preparing new paste…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"En caso de que este mensaje nunca desaparezca por favor revise <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">este FAQ para obtener información para solucionar problemas</a>." "En caso de que este mensaje nunca desaparezca por favor revise <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">este FAQ para obtener información para solucionar problemas</a>.",
"+++ no paste text +++": "+++ no paste text +++"
} }

View File

@ -7,8 +7,8 @@
"en": "fr", "en": "fr",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Le paste n'existe pas, a expiré, ou a été supprimé.", "Le paste n'existe pas, a expiré, ou a été supprimé.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"Désolé, %s nécessite php 5.3.0 ou supérieur pour fonctionner.", "Désolé, %s nécessite php %s ou supérieur pour fonctionner.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.", "%s a besoin de la section de configuration [%s] dans le fichier de configuration pour fonctionner.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"Répondre", "Répondre",
"Anonymous": "Anonymous":
"Anonyme", "Anonyme",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Avatar anonyme (Vizhash de l'adresse IP)", "Avatar anonyme (Vizhash de l'adresse IP)",
"Add comment": "Add comment":
"Ajouter un commentaire", "Ajouter un commentaire",
"Optional nickname...": "Optional nickname":
"Pseudonyme optionnel...", "Pseudonyme optionnel",
"Post comment": "Post comment":
"Poster le commentaire", "Poster le commentaire",
"Sending comment...": "Sending comment":
"Envoi du commentaire...", "Envoi du commentaire",
"Comment posted.": "Comment posted.":
"Commentaire posté.", "Commentaire posté.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"Le serveur ne répond pas ou a rencontré une erreur", "Le serveur ne répond pas ou a rencontré une erreur",
"Could not post comment: %s": "Could not post comment: %s":
"Impossible de poster le commentaire : %s", "Impossible de poster le commentaire : %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Envoi du paste (Merci de bouger votre souris pour plus d'entropie)...", "Merci de bouger votre souris pour plus d'entropie",
"Sending paste...": "Sending paste":
"Envoi du paste...", "Envoi du paste",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur [Ctrl]+[c] pour copier)</span>", "Votre paste est disponible à l'adresse <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Appuyez sur [Ctrl]+[c] pour copier)</span>",
"Delete data": "Delete data":
@ -138,7 +138,8 @@
"Source Code": "Code source", "Source Code": "Code source",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Télécharger la pièce jointe", "Download attachment": "Télécharger la pièce jointe",
"Cloned file attached.": "Cloner le fichier attaché.", "Cloned: '%s'": "Cloner '%s'",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Attacher un fichier ", "Attach a file": "Attacher un fichier ",
"Remove attachment": "Enlever l'attachement", "Remove attachment": "Enlever l'attachement",
"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.":
@ -149,12 +150,15 @@
"Editor": "Éditer", "Editor": "Éditer",
"Preview": "Prévisualiser", "Preview": "Prévisualiser",
"%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 requiert que le PATH se termine dans un \"%s\". Veuillez mettre à jour le PATH dans votre index.php.",
"Decrypt": "Decrypt":
"Decrypt", "Déchiffrer",
"Enter password": "Enter password":
"Entrez le mot de passe", "Entrez le mot de passe",
"Loading…": "Loading…", "Loading…": "Chargement…",
"Decrypting paste…": "Decrypting paste…",
"Preparing new paste…": "Preparing new paste…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English)." "Si ce message ne disparaîssait pas, jetez un oeil à <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">cette FAQ pour des idées de résolution</a> (en Anglais).",
"+++ no paste text +++": "+++ no paste text +++"
} }

View File

@ -7,8 +7,8 @@
"en": "it", "en": "it",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Questo messaggio non esiste, è scaduto o è stato cancellato.", "Questo messaggio non esiste, è scaduto o è stato cancellato.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"%s richiede PHP 5.3.0 o superiore.", "%s richiede php %s o superiore per funzionare. Ci spiace.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s richiede la presenza della sezione [%s] nei file di configurazione.", "%s richiede la presenza della sezione [%s] nei file di configurazione.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -18,7 +18,7 @@
"Invalid data.": "Invalid data.":
"Dati non validi.", "Dati non validi.",
"You are unlucky. Try again.": "You are unlucky. Try again.":
"Riprova, sarai più fortunato.", "Ritenta, sarai più fortunato.",
"Error saving comment. Sorry.": "Error saving comment. Sorry.":
"Errore durante il salvataggio del commento.", "Errore durante il salvataggio del commento.",
"Error saving paste. Sorry.": "Error saving paste. Sorry.":
@ -67,7 +67,7 @@
"Never": "Never":
"Mai", "Mai",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
"Nota: questo è un servizio di prova, i dati possono essere cancellati in qualsiasi momento. Ti preghiamo di non abusare di questo servizio, grazie.", "Nota: questo è un servizio di prova, i messaggi salvati possono essere cancellati in qualsiasi momento. Moriranno dei gattini se abuserai di questo servizio.",
"This document will expire in %d seconds.": "This document will expire in %d seconds.":
["Questo documento scadrà tra un secondo.", "Questo documento scadrà in %d secondi."], ["Questo documento scadrà tra un secondo.", "Questo documento scadrà in %d secondi."],
"This document will expire in %d minutes.": "This document will expire in %d minutes.":
@ -87,41 +87,41 @@
"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.":
"FOR YOUR EYES ONLY. Non chiudere questa finestra, il messaggio non può essere visualizzato una seconda volta.", "FOR YOUR EYES ONLY. Non chiudere questa finestra, il messaggio non può essere visualizzato una seconda volta.",
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?":
"Non riesco a decifrari il commento (Chiave errata?)", "Non riesco a decifrare il commento (Chiave errata?)",
"Reply": "Reply":
"Rispondi", "Rispondi",
"Anonymous": "Anonymous":
"Anonimo", "Anonimo",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Avatar Anonino (Vizhash dell'indirizzo IP)", "Avatar generato dall'indirizzo IP)",
"Add comment": "Add comment":
"Aggiungi un commento", "Aggiungi un commento",
"Optional nickname...": "Optional nickname":
"Nickname opzionale...", "Nickname opzionale",
"Post comment": "Post comment":
"Invia commento", "Invia commento",
"Sending comment...": "Sending comment":
"Commento in fase di invio...", "Commento in fase di invio",
"Comment posted.": "Comment posted.":
"Commento inviato.", "Commento inviato.",
"Could not refresh display: %s": "Could not refresh display: %s":
"Non riesco ad aggiornare il display: %s", "Non riesco ad aggiornare il display: %s",
"unknown status": "unknown status":
"errore sconosciuto", "stato sconosciuto",
"server error or not responding": "server error or not responding":
"errore o mancata risposta dal server", "errore o mancata risposta dal server",
"Could not post comment: %s": "Could not post comment: %s":
"Impossibile inviare il commento: %s", "Impossibile inviare il commento: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)...", "Muovi il mouse in modo casuale, per generare maggior entropia",
"Sending paste...": "Sending paste":
"Messaggio in fase di invio...", "Messaggio in fase di invio",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Il tuo messaggio è qui: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">([CTRL | CMD]+[C] per copiare il link)</span>", "Il tuo messaggio è qui: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Premi [Ctrl]+[c] (Windows) o [Cmd]+[c] (Mac) per copiare il link)</span>",
"Delete data": "Delete data":
"Cancella i dati", "Cancella i dati",
"Could not create paste: %s": "Could not create paste: %s":
"Non rieco a creare il messaggio: %s", "Non riesco a creare il messaggio: %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
"Non riesco a decifrare il messaggio: manca la chiave di decifrazione nell'URL (La chiave è parte integrante dell'URL. Per caso hai usato un Redirector o un altro servizio che ha rimosso una parte dell'URL?)", "Non riesco a decifrare il messaggio: manca la chiave di decifrazione nell'URL (La chiave è parte integrante dell'URL. Per caso hai usato un Redirector o un altro servizio che ha rimosso una parte dell'URL?)",
"Format": "Formato", "Format": "Formato",
@ -129,7 +129,8 @@
"Source Code": "Codice Sorgente", "Source Code": "Codice Sorgente",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Scarica Allegato", "Download attachment": "Scarica Allegato",
"Cloned file attached.": "Copia del file allegata.", "Cloned: '%s'": "Clonato: '%s'",
"The cloned file '%s' was attached to this paste.": "Il file clonato '%s' era allegato a questo messaggio.",
"Attach a file": "Allega un file", "Attach a file": "Allega un file",
"Remove attachment": "Rimuovi allegato", "Remove attachment": "Rimuovi allegato",
"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.":
@ -142,10 +143,13 @@
"%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 necessita che PATH termini con \"%s\". Aggiorna la variabile PATH nel tuo index.php.", "%s necessita che PATH termini con \"%s\". Aggiorna la variabile PATH nel tuo index.php.",
"Decrypt": "Decrypt":
"Decrypt", "Decifra",
"Enter password": "Enter password":
"Inserisci la password", "Inserisci la password",
"Loading…": "Loading…", "Loading…": "Carico…",
"Decrypting paste…": "Decifro il messaggio…",
"Preparing new paste…": "Preparo il nuovo messaggio…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English)." "Nel caso questo messaggio non scompaia, controlla questa <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">FAQ</a> per trovare informazioni su come risolvere il problema (in Inglese).",
"+++ no paste text +++": "+++ nessun testo nel messaggio +++"
} }

View File

@ -7,8 +7,8 @@
"en": "no", "en": "no",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Innlegget eksisterer ikke, er utløpt eller har blitt slettet.", "Innlegget eksisterer ikke, er utløpt eller har blitt slettet.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"Beklager, %s krever php 5.3.0 eller nyere for å kjøre.", "Beklager, %s krever php %s eller nyere for å kjøre.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .", "%s krever konfigurasjonsdel [%s] å være til stede i konfigurasjonsfilen .",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -26,13 +26,13 @@
"Invalid paste ID.": "Invalid paste ID.":
"Feil innlegg ID.", "Feil innlegg ID.",
"Paste is not of burn-after-reading type.": "Paste is not of burn-after-reading type.":
"Innlegg er ikke av type slett-etter-lesing.", "Innlegg er ikke av typen slett etter lesing.",
"Wrong deletion token. Paste was not deleted.": "Wrong deletion token. Paste was not deleted.":
"Feil slettingsnøkkel. Innlegg ble ikke fjernet.", "Feil slettingsnøkkel. Innlegg ble ikke fjernet.",
"Paste was properly deleted.": "Paste was properly deleted.":
"Innlegget er slettet.", "Innlegget er slettet.",
"JavaScript is required for %s to work.<br />Sorry for the inconvenience.": "JavaScript is required for %s to work.<br />Sorry for the inconvenience.":
"Javascript kreves for at %s skal fungere<br />Beklager ulempene.", "Javascript kreves for at %s skal fungere<br />Beklager.",
"%s requires a modern browser to work.": "%s requires a modern browser to work.":
"%s krever en moderne nettleser for å fungere.", "%s krever en moderne nettleser for å fungere.",
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:": "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
@ -83,7 +83,7 @@
"Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)":
"Kunne ikke dekryptere data (Feil nøkkel?)", "Kunne ikke dekryptere data (Feil nøkkel?)",
"Could not delete the paste, it was not stored in burn after reading mode.": "Could not delete the paste, it was not stored in burn after reading mode.":
"Kan ikke slette innlegget, det ble ikke lagret i slett-etter-les modus.", "Kan ikke slette innlegget, det ble ikke lagret som 'slett etter les' type.",
"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.":
"KUN FOR DINE ØYNE. Ikke lukk dette vinduet, denne meldingen kan ikke bli vist igjen.", "KUN FOR DINE ØYNE. Ikke lukk dette vinduet, denne meldingen kan ikke bli vist igjen.",
"Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?":
@ -92,30 +92,30 @@
"Svar", "Svar",
"Anonymous": "Anonymous":
"Anonym", "Anonym",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Anonym avatar (Vizhash av IP adressen)", "Anonym avatar generert med data fra IP adressen)",
"Add comment": "Add comment":
"Legg til kommentar", "Legg til kommentar",
"Optional nickname...": "Optional nickname":
"Valgfritt kallenavn...", "Valgfritt kallenavn",
"Post comment": "Post comment":
"Send kommentar", "Send kommentar",
"Sending comment...": "Sending comment":
"Sender Kommentar...", "Sender Kommentar",
"Comment posted.": "Comment posted.":
"Kommentar sendt.", "Kommentar sendt.",
"Could not refresh display: %s": "Could not refresh display: %s":
"Kunne ikke oppdatere skjermen: %s", "Kunne ikke oppdatere bildet: %s",
"unknown status": "unknown status":
"ukjent status", "ukjent status",
"server error or not responding": "server error or not responding":
"server feilet eller svarer ikke", "tjener feilet eller svarer ikke",
"Could not post comment: %s": "Could not post comment: %s":
"Kunne ikke sende kommentar: %s", "Kunne ikke sende kommentar: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Sender innlegg (Flytt musen for mere entropi)...", "Flytt musen for mer entropi…",
"Sending paste...": "Sending paste":
"Sender innlegg...", "Sender innlegg",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Ditt innlegg er <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Trykk [Ctrl]+[c] for å kopiere)</span>", "Ditt innlegg er <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Trykk [Ctrl]+[c] for å kopiere)</span>",
"Delete data": "Delete data":
@ -129,14 +129,15 @@
"Source Code": "Kildekode", "Source Code": "Kildekode",
"Markdown": "Oppmerket", "Markdown": "Oppmerket",
"Download attachment": "Last ned vedlegg", "Download attachment": "Last ned vedlegg",
"Cloned file attached.": "Kopier vedlegg.", "Cloned: '%s'": "Kopiert: '%s'",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Legg til fil", "Attach a file": "Legg til fil",
"Remove attachment": "Slett vedlegg", "Remove attachment": "Slett vedlegg",
"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.":
"Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.", "Nettleseren din støtter ikke å laste opp krypterte filer. Vennligst bruk en nyere nettleser.",
"Invalid attachment.": "Ugyldig vedlegg.", "Invalid attachment.": "Ugyldig vedlegg.",
"Options": "Alternativer", "Options": "Alternativer",
"Shorten URL": "Adresse-forkorter", "Shorten URL": "Adresse forkorter",
"Editor": "Rediger", "Editor": "Rediger",
"Preview": "Forhåndsvis", "Preview": "Forhåndsvis",
"%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.":
@ -146,6 +147,9 @@
"Enter password": "Enter password":
"Skriv inn passord", "Skriv inn passord",
"Loading…": "Laster…", "Loading…": "Laster…",
"Decrypting paste…": "Dekrypterer innlegg…",
"Preparing new paste…": "Klargjør nytt innlegg…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">ofte stilte spørsmål</a> for informasjon om feilsøking." "Hvis denne meldingen ikke forsvinner kan du ta en titt på siden med <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">ofte stilte spørsmål</a> for informasjon om feilsøking.",
"+++ no paste text +++": "+++ ingen innleggstekst +++"
} }

View File

@ -7,8 +7,8 @@
"en": "oc", "en": "oc",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Lo tèxte existís pas, a expirat, o es estat suprimit.", "Lo tèxte existís pas, a expirat, o es estat suprimit.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"O planhèm, %s necessita php 5.3.0 o superior per foncionar.", "O planhèm, %s necessita php %s o superior per foncionar.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s fa besonh de la seccion de configuracion [%s] dins lo fichièr de configuracion per foncionar.", "%s fa besonh de la seccion de configuracion [%s] dins lo fichièr de configuracion per foncionar.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"Respondre", "Respondre",
"Anonymous": "Anonymous":
"Anonime", "Anonime",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Avatar anonime (Vizhash de l'adreça IP)", "Avatar anonime (Vizhash de l'adreça IP)",
"Add comment": "Add comment":
"Apondre un comentari", "Apondre un comentari",
"Optional nickname...": "Optional nickname":
"Escais opcional...", "Escais opcional",
"Post comment": "Post comment":
"Mandar lo comentari", "Mandar lo comentari",
"Sending comment...": "Sending comment":
"Mandadís del comentari...", "Mandadís del comentari",
"Comment posted.": "Comment posted.":
"Comentari mandat.", "Comentari mandat.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"Lo servidor respond pas o a rencontrat una error", "Lo servidor respond pas o a rencontrat una error",
"Could not post comment: %s": "Could not post comment: %s":
"Impossible de mandar lo comentari : %s", "Impossible de mandar lo comentari : %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)...", "Mercés de bolegar vòstra mirga per mai entropia…",
"Sending paste...": "Sending paste":
"Mandadís del tèxte...", "Mandadís del tèxte",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Vòstre tèxte es disponible a l'adreça <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Picatz sus [Ctrl]+[c] per copiar)</span>", "Vòstre tèxte es disponible a l'adreça <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Picatz sus [Ctrl]+[c] per copiar)</span>",
"Delete data": "Delete data":
@ -138,7 +138,8 @@
"Source Code": "Còdi font", "Source Code": "Còdi font",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Telecargar la pèça junta", "Download attachment": "Telecargar la pèça junta",
"Cloned file attached.": "Clonar lo fichièr junt.", "Cloned: '%s'": "Clonar: '%s'",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Juntar un fichièr ", "Attach a file": "Juntar un fichièr ",
"Remove attachment": "Levar la pèca junta", "Remove attachment": "Levar la pèca junta",
"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.":
@ -155,6 +156,9 @@
"Enter password": "Enter password":
"Picatz lo senhal", "Picatz lo senhal",
"Loading…": "Cargament…", "Loading…": "Cargament…",
"Decrypting paste…": "Decrypting paste…",
"Preparing new paste…": "Preparing new paste…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">aquesta FAQ per las solucions</a> (en Anglés)." "Se per cas aqueste messatge quita pas de s'afichar mercés de gaitar <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">aquesta FAQ per las solucions</a> (en Anglés).",
"+++ no paste text +++": "+++ no paste text +++"
} }

View File

@ -7,8 +7,8 @@
"en": "pl", "en": "pl",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Wklejka nie istnieje, wygasła albo została usunięta.", "Wklejka nie istnieje, wygasła albo została usunięta.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"%s wymaga PHP w wersji 5.3.0 lub nowszej, sorry.", "%s wymaga PHP w wersji %s lub nowszej, sorry.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s wymaga obecności sekcji [%s] w pliku konfiguracyjnym.", "%s wymaga obecności sekcji [%s] w pliku konfiguracyjnym.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"Odpowiedz", "Odpowiedz",
"Anonymous": "Anonymous":
"Anonim", "Anonim",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Anonimowy avatar (Vizhash z adresu IP)", "Anonimowy avatar (Vizhash z adresu IP)",
"Add comment": "Add comment":
"Dodaj komentarz", "Dodaj komentarz",
"Optional nickname...": "Optional nickname":
"Opcjonalny nick...", "Opcjonalny nick",
"Post comment": "Post comment":
"Wyślij komentarz", "Wyślij komentarz",
"Sending comment...": "Sending comment":
"Wysyłanie komentarza...", "Wysyłanie komentarza",
"Comment posted.": "Comment posted.":
"Wysłano komentarz.", "Wysłano komentarz.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"bląd serwera lub brak odpowiedzi", "bląd serwera lub brak odpowiedzi",
"Could not post comment: %s": "Could not post comment: %s":
"Nie udało się wysłać komentarza: %s", "Nie udało się wysłać komentarza: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)...", "Proszę poruszać myszą aby uzyskać większą entropię…",
"Sending paste...": "Sending paste":
"Wysyłanie wklejki...", "Wysyłanie wklejki",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Twoja wklejka to <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(wciśnij [Ctrl]+[c] aby skopiować)</span>", "Twoja wklejka to <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(wciśnij [Ctrl]+[c] aby skopiować)</span>",
"Delete data": "Delete data":
@ -129,7 +129,8 @@
"Source Code": "Kod źródłowy", "Source Code": "Kod źródłowy",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Pobierz załącznik", "Download attachment": "Pobierz załącznik",
"Cloned file attached.": "Sklonowano załączony plik.", "Cloned: '%s'": "Sklonowano: '%s'",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Załącz plik", "Attach a file": "Załącz plik",
"Remove attachment": "Usuń załącznik", "Remove attachment": "Usuń załącznik",
"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.":
@ -146,6 +147,9 @@
"Enter password": "Enter password":
"Wpisz hasło", "Wpisz hasło",
"Loading…": "Loading…", "Loading…": "Loading…",
"Decrypting paste…": "Decrypting paste…",
"Preparing new paste…": "Preparing new paste…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English)." "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English).",
"+++ no paste text +++": "+++ no paste text +++"
} }

155
i18n/pt.json Normal file
View File

@ -0,0 +1,155 @@
{
"PrivateBin": "PrivateBin",
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted <i>in the browser</i> using 256 bits AES. More information on the <a href=\"https://privatebin.info/\">project page</a>.":
"%s é um serviço minimalista e de código aberto do tipo \"pastebin\", em que o servidor tem zero conhecimento dos dados copiados. Os dados são cifrados e decifrados <i>no navegador</i> usando 256 bits AES. Mais informações na <a href=\"https://privatebin.info/\">página do projeto</a>.",
"Because ignorance is bliss":
"Porque a ignorância é uma benção",
"en": "pt",
"Paste does not exist, has expired or has been deleted.":
"A cópia não existe, expirou ou já foi excluída.",
"%s requires php %s or above to work. Sorry.":
"%s requer php %s ou superior para funcionar. Desculpa.",
"%s requires configuration section [%s] to be present in configuration file.":
"%s requer que a seção de configuração [% s] esteja no arquivo de configuração.",
"Please wait %d seconds between each post.":
"Por favor espere %d segundos entre cada publicação.",
"Paste is limited to %s of encrypted data.":
"A cópia está limitada a %s de dados cifrados.",
"Invalid data.":
"Dados inválidos.",
"You are unlucky. Try again.":
"Você é azarado. Tente novamente",
"Error saving comment. Sorry.":
"Erro ao salvar comentário. Desculpa.",
"Error saving paste. Sorry.":
"Erro ao salvar cópia. Desculpa.",
"Invalid paste ID.":
"ID de cópia inválido.",
"Paste is not of burn-after-reading type.":
"Cópia não é do tipo \"queime após ler\".",
"Wrong deletion token. Paste was not deleted.":
"Token de remoção inválido. A cópia não foi excluída.",
"Paste was properly deleted.":
"A cópia foi devidamente excluída.",
"JavaScript is required for %s to work.<br />Sorry for the inconvenience.":
"JavaScript é necessário para que %s funcione.<br />Pedimos desculpas pela inconveniência.",
"%s requires a modern browser to work.":
"%s requer um navegador moderno para funcionar.",
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
"Ainda usando Internet Explorer? Faça-se um favor, mude para um navegador moderno:",
"New":
"Novo",
"Send":
"Enviar",
"Clone":
"Clonar",
"Raw text":
"Texto sem formato",
"Expires":
"Expirar em",
"Burn after reading":
"Queime após ler",
"Open discussion":
"Discussão aberta",
"Password (recommended)":
"Senha (recomendada)",
"Discussion":
"Discussão",
"Toggle navigation":
"Mudar navegação",
"%d seconds": ["%d segundo", "%d segundos"],
"%d minutes": ["%d minuto", "%d minutos"],
"%d hours": ["%d hora", "%d horas"],
"%d days": ["%d dia", "%d dias"],
"%d weeks": ["%d semana", "%d semanas"],
"%d months": ["%d mês", "%d meses"],
"%d years": ["%d ano", "%d anos"],
"Never":
"Nunca",
"Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.":
"Nota: Este é um serviço de teste. Dados podem ser perdidos a qualquer momento. Gatinhos morrerão se você abusar desse serviço.",
"This document will expire in %d seconds.":
["Este documento irá expirar em um segundo.", "Este documento irá expirar em %d segundos."],
"This document will expire in %d minutes.":
["Este documento irá expirar em um minuto.", "Este documento irá expirar em %d minutos."],
"This document will expire in %d hours.":
["Este documento irá expirar em uma hora.", "Este documento irá expirar em %d horas."],
"This document will expire in %d days.":
["Este documento irá expirar em um dia.", "Este documento irá expirar em %d dias."],
"This document will expire in %d months.":
["Este documento irá expirar em um mês.", "Este documento irá expirar em %d meses."],
"Please enter the password for this paste:":
"Por favor, digite a senha para essa cópia:",
"Could not decrypt data (Wrong key?)":
"Não foi possível decifrar os dados (Chave errada?)",
"Could not delete the paste, it was not stored in burn after reading mode.":
"Não foi possível excluir a cópia, ela não foi salva no modo de \"queime após ler\".",
"FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.":
"APENAS PARA SEUS OLHOS. Não feche essa janela, essa mensagem não pode ser exibida novamente.",
"Could not decrypt comment; Wrong key?":
"Não foi possível decifrar o comentário; Chave errada?",
"Reply":
"Responder",
"Anonymous":
"Anônimo",
"Avatar generated from IP address":
"Avatar gerado à partir do endereço IP",
"Add comment":
"Adicionar comentário",
"Optional nickname…":
"Apelido opcional…",
"Post comment":
"Publicar comentário",
"Sending comment…":
"Enviando comentário…",
"Comment posted.":
"Comentário publicado.",
"Could not refresh display: %s":
"Não foi possível atualizar a tela: %s",
"unknown status":
"Estado desconhecido",
"server error or not responding":
"Servidor em erro ou não responsivo",
"Could not post comment: %s":
"Não foi possível publicar o comentário: %s",
"Please move your mouse for more entropy…":
"Por favor, mova o mouse para maior entropia…",
"Sending paste…":
"Enviando cópia…",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Sua cópia é <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pressione [Ctrl]+[c] para copiar)</span>",
"Delete data":
"Excluir dados",
"Could not create paste: %s":
"Não foi possível criar cópia: %s",
"Cannot decrypt paste: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)":
"Não foi possível decifrar a cópia: chave de decriptografia ausente na URL (Você utilizou um redirecionador ou encurtador de URL que removeu parte dela?)",
"Format": "Formato",
"Plain Text": "Texto sem formato",
"Source Code": "Código fonte",
"Markdown": "Markdown",
"Download attachment": "Baixar anexo",
"Cloned: '%s'": "Clonado: '%s'",
"The cloned file '%s' was attached to this paste.": "O arquivo clonado '%s' foi anexado a essa cópia.",
"Attach a file": "Anexar um arquivo",
"Remove attachment": "Remover anexo",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
"Seu navegador não permite subir arquivos cifrados. Por favor, utilize um navegador mais recente.",
"Invalid attachment.": "Anexo inválido.",
"Options": "Opções",
"Shorten URL": "Encurtar URL",
"Editor": "Editor",
"Preview": "Visualizar",
"%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.":
"%s requer que o PATH termine em \"%s\". Por favor, atualize o PATH em seu index.php.",
"Decrypt":
"Decifrar",
"Enter password":
"Digite a senha",
"Loading…": "Carregando…",
"Decrypting paste…": "Decifrando cópia…",
"Preparing new paste…": "Preparando nova cópia…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Caso essa mensagem nunca desapareça, por favor veja <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">este FAQ para saber como resolver os problemas</a>.",
"+++ no paste text +++": "+++ sem texto de cópia +++"
}

View File

@ -7,8 +7,8 @@
"en": "ru", "en": "ru",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Запись не существует, просрочена или была удалена.", "Запись не существует, просрочена или была удалена.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"Для работы %s требуется PHP 5.3.0 или выше. Извините.", "Для работы %s требуется php %s или выше. Извините.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s необходимо наличие секции [%s] в конфигурационном файле.", "%s необходимо наличие секции [%s] в конфигурационном файле.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -32,7 +32,7 @@
"Paste was properly deleted.": "Paste was properly deleted.":
"Запись была успешно удалена.", "Запись была успешно удалена.",
"JavaScript is required for %s to work.<br />Sorry for the inconvenience.": "JavaScript is required for %s to work.<br />Sorry for the inconvenience.":
"Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства..", "Для работы %s требуется включенный JavaScript.<br />Приносим извинения за неудобства.",
"%s requires a modern browser to work.": "%s requires a modern browser to work.":
"Для работы %s требуется более современный браузер.", "Для работы %s требуется более современный браузер.",
"Still using Internet Explorer? Do yourself a favor, switch to a modern browser:": "Still using Internet Explorer? Do yourself a favor, switch to a modern browser:":
@ -92,30 +92,30 @@
"Ответить", "Ответить",
"Anonymous": "Anonymous":
"Аноним", "Аноним",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Анонимный аватар (Vizhash IP адреса)", "Аватар, сгенерированный из IP-адреса",
"Add comment": "Add comment":
"Добавить комментарий", "Добавить комментарий",
"Optional nickname...": "Optional nickname":
"Опциональный никнейм...", "Опциональный никнейм",
"Post comment": "Post comment":
"Отправить комментарий", "Отправить комментарий",
"Sending comment...": "Sending comment":
"Отправка комментария...", "Отправка комментария",
"Comment posted.": "Comment posted.":
"Комментарий опубликован.", "Комментарий опубликован.",
"Could not refresh display: %s": "Could not refresh display: %s":
"Невозможно обновить данные: %s", "Не удалось обновить отображение: %s",
"unknown status": "unknown status":
"неизвестная причина", "неизвестная причина",
"server error or not responding": "server error or not responding":
"ошибка сервера или нет ответа", "ошибка сервера или нет ответа",
"Could not post comment: %s": "Could not post comment: %s":
"Не удалось опубликовать комментарий: %s", "Не удалось опубликовать комментарий: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Отправка записи (Пожалуйста двигайте мышкой для большей энтропии)...", "Пожалуйста двигайте мышкой для большей энтропии",
"Sending paste...": "Sending paste":
"Отправка записи...", "Отправка записи",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Ссылка на запись <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Нажмите [Ctrl]+[c] чтобы скопировать ссылку)</span>", "Ссылка на запись <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Нажмите [Ctrl]+[c] чтобы скопировать ссылку)</span>",
"Delete data": "Delete data":
@ -138,7 +138,9 @@
"Source Code": "Исходный код", "Source Code": "Исходный код",
"Markdown": "Язык разметки", "Markdown": "Язык разметки",
"Download attachment": "Скачать прикрепленный файл", "Download attachment": "Скачать прикрепленный файл",
"Cloned file attached.": "Дубль файла прикреплен.", "Cloned: '%s'": "Дублировано: '%s'",
"The cloned file '%s' was attached to this paste.":
"Дубликат файла '%s' был прикреплен к этой записи.",
"Attach a file": "Прикрепить файл", "Attach a file": "Прикрепить файл",
"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.":
@ -154,6 +156,10 @@
"Расшифровать", "Расшифровать",
"Enter password": "Enter password":
"Введите пароль", "Введите пароль",
"Uploading paste… Please wait.": "Loading…": "Загрузка…",
"Отправка записи... Пожалуйста подождите." "Decrypting paste…": "Расшифровка записи…",
"Preparing new paste…": "Подготовка новой записи…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"Если данное сообщение не исчезает длительное время, посмотрите <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">этот FAQ с информацией о возможном решении проблемы (на английском)</a>.",
"+++ no paste text +++": "+++ в записи нет текста +++"
} }

View File

@ -7,8 +7,8 @@
"en": "sl", "en": "sl",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.", "Prilepek ne obstaja, mu je potekla življenjska doba, ali pa je izbrisan.",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"Oprosti, %s za delovanje potrebuje vsaj php 5.3.0.", "Oprosti, %s za delovanje potrebuje vsaj php %s.",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s potrebuje sekcijo konfiguracij [%s] v konfiguracijski datoteki.", "%s potrebuje sekcijo konfiguracij [%s] v konfiguracijski datoteki.",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"Odgovori", "Odgovori",
"Anonymous": "Anonymous":
"Aninomno", "Aninomno",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"Anonimen avatar (Vizhash IP naslova)", "Anonimen avatar (Vizhash IP naslova)",
"Add comment": "Add comment":
"Dodaj komentar", "Dodaj komentar",
"Optional nickname...": "Optional nickname":
"Uporabniško ime (lahko izpustiš)", "Uporabniško ime (lahko izpustiš)",
"Post comment": "Post comment":
"Objavi komentar", "Objavi komentar",
"Sending comment...": "Sending comment":
"Pošiljam komentar ...", "Pošiljam komentar ",
"Comment posted.": "Comment posted.":
"Komentar poslan.", "Komentar poslan.",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"napaka na strežniku, ali pa se strežnik ne odziva", "napaka na strežniku, ali pa se strežnik ne odziva",
"Could not post comment: %s": "Could not post comment: %s":
"Komentarja ni bilo mogoče objaviti : %s", "Komentarja ni bilo mogoče objaviti : %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"Pošiljam prilepek (prosim premakni svojo miško za več entropije) ...", "Prosim premakni svojo miško za več entropije…",
"Sending paste...": "Sending paste":
"Pošiljam prilepek...", "Pošiljam prilepek",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"Tvoj prilepek je dostopen na naslovu: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pritisni [Ctrl]+[c] ali [Cmd] + [c] in skopiraj)</span>", "Tvoj prilepek je dostopen na naslovu: <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Pritisni [Ctrl]+[c] ali [Cmd] + [c] in skopiraj)</span>",
"Delete data": "Delete data":
@ -138,7 +138,8 @@
"Source Code": "Odprta koda", "Source Code": "Odprta koda",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "Pretoči priponko", "Download attachment": "Pretoči priponko",
"Cloned file attached.": "Pripeta datoteka klonirana", "Cloned: '%s'": "'%s' klonirana",
"The cloned file '%s' was attached to this paste.": "The cloned file '%s' was attached to this paste.",
"Attach a file": "Pripni datoteko", "Attach a file": "Pripni datoteko",
"Remove attachment": "Odstrani priponko", "Remove attachment": "Odstrani priponko",
"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.":
@ -155,6 +156,9 @@
"Enter password": "Enter password":
"Prosim vnesi geslo", "Prosim vnesi geslo",
"Loading…": "Loading…", "Loading…": "Loading…",
"Decrypting paste…": "Decrypting paste…",
"Preparing new paste…": "Preparing new paste…",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English)." "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English).",
"+++ no paste text +++": "+++ no paste text +++"
} }

View File

@ -7,8 +7,8 @@
"en": "zh", "en": "zh",
"Paste does not exist, has expired or has been deleted.": "Paste does not exist, has expired or has been deleted.":
"粘贴不存在,已过期或者已被删除。", "粘贴不存在,已过期或者已被删除。",
"%s requires php 5.3.0 or above to work. Sorry.": "%s requires php %s or above to work. Sorry.":
"%s需要工作于PHP 5.3.0及以上版本,抱歉。", "%s需要工作于PHP %s及以上版本,抱歉。",
"%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.":
"%s需要设置配置文件中 [%s] 的部分。", "%s需要设置配置文件中 [%s] 的部分。",
"Please wait %d seconds between each post.": "Please wait %d seconds between each post.":
@ -92,16 +92,16 @@
"回复", "回复",
"Anonymous": "Anonymous":
"匿名", "匿名",
"Anonymous avatar (Vizhash of the IP address)": "Avatar generated from IP address":
"匿名头像 (由IP地址生成Vizhash)", "由IP生成的头像",
"Add comment": "Add comment":
"添加评论", "添加评论",
"Optional nickname...": "Optional nickname":
"可选昵称...", "可选昵称",
"Post comment": "Post comment":
"评论", "评论",
"Sending comment...": "Sending comment":
"评论发送中...", "评论发送中",
"Comment posted.": "Comment posted.":
"评论已发送。", "评论已发送。",
"Could not refresh display: %s": "Could not refresh display: %s":
@ -112,10 +112,10 @@
"服务器错误或无回应", "服务器错误或无回应",
"Could not post comment: %s": "Could not post comment: %s":
"无法发送评论: %s", "无法发送评论: %s",
"Sending paste (Please move your mouse for more entropy)...": "Please move your mouse for more entropy…":
"粘贴提交中 (请移动鼠标以产生更多熵)...", "请移动鼠标增加随机性…",
"Sending paste...": "Sending paste":
"粘贴提交中...", "粘贴提交中",
"Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>": "Your paste is <a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(Hit [Ctrl]+[c] to copy)</span>":
"您的粘贴的链接是<a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 [Ctrl]+[c] 以复制)</span>", "您的粘贴的链接是<a id=\"pasteurl\" href=\"%s\">%s</a> <span id=\"copyhint\">(按下 [Ctrl]+[c] 以复制)</span>",
"Delete data": "Delete data":
@ -129,7 +129,8 @@
"Source Code": "源代码", "Source Code": "源代码",
"Markdown": "Markdown", "Markdown": "Markdown",
"Download attachment": "下载附件", "Download attachment": "下载附件",
"Cloned file attached.": "已附加克隆的文件", "Cloned: '%s'": "克隆: '%s'",
"The cloned file '%s' was attached to this paste.": "克隆文件 '%s' 已附加到此粘贴。",
"Attach a file": "添加一个附件", "Attach a file": "添加一个附件",
"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.":
@ -137,15 +138,18 @@
"Invalid attachment.": "无效的附件", "Invalid attachment.": "无效的附件",
"Options": "选项", "Options": "选项",
"Shorten URL": "缩短链接", "Shorten URL": "缩短链接",
"Editor": "編輯", "Editor": "编辑",
"Preview": "預習", "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\"。 请修改你的 index.php 中的 PATH 变量。",
"Decrypt": "Decrypt":
"Decrypt", "解密",
"Enter password": "Enter password":
"Enter password", "输入密码",
"Loading…": "Loading…", "Loading…": "载入中…",
"Decrypting paste…": "正在解密",
"Preparing new paste…": "正在准备新的粘贴",
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.": "In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a>.":
"In case this message never disappears please have a look at <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">this FAQ for information to troubleshoot</a> (in English)." "如果这个消息一直不消失,请参考 <a href=\"https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away\">这里的 FAQ 进行故障排除</a> (英文版)。",
"+++ no paste text +++": "+++ 没有粘贴内容 +++"
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,9 @@ var jsc = require('jsverify'),
a2zString.map(function(c) { a2zString.map(function(c) {
return c.toUpperCase(); return c.toUpperCase();
}) })
); ),
// schemas supported by the whatwg-url library
schemas = ['ftp','gopher','http','https','ws','wss'];
global.$ = global.jQuery = require('./jquery-3.1.1'); global.$ = global.jQuery = require('./jquery-3.1.1');
global.sjcl = require('./sjcl-1.0.6'); global.sjcl = require('./sjcl-1.0.6');
@ -20,127 +22,293 @@ global.RawDeflate = require('./rawdeflate-0.5');
require('./rawinflate-0.3'); require('./rawinflate-0.3');
require('./privatebin'); require('./privatebin');
describe('helper', function () { describe('Helper', function () {
describe('secondsToHuman', function () { describe('secondsToHuman', function () {
after(function () { after(function () {
cleanup(); cleanup();
}); });
jsc.property('returns an array with a number and a word', 'integer', function (number) { jsc.property('returns an array with a number and a word', 'integer', function (number) {
var result = $.PrivateBin.helper.secondsToHuman(number); var result = $.PrivateBin.Helper.secondsToHuman(number);
return Array.isArray(result) && return Array.isArray(result) &&
result.length === 2 && result.length === 2 &&
result[0] === parseInt(result[0], 10) && result[0] === parseInt(result[0], 10) &&
typeof result[1] === 'string'; typeof result[1] === 'string';
}); });
jsc.property('returns seconds on the first array position', 'integer 59', function (number) { jsc.property('returns seconds on the first array position', 'integer 59', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === number; return $.PrivateBin.Helper.secondsToHuman(number)[0] === number;
}); });
jsc.property('returns seconds on the second array position', 'integer 59', function (number) { jsc.property('returns seconds on the second array position', 'integer 59', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'second'; return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'second';
}); });
jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) { jsc.property('returns minutes on the first array position', 'integer 60 3599', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / 60); return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / 60);
}); });
jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) { jsc.property('returns minutes on the second array position', 'integer 60 3599', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'minute'; return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'minute';
}); });
jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) { jsc.property('returns hours on the first array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60)); return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60));
}); });
jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) { jsc.property('returns hours on the second array position', 'integer 3600 86399', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'hour'; return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'hour';
}); });
jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) { jsc.property('returns days on the first array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24)); return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24));
}); });
jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) { jsc.property('returns days on the second array position', 'integer 86400 5184000', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'day'; return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'day';
}); });
// max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5 // max safe integer as per http://ecma262-5.com/ELS5_HTML.htm#Section_8.5
jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) { jsc.property('returns months on the first array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30)); return $.PrivateBin.Helper.secondsToHuman(number)[0] === Math.floor(number / (60 * 60 * 24 * 30));
}); });
jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) { jsc.property('returns months on the second array position', 'integer 5184000 9007199254740991', function (number) {
return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month'; return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'month';
}); });
}); });
describe('scriptLocation', function () { // this test is not yet meaningful using jsdom, as it does not contain getSelection support.
// TODO: This needs to be tested using a browser.
describe('selectText', function () {
jsc.property(
'selection contains content of given ID',
jsc.nearray(jsc.nearray(jsc.elements(alnumString))),
'nearray string',
function (ids, contents) {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var clean = jsdom(html);
ids.forEach(function(item, i) {
$.PrivateBin.Helper.selectText(item.join(''));
// TODO: As per https://github.com/tmpvar/jsdom/issues/321 there is no getSelection in jsdom, yet.
// Once there is one, uncomment the line below to actually check the result.
//result *= (contents[i] || contents[0]) === window.getSelection().toString();
});
clean();
return Boolean(result);
}
);
});
describe('setElementText', function () {
after(function () {
cleanup();
});
jsc.property(
'replaces the content of an element',
jsc.nearray(jsc.nearray(jsc.elements(alnumString))),
'nearray string',
'string',
function (ids, contents, replacingContent) {
var html = '',
result = true;
ids.forEach(function(item, i) {
html += '<div id="' + item.join('') + '">' + $.PrivateBin.Helper.htmlEntities(contents[i] || contents[0]) + '</div>';
});
var elements = $('<body />').html(html);
ids.forEach(function(item, i) {
var id = item.join(''),
element = elements.find('#' + id).first();
$.PrivateBin.Helper.setElementText(element, replacingContent);
result *= replacingContent === element.text();
});
return Boolean(result);
}
);
});
describe('urls2links', function () {
after(function () {
cleanup();
});
jsc.property(
'ignores non-URL content',
'string',
function (content) {
var element = $('<div>' + content + '</div>'),
before = element.html();
$.PrivateBin.Helper.urls2links(element);
return before === element.html();
}
);
jsc.property(
'replaces URLs with anchors',
'string',
jsc.elements(['http', 'https', 'ftp']),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.array(jsc.elements(queryString)),
'string',
function (prefix, schema, address, query, fragment, postfix) {
var query = query.join(''),
fragment = fragment.join(''),
url = schema + '://' + address.join('') + '/?' + query + '#' + fragment,
prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = ' ' + $.PrivateBin.Helper.htmlEntities(postfix),
element = $('<div>' + prefix + url + postfix + '</div>');
// special cases: When the query string and fragment imply the beginning of an HTML entity, eg. &#0 or &#x
if (
query.slice(-1) === '&' &&
(parseInt(fragment.substring(0, 1), 10) >= 0 || fragment.charAt(0) === 'x' )
)
{
url = schema + '://' + address.join('') + '/?' + query.substring(0, query.length - 1);
postfix = '';
element = $('<div>' + prefix + url + '</div>');
}
$.PrivateBin.Helper.urls2links(element);
return element.html() === $('<div>' + prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a>' + postfix + '</div>').html();
}
);
jsc.property(
'replaces magnet links with anchors',
'string',
jsc.array(jsc.elements(queryString)),
'string',
function (prefix, query, postfix) {
var url = 'magnet:?' + query.join(''),
prefix = $.PrivateBin.Helper.htmlEntities(prefix),
postfix = $.PrivateBin.Helper.htmlEntities(postfix),
element = $('<div>' + prefix + url + ' ' + postfix + '</div>');
$.PrivateBin.Helper.urls2links(element);
return element.html() === $('<div>' + prefix + '<a href="' + url + '" rel="nofollow">' + url + '</a> ' + postfix + '</div>').html();
}
);
});
describe('sprintf', function () {
after(function () {
cleanup();
});
jsc.property(
'replaces %s in strings with first given parameter',
'string',
'(small nearray) string',
'string',
function (prefix, params, postfix) {
var prefix = prefix.replace(/%(s|d)/g, '%%'),
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + params[0] + postfix;
params.unshift(prefix + '%s' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
'replaces %d in strings with first given parameter',
'string',
'(small nearray) nat',
'string',
function (prefix, params, postfix) {
var prefix = prefix.replace(/%(s|d)/g, '%%'),
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + params[0] + postfix;
params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
'replaces %d in strings with 0 if first parameter is not a number',
'string',
'(small nearray) falsy',
'string',
function (prefix, params, postfix) {
var prefix = prefix.replace(/%(s|d)/g, '%%'),
postfix = postfix.replace(/%(s|d)/g, '%%'),
result = prefix + '0' + postfix;
params.unshift(prefix + '%d' + postfix);
return result === $.PrivateBin.Helper.sprintf.apply(this, params)
}
);
jsc.property(
'replaces %d and %s in strings in order',
'string',
'nat',
'string',
'string',
'string',
function (prefix, uint, middle, string, postfix) {
var prefix = prefix.replace(/%(s|d)/g, '%%'),
postfix = postfix.replace(/%(s|d)/g, '%%'),
params = [prefix + '%d' + middle + '%s' + postfix, uint, string],
result = prefix + uint + middle + string + postfix;
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
jsc.property(
'replaces %d and %s in strings in reverse order',
'string',
'nat',
'string',
'string',
'string',
function (prefix, uint, middle, string, postfix) {
var prefix = prefix.replace(/%(s|d)/g, '%%'),
postfix = postfix.replace(/%(s|d)/g, '%%'),
params = [prefix + '%s' + middle + '%d' + postfix, string, uint],
result = prefix + string + middle + uint + postfix;
return result === $.PrivateBin.Helper.sprintf.apply(this, params);
}
);
});
describe('getCookie', function () {
jsc.property(
'returns the requested cookie',
'nearray asciinestring',
'nearray asciistring',
function (labels, values) {
var selectedKey = '', selectedValue = '',
cookieArray = [],
count = 0;
labels.forEach(function(item, i) {
var key = item.replace(/[\s;,=]/g, 'x'),
value = (values[i] || values[0]).replace(/[\s;,=]/g, '');
cookieArray.push(key + '=' + value);
if (Math.random() < 1 / i)
{
selectedKey = key;
selectedValue = value;
}
});
var clean = jsdom('', {cookie: cookieArray}),
result = $.PrivateBin.Helper.getCookie(selectedKey);
clean();
return result === selectedValue;
}
);
});
describe('baseUri', function () {
before(function () {
$.PrivateBin.Helper.reset();
});
jsc.property( jsc.property(
'returns the URL without query & fragment', 'returns the URL without query & fragment',
jsc.nearray(jsc.elements(a2zString)), jsc.elements(schemas),
jsc.nearray(jsc.elements(a2zString)), jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)), jsc.array(jsc.elements(queryString)),
'string', 'string',
function (schema, address, query, fragment) { function (schema, address, query, fragment) {
var expected = schema.join('') + '://' + address.join('') + '/', var expected = schema + '://' + address.join('') + '/',
clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}), clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
result = $.PrivateBin.helper.scriptLocation(); result = $.PrivateBin.Helper.baseUri();
$.PrivateBin.Helper.reset();
clean(); clean();
return expected === result; return expected === result;
} }
); );
}); });
describe('pasteId', function () {
jsc.property(
'returns the query string without separator, if any',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
'string',
function (schema, address, query, fragment) {
var queryString = query.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + queryString + '#' + fragment
}),
result = $.PrivateBin.helper.pasteId();
clean();
return queryString === result;
}
);
});
describe('pageKey', function () {
jsc.property(
'returns the fragment of the URL',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.array(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.helper.pageKey();
clean();
return fragmentString === result;
}
);
jsc.property(
'returns the fragment stripped of trailing query parts',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.array(jsc.elements(base64String)),
jsc.array(jsc.elements(queryString)),
function (schema, address, query, fragment, trail) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') + '/?' +
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.helper.pageKey();
clean();
return fragmentString === result;
}
);
});
describe('htmlEntities', function () { describe('htmlEntities', function () {
after(function () { after(function () {
cleanup(); cleanup();
@ -150,9 +318,76 @@ describe('helper', function () {
'removes all HTML entities from any given string', 'removes all HTML entities from any given string',
'string', 'string',
function (string) { function (string) {
var result = $.PrivateBin.helper.htmlEntities(string); var result = $.PrivateBin.Helper.htmlEntities(string);
return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result))); return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result)));
} }
); );
}); });
}); });
describe('Model', function () {
describe('getPasteId', function () {
before(function () {
$.PrivateBin.Model.reset();
});
jsc.property(
'returns the query string without separator, if any',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(queryString)),
'string',
function (schema, address, query, fragment) {
var queryString = query.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + queryString + '#' + fragment
}),
result = $.PrivateBin.Model.getPasteId();
$.PrivateBin.Model.reset();
clean();
return queryString === result;
}
);
});
describe('getPasteKey', function () {
jsc.property(
'returns the fragment of the URL',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
function (schema, address, query, fragment) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') +
'/?' + query.join('') + '#' + fragmentString
}),
result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset();
clean();
return fragmentString === result;
}
);
jsc.property(
'returns the fragment stripped of trailing query parts',
jsc.nearray(jsc.elements(a2zString)),
jsc.nearray(jsc.elements(a2zString)),
jsc.array(jsc.elements(queryString)),
jsc.nearray(jsc.elements(base64String)),
jsc.array(jsc.elements(queryString)),
function (schema, address, query, fragment, trail) {
var fragmentString = fragment.join(''),
clean = jsdom('', {
url: schema.join('') + '://' + address.join('') + '/?' +
query.join('') + '#' + fragmentString + '&' + trail.join('')
}),
result = $.PrivateBin.Model.getPasteKey();
$.PrivateBin.Model.reset();
clean();
return fragmentString === result;
}
);
});
});

View File

@ -1,2 +1 @@
Allow from none Require all denied
Deny from all

View File

@ -58,7 +58,7 @@ abstract class AbstractData
* @access public * @access public
* @static * @static
* @param array $options * @param array $options
* @return privatebin_abstract * @return AbstractData
*/ */
public static function getInstance($options) public static function getInstance($options)
{ {
@ -88,7 +88,6 @@ abstract class AbstractData
* *
* @access public * @access public
* @param string $pasteid * @param string $pasteid
* @return void
*/ */
abstract public function delete($pasteid); abstract public function delete($pasteid);
@ -147,7 +146,6 @@ abstract class AbstractData
* *
* @access public * @access public
* @param int $batchsize * @param int $batchsize
* @return void
*/ */
public function purge($batchsize) public function purge($batchsize)
{ {

View File

@ -282,7 +282,6 @@ class Database extends AbstractData
* *
* @access public * @access public
* @param string $pasteid * @param string $pasteid
* @return void
*/ */
public function delete($pasteid) public function delete($pasteid)
{ {
@ -375,11 +374,10 @@ class Database extends AbstractData
$comments[$i]->data = $row['data']; $comments[$i]->data = $row['data'];
$comments[$i]->meta = new stdClass; $comments[$i]->meta = new stdClass;
$comments[$i]->meta->postdate = (int) $row['postdate']; $comments[$i]->meta->postdate = (int) $row['postdate'];
if (array_key_exists('nickname', $row) && !empty($row['nickname'])) { foreach (array('nickname', 'vizhash') as $key) {
$comments[$i]->meta->nickname = $row['nickname']; if (array_key_exists($key, $row) && !empty($row[$key])) {
} $comments[$i]->meta->$key = $row[$key];
if (array_key_exists('vizhash', $row) && !empty($row['vizhash'])) { }
$comments[$i]->meta->vizhash = $row['vizhash'];
} }
} }
ksort($comments); ksort($comments);
@ -564,7 +562,6 @@ class Database extends AbstractData
* *
* @access private * @access private
* @static * @static
* @return void
*/ */
private static function _createPasteTable() private static function _createPasteTable()
{ {
@ -589,7 +586,6 @@ class Database extends AbstractData
* *
* @access private * @access private
* @static * @static
* @return void
*/ */
private static function _createCommentTable() private static function _createCommentTable()
{ {
@ -616,7 +612,6 @@ class Database extends AbstractData
* *
* @access private * @access private
* @static * @static
* @return void
*/ */
private static function _createConfigTable() private static function _createConfigTable()
{ {
@ -651,7 +646,6 @@ class Database extends AbstractData
* @access private * @access private
* @static * @static
* @param string $oldversion * @param string $oldversion
* @return void
*/ */
private static function _upgradeDatabase($oldversion) private static function _upgradeDatabase($oldversion)
{ {

View File

@ -12,8 +12,8 @@
namespace PrivateBin\Data; namespace PrivateBin\Data;
use PrivateBin\Json;
use PrivateBin\Model\Paste; use PrivateBin\Model\Paste;
use PrivateBin\Persistence\DataStore;
/** /**
* Filesystem * Filesystem
@ -22,15 +22,6 @@ use PrivateBin\Model\Paste;
*/ */
class Filesystem extends AbstractData class Filesystem extends AbstractData
{ {
/**
* directory where data is stored
*
* @access private
* @static
* @var string
*/
private static $_dir = 'data/';
/** /**
* get instance of singleton * get instance of singleton
* *
@ -41,17 +32,16 @@ class Filesystem extends AbstractData
*/ */
public static function getInstance($options = null) public static function getInstance($options = null)
{ {
// if needed initialize the singleton
if (!(self::$_instance instanceof self)) {
self::$_instance = new self;
}
// if given update the data directory // if given update the data directory
if ( if (
is_array($options) && is_array($options) &&
array_key_exists('dir', $options) array_key_exists('dir', $options)
) { ) {
self::$_dir = $options['dir'] . DIRECTORY_SEPARATOR; DataStore::setPath($options['dir']);
}
// if needed initialize the singleton
if (!(self::$_instance instanceof self)) {
self::$_instance = new self;
self::_init();
} }
return self::$_instance; return self::$_instance;
} }
@ -62,19 +52,19 @@ class Filesystem extends AbstractData
* @access public * @access public
* @param string $pasteid * @param string $pasteid
* @param array $paste * @param array $paste
* @throws Exception
* @return bool * @return bool
*/ */
public function create($pasteid, $paste) public function create($pasteid, $paste)
{ {
$storagedir = self::_dataid2path($pasteid); $storagedir = self::_dataid2path($pasteid);
if (is_file($storagedir . $pasteid)) { $file = $storagedir . $pasteid;
if (is_file($file)) {
return false; return false;
} }
if (!is_dir($storagedir)) { if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true); mkdir($storagedir, 0700, true);
} }
return (bool) file_put_contents($storagedir . $pasteid, Json::encode($paste)); return DataStore::store($file, $paste);
} }
/** /**
@ -108,7 +98,6 @@ class Filesystem extends AbstractData
* *
* @access public * @access public
* @param string $pasteid * @param string $pasteid
* @return void
*/ */
public function delete($pasteid) public function delete($pasteid)
{ {
@ -155,20 +144,19 @@ class Filesystem extends AbstractData
* @param string $parentid * @param string $parentid
* @param string $commentid * @param string $commentid
* @param array $comment * @param array $comment
* @throws Exception
* @return bool * @return bool
*/ */
public function createComment($pasteid, $parentid, $commentid, $comment) public function createComment($pasteid, $parentid, $commentid, $comment)
{ {
$storagedir = self::_dataid2discussionpath($pasteid); $storagedir = self::_dataid2discussionpath($pasteid);
$filename = $pasteid . '.' . $commentid . '.' . $parentid; $file = $storagedir . $pasteid . '.' . $commentid . '.' . $parentid;
if (is_file($storagedir . $filename)) { if (is_file($file)) {
return false; return false;
} }
if (!is_dir($storagedir)) { if (!is_dir($storagedir)) {
mkdir($storagedir, 0700, true); mkdir($storagedir, 0700, true);
} }
return (bool) file_put_contents($storagedir . $filename, Json::encode($comment)); return DataStore::store($file, $comment);
} }
/** /**
@ -237,8 +225,9 @@ class Filesystem extends AbstractData
protected function _getExpiredPastes($batchsize) protected function _getExpiredPastes($batchsize)
{ {
$pastes = array(); $pastes = array();
$mainpath = DataStore::getPath();
$firstLevel = array_filter( $firstLevel = array_filter(
scandir(self::$_dir), scandir($mainpath),
'self::_isFirstLevelDir' 'self::_isFirstLevelDir'
); );
if (count($firstLevel) > 0) { if (count($firstLevel) > 0) {
@ -246,7 +235,7 @@ class Filesystem extends AbstractData
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) { for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
$firstKey = array_rand($firstLevel); $firstKey = array_rand($firstLevel);
$secondLevel = array_filter( $secondLevel = array_filter(
scandir(self::$_dir . $firstLevel[$firstKey]), scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
'self::_isSecondLevelDir' 'self::_isSecondLevelDir'
); );
@ -257,8 +246,9 @@ class Filesystem extends AbstractData
} }
$secondKey = array_rand($secondLevel); $secondKey = array_rand($secondLevel);
$path = self::$_dir . $firstLevel[$firstKey] . $path = $mainpath . DIRECTORY_SEPARATOR .
DIRECTORY_SEPARATOR . $secondLevel[$secondKey]; $firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
$secondLevel[$secondKey];
if (!is_dir($path)) { if (!is_dir($path)) {
continue; continue;
} }
@ -292,29 +282,6 @@ class Filesystem extends AbstractData
return $pastes; return $pastes;
} }
/**
* initialize privatebin
*
* @access private
* @static
* @return void
*/
private static function _init()
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_dir)) {
mkdir(self::$_dir, 0700);
}
// Create .htaccess file if it does not exist.
if (!is_file(self::$_dir . '.htaccess')) {
file_put_contents(
self::$_dir . '.htaccess',
'Allow from none' . PHP_EOL .
'Deny from all' . PHP_EOL
);
}
}
/** /**
* Convert paste id to storage path. * Convert paste id to storage path.
* *
@ -332,8 +299,10 @@ class Filesystem extends AbstractData
*/ */
private static function _dataid2path($dataid) private static function _dataid2path($dataid)
{ {
return self::$_dir . substr($dataid, 0, 2) . DIRECTORY_SEPARATOR . return DataStore::getPath(
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR; substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR
);
} }
/** /**
@ -363,7 +332,7 @@ class Filesystem extends AbstractData
private static function _isFirstLevelDir($element) private static function _isFirstLevelDir($element)
{ {
return self::_isSecondLevelDir($element) && return self::_isSecondLevelDir($element) &&
is_dir(self::$_dir . DIRECTORY_SEPARATOR . $element); is_dir(DataStore::getPath($element));
} }
/** /**

View File

@ -21,21 +21,6 @@ use Exception;
*/ */
class Filter class Filter
{ {
/**
* strips slashes deeply
*
* @access public
* @static
* @param mixed $value
* @return mixed
*/
public static function stripslashesDeep($value)
{
return is_array($value) ?
array_map('self::stripslashesDeep', $value) :
stripslashes($value);
}
/** /**
* format a given time string into a human readable label (localized) * format a given time string into a human readable label (localized)
* *

View File

@ -135,15 +135,17 @@ class I18n
* *
* @access public * @access public
* @static * @static
* @return void
*/ */
public static function loadTranslations() public static function loadTranslations()
{ {
$availableLanguages = self::getAvailableLanguages(); $availableLanguages = self::getAvailableLanguages();
// check if the lang cookie was set and that language exists // check if the lang cookie was set and that language exists
if (array_key_exists('lang', $_COOKIE) && in_array($_COOKIE['lang'], $availableLanguages)) { if (
$match = $_COOKIE['lang']; array_key_exists('lang', $_COOKIE) &&
($key = array_search($_COOKIE['lang'], $availableLanguages)) !== false
) {
$match = $availableLanguages[$key];
} }
// find a translation file matching the browsers language preferences // find a translation file matching the browsers language preferences
else { else {
@ -256,7 +258,6 @@ class I18n
* @access public * @access public
* @static * @static
* @param string $lang * @param string $lang
* @return void
*/ */
public static function setLanguageFallback($lang) public static function setLanguageFallback($lang)
{ {
@ -304,7 +305,7 @@ class I18n
return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2); return $n % 10 == 1 && $n % 100 != 11 ? 0 : ($n % 10 >= 2 && $n % 10 <= 4 && ($n % 100 < 10 || $n % 100 >= 20) ? 1 : 2);
case 'sl': case 'sl':
return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0)); return $n % 100 == 1 ? 1 : ($n % 100 == 2 ? 2 : ($n % 100 == 3 || $n % 100 == 4 ? 3 : 0));
// de, en, es, it, no // de, en, es, it, no, pt
default: default:
return $n != 1 ? 1 : 0; return $n != 1 ? 1 : 0;
} }

View File

@ -40,7 +40,6 @@ class Model
* Factory constructor. * Factory constructor.
* *
* @param configuration $conf * @param configuration $conf
* @return void
*/ */
public function __construct(Configuration $conf) public function __construct(Configuration $conf)
{ {
@ -64,8 +63,6 @@ class Model
/** /**
* Checks if a purge is necessary and triggers it if yes. * Checks if a purge is necessary and triggers it if yes.
*
* @return void
*/ */
public function purge() public function purge()
{ {

View File

@ -63,7 +63,6 @@ abstract class AbstractModel
* @access public * @access public
* @param Configuration $configuration * @param Configuration $configuration
* @param AbstractData $storage * @param AbstractData $storage
* @return void
*/ */
public function __construct(Configuration $configuration, AbstractData $storage) public function __construct(Configuration $configuration, AbstractData $storage)
{ {
@ -90,7 +89,6 @@ abstract class AbstractModel
* @access public * @access public
* @param string $id * @param string $id
* @throws Exception * @throws Exception
* @return void
*/ */
public function setId($id) public function setId($id)
{ {
@ -106,7 +104,6 @@ abstract class AbstractModel
* @access public * @access public
* @param string $data * @param string $data
* @throws Exception * @throws Exception
* @return void
*/ */
public function setData($data) public function setData($data)
{ {
@ -133,7 +130,6 @@ abstract class AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
abstract public function store(); abstract public function store();
@ -142,7 +138,6 @@ abstract class AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
abstract public function delete(); abstract public function delete();

View File

@ -61,7 +61,6 @@ class Comment extends AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
public function store() public function store()
{ {
@ -101,7 +100,6 @@ class Comment extends AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
public function delete() public function delete()
{ {
@ -129,7 +127,6 @@ class Comment extends AbstractModel
* @access public * @access public
* @param Paste $paste * @param Paste $paste
* @throws Exception * @throws Exception
* @return void
*/ */
public function setPaste(Paste $paste) public function setPaste(Paste $paste)
{ {
@ -154,7 +151,6 @@ class Comment extends AbstractModel
* @access public * @access public
* @param string $id * @param string $id
* @throws Exception * @throws Exception
* @return void
*/ */
public function setParentId($id) public function setParentId($id)
{ {
@ -184,7 +180,6 @@ class Comment extends AbstractModel
* @access public * @access public
* @param string $nickname * @param string $nickname
* @throws Exception * @throws Exception
* @return void
*/ */
public function setNickname($nickname) public function setNickname($nickname)
{ {

View File

@ -75,7 +75,6 @@ class Paste extends AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
public function store() public function store()
{ {
@ -103,7 +102,6 @@ class Paste extends AbstractModel
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
public function delete() public function delete()
{ {
@ -183,7 +181,6 @@ class Paste extends AbstractModel
* @access public * @access public
* @param string $attachment * @param string $attachment
* @throws Exception * @throws Exception
* @return void
*/ */
public function setAttachment($attachment) public function setAttachment($attachment)
{ {
@ -199,7 +196,6 @@ class Paste extends AbstractModel
* @access public * @access public
* @param string $attachmentname * @param string $attachmentname
* @throws Exception * @throws Exception
* @return void
*/ */
public function setAttachmentName($attachmentname) public function setAttachmentName($attachmentname)
{ {
@ -214,7 +210,6 @@ class Paste extends AbstractModel
* *
* @access public * @access public
* @param string $expiration * @param string $expiration
* @return void
*/ */
public function setExpiration($expiration) public function setExpiration($expiration)
{ {
@ -236,7 +231,6 @@ class Paste extends AbstractModel
* @access public * @access public
* @param string $burnafterreading * @param string $burnafterreading
* @throws Exception * @throws Exception
* @return void
*/ */
public function setBurnafterreading($burnafterreading = '1') public function setBurnafterreading($burnafterreading = '1')
{ {
@ -257,7 +251,6 @@ class Paste extends AbstractModel
* @access public * @access public
* @param string $opendiscussion * @param string $opendiscussion
* @throws Exception * @throws Exception
* @return void
*/ */
public function setOpendiscussion($opendiscussion = '1') public function setOpendiscussion($opendiscussion = '1')
{ {
@ -281,7 +274,6 @@ class Paste extends AbstractModel
* @access public * @access public
* @param string $format * @param string $format
* @throws Exception * @throws Exception
* @return void
*/ */
public function setFormatter($format) public function setFormatter($format)
{ {

View File

@ -36,7 +36,6 @@ abstract class AbstractPersistence
* @access public * @access public
* @static * @static
* @param string $path * @param string $path
* @return void
*/ */
public static function setPath($path) public static function setPath($path)
{ {
@ -80,27 +79,23 @@ abstract class AbstractPersistence
* @access protected * @access protected
* @static * @static
* @throws Exception * @throws Exception
* @return void
*/ */
protected static function _initialize() protected static function _initialize()
{ {
// Create storage directory if it does not exist. // Create storage directory if it does not exist.
if (!is_dir(self::$_path)) { if (!is_dir(self::$_path)) {
if (!@mkdir(self::$_path)) { if (!@mkdir(self::$_path, 0700)) {
throw new Exception('unable to create directory ' . self::$_path, 10); throw new Exception('unable to create directory ' . self::$_path, 10);
} }
} }
// Create .htaccess file if it does not exist.
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess'; $file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
if (!is_file($file)) { if (!is_file($file)) {
$writtenBytes = @file_put_contents( $writtenBytes = @file_put_contents(
$file, $file,
'Allow from none' . PHP_EOL . 'Require all denied' . PHP_EOL,
'Deny from all' . PHP_EOL,
LOCK_EX LOCK_EX
); );
if ($writtenBytes === false || $writtenBytes < 30) { if ($writtenBytes === false || $writtenBytes < 19) {
throw new Exception('unable to write to file ' . $file, 11); throw new Exception('unable to write to file ' . $file, 11);
} }
} }

View File

@ -0,0 +1,47 @@
<?php
/**
* 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
* @version 1.1
*/
namespace PrivateBin\Persistence;
use Exception;
use PrivateBin\Json;
/**
* DataStore
*
* Handles data storage for Data\Filesystem.
*/
class DataStore extends AbstractPersistence
{
/**
* store the data
*
* @access public
* @static
* @param string $filename
* @param array $data
* @return bool
*/
public static function store($filename, $data)
{
$path = self::getPath();
if (strpos($filename, $path) === 0) {
$filename = substr($filename, strlen($path));
}
try {
self::_store($filename, Json::encode($data));
return true;
} catch (Exception $e) {
return false;
}
}
}

View File

@ -36,7 +36,6 @@ class PurgeLimiter extends AbstractPersistence
* @access public * @access public
* @static * @static
* @param int $limit * @param int $limit
* @return void
*/ */
public static function setLimit($limit) public static function setLimit($limit)
{ {
@ -49,7 +48,6 @@ class PurgeLimiter extends AbstractPersistence
* @access public * @access public
* @static * @static
* @param Configuration $conf * @param Configuration $conf
* @return void
*/ */
public static function setConfiguration(Configuration $conf) public static function setConfiguration(Configuration $conf)
{ {

View File

@ -95,7 +95,6 @@ class ServerSalt extends AbstractPersistence
* @access public * @access public
* @static * @static
* @param string $path * @param string $path
* @return void
*/ */
public static function setPath($path) public static function setPath($path)
{ {

View File

@ -45,7 +45,6 @@ class TrafficLimiter extends AbstractPersistence
* @access public * @access public
* @static * @static
* @param int $limit * @param int $limit
* @return void
*/ */
public static function setLimit($limit) public static function setLimit($limit)
{ {
@ -58,7 +57,6 @@ class TrafficLimiter extends AbstractPersistence
* @access public * @access public
* @static * @static
* @param Configuration $conf * @param Configuration $conf
* @return void
*/ */
public static function setConfiguration(Configuration $conf) public static function setConfiguration(Configuration $conf)
{ {

View File

@ -30,6 +30,13 @@ class PrivateBin
*/ */
const VERSION = '1.1'; const VERSION = '1.1';
/**
* minimal required PHP version
*
* @const string
*/
const MIN_PHP_VERSION = '5.4.0';
/** /**
* show the same error message if the paste expired or does not exist * show the same error message if the paste expired or does not exist
* *
@ -116,12 +123,11 @@ class PrivateBin
* *
* @access public * @access public
* @throws Exception * @throws Exception
* @return void
*/ */
public function __construct() public function __construct()
{ {
if (version_compare(PHP_VERSION, '5.3.0') < 0) { if (version_compare(PHP_VERSION, self::MIN_PHP_VERSION) < 0) {
throw new Exception(I18n::_('%s requires php 5.3.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1); throw new Exception(I18n::_('%s requires php %s or above to work. Sorry.', I18n::_('PrivateBin'), self::MIN_PHP_VERSION), 1);
} }
if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) { if (strlen(PATH) < 0 && substr(PATH, -1) !== DIRECTORY_SEPARATOR) {
throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5); throw new Exception(I18n::_('%s requires the PATH to end in a "%s". Please update the PATH in your index.php.', I18n::_('PrivateBin'), DIRECTORY_SEPARATOR), 5);
@ -164,21 +170,9 @@ class PrivateBin
* initialize privatebin * initialize privatebin
* *
* @access private * @access private
* @return void
*/ */
private function _init() private function _init()
{ {
foreach (array('cfg', 'lib') as $dir) {
if (!is_file(PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess')) {
file_put_contents(
PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess',
'Allow from none' . PHP_EOL .
'Deny from all' . PHP_EOL,
LOCK_EX
);
}
}
$this->_conf = new Configuration; $this->_conf = new Configuration;
$this->_model = new Model($this->_conf); $this->_model = new Model($this->_conf);
$this->_request = new Request; $this->_request = new Request;
@ -324,7 +318,6 @@ class PrivateBin
* @access private * @access private
* @param string $dataid * @param string $dataid
* @param string $deletetoken * @param string $deletetoken
* @return void
*/ */
private function _delete($dataid, $deletetoken) private function _delete($dataid, $deletetoken)
{ {
@ -334,19 +327,16 @@ class PrivateBin
// accessing this property ensures that the paste would be // accessing this property ensures that the paste would be
// deleted if it has already expired // deleted if it has already expired
$burnafterreading = $paste->isBurnafterreading(); $burnafterreading = $paste->isBurnafterreading();
if ($deletetoken == 'burnafterreading') { if (
if ($burnafterreading) { ($burnafterreading && $deletetoken == 'burnafterreading') ||
$paste->delete(); Filter::slowEquals($deletetoken, $paste->getDeleteToken())
$this->_return_message(0, $dataid); ) {
} else { // Paste exists and deletion token is valid: Delete the paste.
$this->_return_message(1, 'Paste is not of burn-after-reading type.'); $paste->delete();
} $this->_status = 'Paste was properly deleted.';
} else { } else {
// Make sure the token is valid. if (!$burnafterreading && $deletetoken == 'burnafterreading') {
if (Filter::slowEquals($deletetoken, $paste->getDeleteToken())) { $this->_error = 'Paste is not of burn-after-reading type.';
// Paste exists and deletion token is valid: Delete the paste.
$paste->delete();
$this->_status = 'Paste was properly deleted.';
} else { } else {
$this->_error = 'Wrong deletion token. Paste was not deleted.'; $this->_error = 'Wrong deletion token. Paste was not deleted.';
} }
@ -357,6 +347,13 @@ class PrivateBin
} catch (Exception $e) { } catch (Exception $e) {
$this->_error = $e->getMessage(); $this->_error = $e->getMessage();
} }
if ($this->_request->isJsonApiCall()) {
if (strlen($this->_error)) {
$this->_return_message(1, $this->_error);
} else {
$this->_return_message(0, $dataid);
}
}
} }
/** /**
@ -364,7 +361,6 @@ class PrivateBin
* *
* @access private * @access private
* @param string $dataid * @param string $dataid
* @return void
*/ */
private function _read($dataid) private function _read($dataid)
{ {
@ -397,7 +393,6 @@ class PrivateBin
* Display PrivateBin frontend. * Display PrivateBin frontend.
* *
* @access private * @access private
* @return void
*/ */
private function _view() private function _view()
{ {
@ -461,7 +456,6 @@ class PrivateBin
* *
* @access private * @access private
* @param string $type * @param string $type
* @return void
*/ */
private function _jsonld($type) private function _jsonld($type)
{ {
@ -494,7 +488,6 @@ class PrivateBin
* @param int $status * @param int $status
* @param string $message * @param string $message
* @param array $other * @param array $other
* @return void
*/ */
private function _return_message($status, $message, $other = array()) private function _return_message($status, $message, $other = array())
{ {

View File

@ -41,7 +41,7 @@ class Request
const MIME_XHTML = 'application/xhtml+xml'; const MIME_XHTML = 'application/xhtml+xml';
/** /**
* Input stream to use for PUT parameter parsing. * Input stream to use for PUT parameter parsing
* *
* @access private * @access private
* @var string * @var string
@ -49,7 +49,7 @@ class Request
private static $_inputStream = 'php://input'; private static $_inputStream = 'php://input';
/** /**
* Operation to perform. * Operation to perform
* *
* @access private * @access private
* @var string * @var string
@ -57,7 +57,7 @@ class Request
private $_operation = 'view'; private $_operation = 'view';
/** /**
* Request parameters. * Request parameters
* *
* @access private * @access private
* @var array * @var array
@ -65,7 +65,7 @@ class Request
private $_params = array(); private $_params = array();
/** /**
* If we are in a JSON API context. * If we are in a JSON API context
* *
* @access private * @access private
* @var bool * @var bool
@ -73,20 +73,12 @@ class Request
private $_isJsonApi = false; private $_isJsonApi = false;
/** /**
* Constructor. * Constructor
* *
* @access public * @access public
* @return void
*/ */
public function __construct() public function __construct()
{ {
// in case stupid admin has left magic_quotes enabled in php.ini (for PHP < 5.4)
if (version_compare(PHP_VERSION, '5.4.0') < 0 && get_magic_quotes_gpc()) {
$_POST = array_map('PrivateBin\\Filter::stripslashesDeep', $_POST);
$_GET = array_map('PrivateBin\\Filter::stripslashesDeep', $_GET);
$_COOKIE = array_map('PrivateBin\\Filter::stripslashesDeep', $_COOKIE);
}
// decide if we are in JSON API or HTML context // decide if we are in JSON API or HTML context
$this->_isJsonApi = $this->_detectJsonRequest(); $this->_isJsonApi = $this->_detectJsonRequest();
@ -129,7 +121,7 @@ class Request
} }
/** /**
* Get current operation. * Get current operation
* *
* @access public * @access public
* @return string * @return string
@ -140,7 +132,7 @@ class Request
} }
/** /**
* Get a request parameter. * Get a request parameter
* *
* @access public * @access public
* @param string $param * @param string $param
@ -153,7 +145,7 @@ class Request
} }
/** /**
* If we are in a JSON API context. * If we are in a JSON API context
* *
* @access public * @access public
* @return bool * @return bool
@ -164,7 +156,7 @@ class Request
} }
/** /**
* Override the default input stream source, used for unit testing. * Override the default input stream source, used for unit testing
* *
* @param string $input * @param string $input
*/ */
@ -174,7 +166,7 @@ class Request
} }
/** /**
* detect the clients supported media type and decide if its a JSON API call or not * Detect the clients supported media type and decide if its a JSON API call or not
* *
* Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447 * Adapted from: https://stackoverflow.com/questions/3770513/detect-browser-language-in-php#3771447
* *

View File

@ -35,7 +35,6 @@ class View
* @access public * @access public
* @param string $name * @param string $name
* @param mixed $value * @param mixed $value
* @return void
*/ */
public function assign($name, $value) public function assign($name, $value)
{ {
@ -48,7 +47,6 @@ class View
* @access public * @access public
* @param string $template * @param string $template
* @throws Exception * @throws Exception
* @return void
*/ */
public function draw($template) public function draw($template)
{ {

View File

@ -61,7 +61,6 @@ class Vizhash16x16
* constructor * constructor
* *
* @access public * @access public
* @return void
*/ */
public function __construct() public function __construct()
{ {
@ -210,7 +209,6 @@ class Vizhash16x16
* @param resource $image * @param resource $image
* @param int $action * @param int $action
* @param int $color * @param int $color
* @return void
*/ */
private function drawshape($image, $action, $color) private function drawshape($image, $action, $color)
{ {

1
tpl/.htaccess Normal file
View File

@ -0,0 +1 @@
Require all denied

View File

@ -4,7 +4,7 @@ $isCpct = substr($template, 9, 8) === '-compact';
$isDark = substr($template, 9, 5) === '-dark'; $isDark = substr($template, 9, 5) === '-dark';
$isPage = substr($template, -5) === '-page'; $isPage = substr($template, -5) === '-page';
?><!DOCTYPE html> ?><!DOCTYPE html>
<html lang="en"> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
@ -69,7 +69,7 @@ if ($MARKDOWN):
<?php <?php
endif; endif;
?> ?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/5orRNmbpvxqudg675a1LiF3wQ5DlMGoT6jI/iXDZN2x2DrLHnB3tSE0wGY6qpeY+eX9vv6mZ/6AuPm0gnU2/A==" crossorigin="anonymous"></script> <script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-+/yhR0jcxCRLS10m+w8ii0BDeifeMrT7moI3gNalod6rHdgzMfc962q+2q3880fQ5XhcjF+/hx+8bKxRUMOaCg==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->
@ -94,7 +94,7 @@ endif;
<form id="passwordform" role="form"> <form id="passwordform" role="form">
<div class="form-group"> <div class="form-group">
<label for="passworddecrypt"><span class="glyphicon glyphicon-eye-open"></span> <?php echo I18n::_('Please enter the password for this paste:') ?></label> <label for="passworddecrypt"><span class="glyphicon glyphicon-eye-open"></span> <?php echo I18n::_('Please enter the password for this paste:') ?></label>
<input id="passworddecrypt" type="password" class="form-control" placeholder="<?php echo I18n::_('Enter password') ?>" autofocus> <input id="passworddecrypt" type="password" class="form-control" placeholder="<?php echo I18n::_('Enter password') ?>">
</div> </div>
<button type="submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span> <?php echo I18n::_('Decrypt') ?></button> <button type="submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span> <?php echo I18n::_('Decrypt') ?></button>
</form> </form>
@ -121,8 +121,8 @@ endif;
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li id="loadingindicator" class="navbar-text hidden"> <li id="loadingindicator" class="navbar-text hidden">
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <span class="glyphicon glyphicon-time" aria-hidden="true"></span>
<?php echo I18n::_('Uploading paste… Please wait.'), PHP_EOL; ?> <?php echo I18n::_('Loading…'), PHP_EOL; ?>
</li> </li>
<li> <li>
<?php <?php
@ -132,7 +132,7 @@ if ($isPage):
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL; <span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL;
else: else:
?> ?>
<button id="newbutton" type="button" class="reloadlink hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn"> <button id="newbutton" type="button" class="hidden btn btn-<?php echo $isDark ? 'warning' : 'default'; ?> navbar-btn">
<span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL; <span class="glyphicon glyphicon-file" aria-hidden="true"></span> <?php echo I18n::_('New'), PHP_EOL;
endif; endif;
?> ?>
@ -198,7 +198,7 @@ if ($isCpct):
<?php <?php
if ($DISCUSSION): if ($DISCUSSION):
?> ?>
<li id="opendisc" class="checkbox hidden"> <li id="opendiscussionoption" class="checkbox hidden">
<label> <label>
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php <input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION): if ($OPENDISCUSSION):
@ -230,17 +230,6 @@ if ($isCpct):
?> ?>
</ul> </ul>
<select id="pasteFormatter" name="pasteFormatter" class="hidden"> <select id="pasteFormatter" name="pasteFormatter" class="hidden">
<?php
foreach ($FORMATTER as $key => $value):
?>
<option value="<?php echo $key; ?>"<?php
if ($key == $FORMATTERDEFAULT):
?> selected="selected"<?php
endif;
?>><?php echo $value; ?></option>
<?php
endforeach;
?>
</select> </select>
</li> </li>
<?php <?php
@ -262,7 +251,7 @@ else:
if ($DISCUSSION): if ($DISCUSSION):
?> ?>
<li> <li>
<div id="opendisc" class="navbar-text checkbox hidden"> <div id="opendiscussionoption" class="navbar-text checkbox hidden">
<label> <label>
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php <input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION): if ($OPENDISCUSSION):
@ -296,6 +285,7 @@ if ($FILEUPLOAD):
</div> </div>
<div id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('or drag & drop file'); ?></div> <div id="dragAndDropFileName" class="dragAndDropFile"><?php echo I18n::_('or drag & drop file'); ?></div>
</li> </li>
<li id="customattachment" class="hidden"></li>
<li> <li>
<a id="fileremovebutton" href="#"> <a id="fileremovebutton" href="#">
<?php echo I18n::_('Remove attachment'), PHP_EOL; ?> <?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
@ -384,49 +374,57 @@ if ($isCpct):
?></div><?php ?></div><?php
endif; endif;
?></nav> ?></nav>
<header class="container"> <main>
<section class="container">
<?php <?php
if (strlen($NOTICE)): if (strlen($NOTICE)):
?> ?>
<div role="alert" class="alert alert-info"> <div role="alert" class="alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?> <span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
<?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
</div> </div>
<?php <?php
endif; endif;
?> ?>
<div id="remainingtime" role="alert" class="hidden alert alert-info"> <div id="remainingtime" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <span class="glyphicon glyphicon-fire" aria-hidden="true"></span>
</div> </div>
<?php <?php
if ($FILEUPLOAD): if ($FILEUPLOAD):
?> ?>
<div id="attachment" role="alert" class="hidden alert alert-info"> <div id="attachment" role="alert" class="hidden alert alert-info">
<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <a><?php echo I18n::_('Download attachment'); ?></a> <span id="clonedfile" class="hidden"><?php echo I18n::_('Cloned file attached.'); ?></span> <span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>
</div> <a class="alert-link"><?php echo I18n::_('Download attachment'), PHP_EOL; ?></a>
<?php
endif;
if (strlen($STATUS)):
?>
<div id="status" role="alert" class="alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
</div> </div>
<?php <?php
endif; endif;
?> ?>
<div id="errormessage" role="alert" class="<?php <div id="status" role="alert" class="statusmessage alert alert-info<?php echo empty($STATUS) ? ' hidden' : '' ?>">
if (!strlen($ERROR)): <span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
?>hidden <?php <?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
endif; </div>
?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></div> <div id="errormessage" role="alert" class="statusmessage<?php echo empty($ERROR) ? ' hidden' : '' ?> alert alert-danger">
<noscript><div id="noscript" role="alert" class="nonworking alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> <?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)); ?></div></noscript> <span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)); ?></div> <?php echo htmlspecialchars($ERROR), PHP_EOL; ?>
<div id="ienotice" role="alert" class="hidden alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>"><span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span> <?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?> </div>
<noscript>
<div id="noscript" role="alert" class="nonworking alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>">
<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<?php echo I18n::_('JavaScript is required for %s to work.<br />Sorry for the inconvenience.', I18n::_($NAME)), PHP_EOL; ?>
</div>
</noscript>
<div id="oldienotice" role="alert" class="hidden nonworking alert alert-danger">
<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
<?php echo I18n::_('%s requires a modern browser to work.', I18n::_($NAME)), PHP_EOL; ?>
</div>
<div id="ienotice" role="alert" class="hidden alert alert-<?php echo $isDark ? 'error' : 'warning'; ?>">
<span class="glyphicon glyphicon-question-sign" aria-hidden="true"></span>
<?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
<a href="https://www.mozilla.org/firefox/">Firefox</a>, <a href="https://www.mozilla.org/firefox/">Firefox</a>,
<a href="https://www.opera.com/">Opera</a>, <a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>, <a href="https://www.google.com/chrome">Chrome</a>
<a href="https://www.apple.com/safari">Safari</a>...
</div> </div>
<div id="pasteresult" role="alert" class="hidden alert alert-success"> <div id="pasteSuccess" role="alert" class="hidden alert alert-success">
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> <span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<div id="deletelink"></div> <div id="deletelink"></div>
<div id="pastelink"> <div id="pastelink">
@ -441,30 +439,32 @@ endif;
?> ?>
</div> </div>
</div> </div>
<ul id="preview" class="nav nav-tabs hidden"> <ul id="editorTabs" class="nav nav-tabs hidden">
<li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li> <li role="presentation" class="active"><a id="messageedit" href="#"><?php echo I18n::_('Editor'); ?></a></li>
<li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li> <li role="presentation"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
</ul> </ul>
</header> </section>
<section class="container"> <section class="container">
<article class="row"> <article class="row">
<div id="attachmentPreview" class="col-md-12 text-center hidden"></div> <div id="placeholder" class="col-md-12 hidden"><?php echo I18n::_('+++ no paste text +++'); ?></div>
<div id="attachmentPreview" class="col-md-12 text-center hidden"></div>
<div id="prettymessage" class="col-md-12 hidden"> <div id="prettymessage" class="col-md-12 hidden">
<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre> <pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
</div> </div>
<div id="cleartext" class="col-md-12 hidden"></div> <div id="plaintext" class="col-md-12 hidden"></div>
<p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p> <p class="col-md-12"><textarea id="message" name="message" cols="80" rows="25" class="form-control hidden"></textarea></p>
</article> </article>
</section> </section>
<section class="container"> <section class="container">
<div id="discussion" class="hidden"> <div id="discussion" class="hidden">
<h4><?php echo I18n::_('Discussion'); ?></h4> <h4><?php echo I18n::_('Discussion'); ?></h4>
<div id="comments"></div> <div id="commentcontainer"></div>
</div> </div>
</section> </section>
<section class="container"> <section class="container">
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"> <div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide">
<span> <?php echo I18n::_('Loading…'); ?></span><br> <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
<?php echo I18n::_('Loading…'); ?><br />
<span class="small"><?php echo I18n::_('In case this message never disappears please have a look at <a href="https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away">this FAQ for information to troubleshoot</a>.'); ?></span> <span class="small"><?php echo I18n::_('In case this message never disappears please have a look at <a href="https://github.com/PrivateBin/PrivateBin/wiki/FAQ#why-does-not-the-loading-message-go-away">this FAQ for information to troubleshoot</a>.'); ?></span>
</div> </div>
</section> </section>
@ -477,6 +477,21 @@ endif;
</p> </p>
</div> </div>
</footer> </footer>
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div> </main>
<div id="serverdata" class="hidden" aria-hidden="true">
<div id="cipherdata"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<?php
if ($DISCUSSION):
?>
<div id="templates">
<!-- @TODO: when I intend/structure this corrrectly Firefox adds whitespaces everywhere which completly destroy the layout. (same possible when you remove the template data below and show this area in the browser) -->
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
<p id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></p>
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
</div>
<?php
endif;
?>
</div>
</body> </body>
</html> </html>

View File

@ -47,7 +47,7 @@ if ($MARKDOWN):
<?php <?php
endif; endif;
?> ?>
<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-/5orRNmbpvxqudg675a1LiF3wQ5DlMGoT6jI/iXDZN2x2DrLHnB3tSE0wGY6qpeY+eX9vv6mZ/6AuPm0gnU2/A==" crossorigin="anonymous"></script> <script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-+/yhR0jcxCRLS10m+w8ii0BDeifeMrT7moI3gNalod6rHdgzMfc962q+2q3880fQ5XhcjF+/hx+8bKxRUMOaCg==" crossorigin="anonymous"></script>
<!--[if lt IE 10]> <!--[if lt IE 10]>
<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style> <style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
<![endif]--> <![endif]-->
@ -79,12 +79,12 @@ endif;
<div id="ienotice"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?> <div id="ienotice"><?php echo I18n::_('Still using Internet Explorer? Do yourself a favor, switch to a modern browser:'), PHP_EOL; ?>
<a href="https://www.mozilla.org/firefox/">Firefox</a>, <a href="https://www.mozilla.org/firefox/">Firefox</a>,
<a href="https://www.opera.com/">Opera</a>, <a href="https://www.opera.com/">Opera</a>,
<a href="https://www.google.com/chrome">Chrome</a>, <a href="https://www.google.com/chrome">Chrome</a>
<a href="https://www.apple.com/safari">Safari</a>...
</div> </div>
</header> </header>
<section> <section>
<article> <article>
<div id="loadingindicator" class="hidden"><?php echo I18n::_('Loading…'); ?></div>
<div id="status"><?php echo htmlspecialchars($STATUS); ?></div> <div id="status"><?php echo htmlspecialchars($STATUS); ?></div>
<div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div> <div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div>
<div id="toolbar"> <div id="toolbar">
@ -125,7 +125,7 @@ endif;
<?php <?php
if ($DISCUSSION): if ($DISCUSSION):
?> ?>
<div id="opendisc" class="button hidden"> <div id="opendiscussionoption" class="button hidden">
<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php <input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
if ($OPENDISCUSSION): if ($OPENDISCUSSION):
?> checked="checked"<?php ?> checked="checked"<?php
@ -217,17 +217,31 @@ endif;
<div id="prettymessage" class="hidden"> <div id="prettymessage" class="hidden">
<pre id="prettyprint" class="prettyprint linenums:1"></pre> <pre id="prettyprint" class="prettyprint linenums:1"></pre>
</div> </div>
<div id="cleartext" class="hidden"></div> <div id="plaintext" class="hidden"></div>
<textarea id="message" name="message" cols="80" rows="25" class="hidden"></textarea> <textarea id="message" name="message" cols="80" rows="25" class="hidden"></textarea>
</article> </article>
</section> </section>
<section> <section>
<div id="discussion" class="hidden"> <div id="discussion" class="hidden">
<h4 class="title"><?php echo I18n::_('Discussion'); ?></h4> <h4 class="title"><?php echo I18n::_('Discussion'); ?></h4>
<div id="comments"></div> <div id="commentcontainer"></div>
</div> </div>
</section> </section>
<div id="serverdata" class="hidden" aria-hidden="true">
<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div> <div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
<?php
if ($DISCUSSION):
?>
<div id="templates">
<!-- @TODO: when I intend/structure this corrrectly Firefox adds whitespaces everywhere which completly destroy the layout. (same possible when you remove the template data below and show this area in the browser) -->
<article id="commenttemplate" class="comment"><div class="commentmeta"><span class="nickname">name</span><span class="commentdate">0000-00-00</span></div><div class="commentdata">c</div><button class="btn btn-default btn-sm"><?php echo I18n::_('Reply'); ?></button></article>
<div id="commenttailtemplate" class="comment"><button class="btn btn-default btn-sm"><?php echo I18n::_('Add comment'); ?></button></div>
<div id="replytemplate" class="reply hidden"><input type="text" id="nickname" class="form-control" title="<?php echo I18n::_('Optional nickname…'); ?>" placeholder="<?php echo I18n::_('Optional nickname…'); ?>" /><textarea id="replymessage" class="replymessage form-control" cols="80" rows="7"></textarea><br /><div id="replystatus" role="alert" class="statusmessage hidden alert"><span class="glyphicon" aria-hidden="true"></span> </div><button id="replybutton" class="btn btn-default btn-sm"><?php echo I18n::_('Post comment'); ?></button></div>
</div>
<?php
endif;
?>
</div>
<section class="container"> <section class="container">
<div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"> <div id="noscript" role="alert" class="nonworking alert alert-info noscript-hide"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true">
<span> <?php echo I18n::_('Loading…'); ?></span><br> <span> <?php echo I18n::_('Loading…'); ?></span><br>

1
tst/.gitignore vendored
View File

@ -1 +0,0 @@
/ConfigurationCombinationsTest.php

View File

@ -1,2 +0,0 @@
Allow from none
Deny from all

View File

@ -172,22 +172,24 @@ class Helper
*/ */
public static function rmDir($path) public static function rmDir($path)
{ {
$path .= DIRECTORY_SEPARATOR; if (is_dir($path)) {
$dir = dir($path); $path .= DIRECTORY_SEPARATOR;
while (false !== ($file = $dir->read())) { $dir = dir($path);
if ($file != '.' && $file != '..') { while (false !== ($file = $dir->read())) {
if (is_dir($path . $file)) { if ($file != '.' && $file != '..') {
self::rmDir($path . $file); if (is_dir($path . $file)) {
} elseif (is_file($path . $file)) { self::rmDir($path . $file);
if (!unlink($path . $file)) { } elseif (is_file($path . $file)) {
throw new Exception('Error deleting file "' . $path . $file . '".'); if (!unlink($path . $file)) {
throw new Exception('Error deleting file "' . $path . $file . '".');
}
} }
} }
} }
} $dir->close();
$dir->close(); if (!rmdir($path)) {
if (!rmdir($path)) { throw new Exception('Error deleting directory "' . $path . '".');
throw new Exception('Error deleting directory "' . $path . '".'); }
} }
} }

View File

@ -311,7 +311,7 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
'vizhash BLOB, ' . 'vizhash BLOB, ' .
'postdate INT );' 'postdate INT );'
); );
$this->assertInstanceOf(Database::class, Database::getInstance($this->_options)); $this->assertInstanceOf('PrivateBin\\Data\\Database', Database::getInstance($this->_options));
// check if version number was upgraded in created configuration table // check if version number was upgraded in created configuration table
$statement = $db->prepare('SELECT value FROM foo_config WHERE id LIKE ?'); $statement = $db->prepare('SELECT value FROM foo_config WHERE id LIKE ?');

View File

@ -8,16 +8,26 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
private $_path; private $_path;
private $_invalidPath;
public function setUp() public function setUp()
{ {
/* Setup Routine */ /* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path)); $this->_invalidPath = $this->_path . DIRECTORY_SEPARATOR . 'bar';
$this->_model = Filesystem::getInstance(array('dir' => $this->_path));
if (!is_dir($this->_path)) {
mkdir($this->_path);
}
if (!is_dir($this->_invalidPath)) {
mkdir($this->_invalidPath);
}
} }
public function tearDown() public function tearDown()
{ {
/* Tear Down Routine */ /* Tear Down Routine */
chmod($this->_invalidPath, 0700);
Helper::rmDir($this->_path); Helper::rmDir($this->_path);
} }
@ -37,6 +47,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist'); $this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment'); $this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it'); $this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice');
$comment = json_decode(json_encode(Helper::getComment())); $comment = json_decode(json_encode(Helper::getComment()));
$comment->id = Helper::getCommentId(); $comment->id = Helper::getCommentId();
$comment->parentid = Helper::getPasteId(); $comment->parentid = Helper::getPasteId();
@ -99,10 +110,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
} }
} }
/**
* @expectedException Exception
* @expectedExceptionCode 90
*/
public function testErrorDetection() public function testErrorDetection()
{ {
$this->_model->delete(Helper::getPasteId()); $this->_model->delete(Helper::getPasteId());
@ -112,10 +119,6 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist'); $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
} }
/**
* @expectedException Exception
* @expectedExceptionCode 90
*/
public function testCommentErrorDetection() public function testCommentErrorDetection()
{ {
$this->_model->delete(Helper::getPasteId()); $this->_model->delete(Helper::getPasteId());

View File

@ -4,14 +4,6 @@ use PrivateBin\Filter;
class FilterTest extends PHPUnit_Framework_TestCase class FilterTest extends PHPUnit_Framework_TestCase
{ {
public function testFilterStripsSlashesDeeply()
{
$this->assertEquals(
array("f'oo", "b'ar", array("fo'o", "b'ar")),
Filter::stripslashesDeep(array("f\\'oo", "b\\'ar", array("fo\\'o", "b\\'ar")))
);
}
public function testFilterMakesTimesHumanlyReadable() public function testFilterMakesTimesHumanlyReadable()
{ {
$this->assertEquals('5 minutes', Filter::formatHumanReadableTime('5min')); $this->assertEquals('5 minutes', Filter::formatHumanReadableTime('5min'));

View File

@ -142,4 +142,36 @@ class I18nTest extends PHPUnit_Framework_TestCase
I18n::loadTranslations(); I18n::loadTranslations();
$this->assertEquals('some string + 1', I18n::_('some %s + %d', 'string', 1), 'browser language en'); $this->assertEquals('some string + 1', I18n::_('some %s + %d', 'string', 1), 'browser language en');
} }
public function testMessageIdsExistInAllLanguages()
{
$messageIds = array();
$languages = array();
$dir = dir(PATH . 'i18n');
while (false !== ($file = $dir->read())) {
if (strlen($file) === 7) {
$language = substr($file, 0, 2);
$languageMessageIds = array_keys(
json_decode(
file_get_contents(PATH . 'i18n' . DIRECTORY_SEPARATOR . $file),
true
)
);
$messageIds = array_unique(array_merge($messageIds, $languageMessageIds));
$languages[$language] = $languageMessageIds;
}
}
foreach ($messageIds as $messageId) {
foreach (array_keys($languages) as $language) {
// most languages don't translate the data size units, ignore those
if ($messageId !== 'B' && strlen($messageId) !== 3 && strpos($messageId, 'B', 2) !== 2) {
$this->assertContains(
$messageId,
$languages[$language],
"message ID '$messageId' exists in translation file $language.json"
);
}
}
}
}
} }

View File

@ -98,6 +98,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
new PrivateBin; new PrivateBin;
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
unlink($file);
$response = json_decode($content, true); $response = json_decode($content, true);
$this->assertEquals(0, $response['status'], 'outputs status'); $this->assertEquals(0, $response['status'], 'outputs status');
$this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input'); $this->assertEquals(Helper::getPasteId(), $response['id'], 'outputted paste ID matches input');
@ -132,6 +133,7 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
new PrivateBin; new PrivateBin;
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
unlink($file);
$response = json_decode($content, true); $response = json_decode($content, true);
$this->assertEquals(0, $response['status'], 'outputs status'); $this->assertEquals(0, $response['status'], 'outputs status');
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted'); $this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
@ -147,10 +149,9 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data'); $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists before deleting data');
$paste = $this->_model->read(Helper::getPasteId()); $paste = $this->_model->read(Helper::getPasteId());
$_POST = array( $_POST = array(
'action' => 'delete', 'pasteid' => Helper::getPasteId(),
'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt), 'deletetoken' => hash_hmac('sha256', Helper::getPasteId(), $paste->meta->salt),
); );
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST'; $_SERVER['REQUEST_METHOD'] = 'POST';
ob_start(); ob_start();

View File

@ -82,6 +82,7 @@ class ModelTest extends PHPUnit_Framework_TestCase
$comment = $paste->getComment(Helper::getPasteId()); $comment = $paste->getComment(Helper::getPasteId());
$comment->setData($commentData['data']); $comment->setData($commentData['data']);
$comment->setNickname($commentData['meta']['nickname']); $comment->setNickname($commentData['meta']['nickname']);
$comment->getParentId();
$comment->store(); $comment->store();
$comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId()); $comment = $paste->getComment(Helper::getPasteId(), Helper::getCommentId());
@ -189,6 +190,27 @@ class ModelTest extends PHPUnit_Framework_TestCase
$this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack'); $this->assertFalse(Paste::isValidId('../bar/baz'), 'path attack');
} }
/**
* @expectedException Exception
* @expectedExceptionCode 64
*/
public function testInvalidPaste()
{
$this->_model->getPaste(Helper::getPasteId())->delete();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->get();
}
/**
* @expectedException Exception
* @expectedExceptionCode 61
*/
public function testInvalidData()
{
$paste = $this->_model->getPaste();
$paste->setData('');
}
/** /**
* @expectedException Exception * @expectedException Exception
* @expectedExceptionCode 62 * @expectedExceptionCode 62
@ -199,6 +221,37 @@ class ModelTest extends PHPUnit_Framework_TestCase
$paste->getComment(Helper::getPasteId()); $paste->getComment(Helper::getPasteId());
} }
/**
* @expectedException Exception
* @expectedExceptionCode 67
*/
public function testInvalidCommentDeletedPaste()
{
$pasteData = Helper::getPaste();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->setData($pasteData['data']);
$paste->store();
$comment = $paste->getComment(Helper::getPasteId());
$paste->delete();
$comment->store();
}
/**
* @expectedException Exception
* @expectedExceptionCode 68
*/
public function testInvalidCommentData()
{
$pasteData = Helper::getPaste();
$paste = $this->_model->getPaste(Helper::getPasteId());
$paste->setData($pasteData['data']);
$paste->store();
$comment = $paste->getComment(Helper::getPasteId());
$comment->store();
}
public function testExpiration() public function testExpiration()
{ {
$pasteData = Helper::getPaste(); $pasteData = Helper::getPaste();

View File

@ -140,21 +140,18 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
public function testHtaccess() public function testHtaccess()
{ {
$this->reset(); $this->reset();
$dirs = array('cfg', 'lib'); $file = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
foreach ($dirs as $dir) { @unlink($file);
$file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($file); $_POST = Helper::getPaste();
} $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
ob_start(); ob_start();
new PrivateBin; new PrivateBin;
ob_end_clean(); ob_end_clean();
foreach ($dirs as $dir) {
$file = PATH . $dir . DIRECTORY_SEPARATOR . '.htaccess'; $this->assertFileExists($file, 'htaccess recreated');
$this->assertFileExists(
$file,
"$dir htaccess recreated"
);
}
} }
/** /**
@ -739,10 +736,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
new PrivateBin; new PrivateBin;
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertContains( $this->assertRegExp(
'<div id="cipherdata" class="hidden">' . '#<div id="cipherdata"[^>]*>' .
htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES) . preg_quote(htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES)) .
'</div>', '</div>#',
$content, $content,
'outputs data correctly' 'outputs data correctly'
); );
@ -760,7 +757,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
$content, $content,
'outputs error correctly' 'outputs error correctly'
); );
@ -778,7 +775,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content, $content,
'outputs error correctly' 'outputs error correctly'
); );
@ -798,7 +795,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content, $content,
'outputs error correctly' 'outputs error correctly'
); );
@ -818,10 +815,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
unset($burnPaste['meta']['salt']); unset($burnPaste['meta']['salt']);
$this->assertContains( $this->assertRegExp(
'<div id="cipherdata" class="hidden">' . '#<div id="cipherdata"[^>]*>' .
htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES) . preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
'</div>', '</div>#',
$content, $content,
'outputs data correctly' 'outputs data correctly'
); );
@ -889,10 +886,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$meta['formatter'] = 'syntaxhighlighting'; $meta['formatter'] = 'syntaxhighlighting';
$this->assertContains( $this->assertRegExp(
'<div id="cipherdata" class="hidden">' . '#<div id="cipherdata"[^>]*>' .
htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES) . preg_quote(htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES)) .
'</div>', '</div>#',
$content, $content,
'outputs data correctly' 'outputs data correctly'
); );
@ -914,10 +911,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
ob_end_clean(); ob_end_clean();
$oldPaste['meta']['formatter'] = 'plaintext'; $oldPaste['meta']['formatter'] = 'plaintext';
unset($oldPaste['meta']['salt']); unset($oldPaste['meta']['salt']);
$this->assertContains( $this->assertRegExp(
'<div id="cipherdata" class="hidden">' . '#<div id="cipherdata"[^>]*>' .
htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES) . preg_quote(htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)) .
'</div>', '</div>#',
$content, $content,
'outputs data correctly' 'outputs data correctly'
); );
@ -939,7 +936,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s', '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
$content, $content,
'outputs deleted status correctly' 'outputs deleted status correctly'
); );
@ -960,7 +957,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
$content, $content,
'outputs delete error correctly' 'outputs delete error correctly'
); );
@ -980,7 +977,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content, $content,
'outputs delete error correctly' 'outputs delete error correctly'
); );
@ -1000,7 +997,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token[^<]*</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
$content, $content,
'outputs delete error correctly' 'outputs delete error correctly'
); );
@ -1047,7 +1044,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
ob_end_clean(); ob_end_clean();
$response = json_decode($content, true); $response = json_decode($content, true);
$this->assertEquals(1, $response['status'], 'outputs status'); $this->assertEquals(1, $response['status'], 'outputs status');
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted'); $this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after failing to delete data');
} }
/** /**
@ -1067,7 +1064,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist[^<]*</div>#', '#<div[^>]*id="errormessage"[^>]*>.*Paste does not exist, has expired or has been deleted\.#s',
$content, $content,
'outputs error correctly' 'outputs error correctly'
); );
@ -1091,7 +1088,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
$content = ob_get_contents(); $content = ob_get_contents();
ob_end_clean(); ob_end_clean();
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s', '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
$content, $content,
'outputs deleted status correctly' 'outputs deleted status correctly'
); );

View File

@ -63,6 +63,7 @@ class RequestTest extends PHPUnit_Framework_TestCase
file_put_contents($file, 'data=foo'); file_put_contents($file, 'data=foo');
Request::setInputStream($file); Request::setInputStream($file);
$request = new Request; $request = new Request;
unlink($file);
$this->assertTrue($request->isJsonApiCall(), 'is JSON Api call'); $this->assertTrue($request->isJsonApiCall(), 'is JSON Api call');
$this->assertEquals('create', $request->getOperation()); $this->assertEquals('create', $request->getOperation());
$this->assertEquals('foo', $request->getParam('data')); $this->assertEquals('foo', $request->getParam('data'));

View File

@ -1,11 +1,13 @@
<?php <?php
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Sjcl; use PrivateBin\Sjcl;
class SjclTest extends PHPUnit_Framework_TestCase class SjclTest extends PHPUnit_Framework_TestCase
{ {
public function testSjclValidatorValidatesCorrectly() public function testSjclValidatorValidatesCorrectly()
{ {
ServerSalt::setPath(sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data');
$paste = Helper::getPasteWithAttachment(); $paste = Helper::getPasteWithAttachment();
$this->assertTrue(Sjcl::isValid($paste['data']), 'valid sjcl'); $this->assertTrue(Sjcl::isValid($paste['data']), 'valid sjcl');
$this->assertTrue(Sjcl::isValid($paste['attachment']), 'valid sjcl'); $this->assertTrue(Sjcl::isValid($paste['attachment']), 'valid sjcl');

View File

@ -96,15 +96,15 @@ class ViewTest extends PHPUnit_Framework_TestCase
public function testTemplateRendersCorrectly() public function testTemplateRendersCorrectly()
{ {
foreach ($this->_content as $template => $content) { foreach ($this->_content as $template => $content) {
$this->assertContains( $this->assertRegExp(
'<div id="cipherdata" class="hidden">' . '#<div[^>]+id="cipherdata"[^>]*>' .
htmlspecialchars(Helper::getPaste()['data'], ENT_NOQUOTES) . preg_quote(htmlspecialchars(Helper::getPaste()['data'], ENT_NOQUOTES)) .
'</div>', '</div>#',
$content, $content,
$template . ': outputs data correctly' $template . ': outputs data correctly'
); );
$this->assertRegExp( $this->assertRegExp(
'#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '</div>#', '#<div[^>]+id="errormessage"[^>]*>.*' . self::$error . '#s',
$content, $content,
$template . ': outputs error correctly' $template . ': outputs error correctly'
); );
@ -119,7 +119,7 @@ class ViewTest extends PHPUnit_Framework_TestCase
$template . ': checked discussion if configured' $template . ': checked discussion if configured'
); );
$this->assertRegExp( $this->assertRegExp(
'#<[^>]+id="opendisc"[^>]*>#', '#<[^>]+id="opendiscussionoption"[^>]*>#',
$content, $content,
$template . ': discussions available if configured' $template . ': discussions available if configured'
); );

1
vendor/.htaccess vendored Normal file
View File

@ -0,0 +1 @@
Require all denied