rugk 9 лет назад
Родитель
Сommit
eebac929c5
34 измененных файлов с 1562 добавлено и 1327 удалено
  1. 13 5
      .eslintrc
  2. 1 0
      .gitignore
  3. 1 1
      .travis.yml
  4. 3 1
      CHANGELOG.md
  5. 1 0
      CREDITS.md
  6. 0 1
      cfg/.gitignore
  7. 1 2
      cfg/.htaccess
  8. 2 1
      composer.json
  9. 10 0
      css/bootstrap/privatebin.css
  10. 22 17
      i18n/de.json
  11. 8 8
      i18n/es.json
  12. 12 12
      i18n/fr.json
  13. 8 8
      i18n/it.json
  14. 8 8
      i18n/no.json
  15. 8 8
      i18n/oc.json
  16. 8 8
      i18n/pl.json
  17. 151 0
      i18n/pt.json
  18. 9 9
      i18n/ru.json
  19. 7 7
      i18n/sl.json
  20. 15 15
      i18n/zh.json
  21. 1053 1011
      js/privatebin.js
  22. 56 40
      js/test.js
  23. 1 2
      lib/.htaccess
  24. 0 15
      lib/Filter.php
  25. 1 1
      lib/I18n.php
  26. 2 2
      lib/PrivateBin.php
  27. 0 7
      lib/Request.php
  28. 1 0
      tpl/.htaccess
  29. 107 92
      tpl/bootstrap.php
  30. 21 7
      tpl/page.php
  31. 0 8
      tst/FilterTest.php
  32. 25 25
      tst/PrivateBinTest.php
  33. 6 6
      tst/ViewTest.php
  34. 1 0
      vendor/.htaccess

+ 13 - 5
.eslintrc

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

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 # Ignore server files for safety
 .htaccess
 .htpasswd
+cfg/conf.ini
 
 # Ignore data/
 data/

+ 1 - 1
.travis.yml

@@ -9,7 +9,7 @@ before_script:
   - composer install -n
 
 script:
-  - cd tst && phpunit
+  - cd tst && ../vendor/bin/phpunit
 
 after_script:
   - cd ..

+ 3 - 1
CHANGELOG.md

@@ -1,8 +1,10 @@
 # PrivateBin version history
 
   * **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
+    * 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
   * **1.1 (2016-12-26)**
     * ADDED: Translations for Italian and Russian

+ 1 - 0
CREDITS.md

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

+ 0 - 1
cfg/.gitignore

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

+ 1 - 2
cfg/.htaccess

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

+ 2 - 1
composer.json

@@ -24,7 +24,8 @@
     },
     "require-dev": {
         "codacy/coverage": "dev-master",
-        "codeclimate/php-test-reporter": "dev-master"
+        "codeclimate/php-test-reporter": "dev-master",
+        "phpunit/phpunit": "^4.6 || ^5.0"
     },
     "autoload": {
         "psr-4": {

+ 10 - 0
css/bootstrap/privatebin.css

@@ -17,6 +17,10 @@ body.navbar-spacing {
 	padding-top: 70px;
 }
 
+body.loading {
+	cursor: wait;
+}
+
 .buttondisabled {
 	opacity: 0.3;
 }
@@ -102,6 +106,12 @@ body.navbar-spacing {
 	border-left: 1px solid #ccc;
 	padding: 5px 0 5px 10px;
 	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 {

+ 22 - 17
i18n/de.json

@@ -83,25 +83,25 @@
     "Could not decrypt data (Wrong key?)":
         "Konnte Daten nicht entschlüsseln (Falscher Schlüssel?)",
     "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.":
-        "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?":
         "Konnte Kommentar nicht entschlüsseln; Falscher Schlüssel?",
     "Reply":
         "Antworten",
     "Anonymous":
         "Anonym",
-    "Anonymous avatar (Vizhash of the IP address)":
-        "Anonymer Avatar (Vizhash der IP-Addresse)",
+    "Avatar generated from IP address":
+        "Avatar (generiert aus der IP-Adresse)",
     "Add comment":
         "Kommentar hinzufügen",
-    "Optional nickname...":
-        "Optionales Pseudonym...",
+    "Optional nickname":
+        "Optionales Pseudonym",
     "Post comment":
         "Kommentar absenden",
-    "Sending comment...":
-        "Sende Kommentar...",
+    "Sending comment":
+        "Sende Kommentar",
     "Comment posted.":
         "Kommentar gesendet.",
     "Could not refresh display: %s":
@@ -112,24 +112,25 @@
         "Fehler auf dem Server oder keine Antwort vom Server",
     "Could not post comment: %s":
         "Konnte Kommentar nicht senden: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Sende Text (Bitte bewege Deine Maus um die Entropie zu erhöhen)...",
-    "Sending paste...":
-        "Sende Text...",
+    "Please move your mouse for more entropy…":
+        "Bitte bewege Deine Maus um die Entropie zu erhöhen",
+    "Sending paste":
+        "Sende Paste…",
     "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":
         "Lösche Daten",
     "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?)":
-        "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",
     "Plain Text": "Nur Text",
     "Source Code": "Quellcode",
     "Markdown": "Markdown",
     "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",
     "Remove attachment": "Anhang entfernen",
     "Your browser does not support uploading encrypted files. Please use a newer browser.":
@@ -146,6 +147,10 @@
     "Enter password":
         "Passwort eingeben",
     "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>.":
-        "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 +++"
 }

+ 8 - 8
i18n/es.json

@@ -96,12 +96,12 @@
         "Avatar anónimo (Vizhash de la dirección IP)",
     "Add comment":
         "Añadir comentario",
-    "Optional nickname...":
-        "Seudónimo opcional...",
+    "Optional nickname":
+        "Seudónimo opcional",
     "Post comment":
         "Publicar comentario",
-    "Sending comment...":
-        "Enviando comentario...",
+    "Sending comment":
+        "Enviando comentario",
     "Comment posted.":
         "Comentario publicado.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "Error del servidor o el servidor no responde",
     "Could not post comment: %s":
         "No fue posible publicar comentario: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Enviando texto (Por favor, mueva el ratón para mayor entropía)...",
-    "Sending paste...":
-        "Enviando texto...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Enviando texto (Por favor, mueva el ratón para mayor entropía)",
+    "Sending paste":
+        "Enviando texto",
     "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>",
     "Delete data":

+ 12 - 12
i18n/fr.json

@@ -96,12 +96,12 @@
         "Avatar anonyme (Vizhash de l'adresse IP)",
     "Add comment":
         "Ajouter un commentaire",
-    "Optional nickname...":
-        "Pseudonyme optionnel...",
+    "Optional nickname":
+        "Pseudonyme optionnel",
     "Post comment":
         "Poster le commentaire",
-    "Sending comment...":
-        "Envoi du commentaire...",
+    "Sending comment":
+        "Envoi du commentaire",
     "Comment posted.":
         "Commentaire posté.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "Le serveur ne répond pas ou a rencontré une erreur",
     "Could not post comment: %s":
         "Impossible de poster le commentaire : %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Envoi du paste (Merci de bouger votre souris pour plus d'entropie)...",
-    "Sending paste...":
-        "Envoi du paste...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Envoi du paste (Merci de bouger votre souris pour plus d'entropie)",
+    "Sending paste":
+        "Envoi du paste",
     "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>",
     "Delete data":
@@ -149,12 +149,12 @@
     "Editor": "Éditer",
     "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 requiert que le PATH se termine dans un \"%s\". Veuillez mettre à jour le PATH dans votre index.php.",
     "Decrypt":
-        "Decrypt",
+        "Déchiffrer",
     "Enter password":
         "Entrez le mot de passe",
-    "Loading…": "Loading…",
+    "Loading…": "Chargement…",
     "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)."
 }

+ 8 - 8
i18n/it.json

@@ -96,12 +96,12 @@
         "Avatar Anonino (Vizhash dell'indirizzo IP)",
     "Add comment":
         "Aggiungi un commento",
-    "Optional nickname...":
-        "Nickname opzionale...",
+    "Optional nickname":
+        "Nickname opzionale",
     "Post comment":
         "Invia commento",
-    "Sending comment...":
-        "Commento in fase di invio...",
+    "Sending comment":
+        "Commento in fase di invio",
     "Comment posted.":
         "Commento inviato.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "errore o mancata risposta dal server",
     "Could not post comment: %s":
         "Impossibile inviare il commento: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)...",
-    "Sending paste...":
-        "Messaggio in fase di invio...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Invio messaggio (Muovi il mouse in modo casuale, per generare maggior entropia)",
+    "Sending paste":
+        "Messaggio in fase di invio",
     "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>",
     "Delete data":

+ 8 - 8
i18n/no.json

@@ -96,12 +96,12 @@
         "Anonym avatar (Vizhash av IP adressen)",
     "Add comment":
         "Legg til kommentar",
-    "Optional nickname...":
-        "Valgfritt kallenavn...",
+    "Optional nickname":
+        "Valgfritt kallenavn",
     "Post comment":
         "Send kommentar",
-    "Sending comment...":
-        "Sender Kommentar...",
+    "Sending comment":
+        "Sender Kommentar",
     "Comment posted.":
         "Kommentar sendt.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "server feilet eller svarer ikke",
     "Could not post comment: %s":
         "Kunne ikke sende kommentar: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Sender innlegg (Flytt musen for mere entropi)...",
-    "Sending paste...":
-        "Sender innlegg...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Sender innlegg (Flytt musen for mere entropi)",
+    "Sending paste":
+        "Sender innlegg",
     "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>",
     "Delete data":

+ 8 - 8
i18n/oc.json

@@ -96,12 +96,12 @@
         "Avatar anonime (Vizhash de l'adreça IP)",
     "Add comment":
         "Apondre un comentari",
-    "Optional nickname...":
-        "Escais opcional...",
+    "Optional nickname":
+        "Escais opcional",
     "Post comment":
         "Mandar lo comentari",
-    "Sending comment...":
-        "Mandadís del comentari...",
+    "Sending comment":
+        "Mandadís del comentari",
     "Comment posted.":
         "Comentari mandat.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "Lo servidor respond pas o a rencontrat una error",
     "Could not post comment: %s":
         "Impossible de mandar lo comentari : %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)...",
-    "Sending paste...":
-        "Mandadís del tèxte...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Mandadís del tèxte (Mercés de bolegar vòstra mirga per mai entropia)",
+    "Sending paste":
+        "Mandadís del tèxte",
     "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>",
     "Delete data":

+ 8 - 8
i18n/pl.json

@@ -96,12 +96,12 @@
         "Anonimowy avatar (Vizhash z adresu IP)",
     "Add comment":
         "Dodaj komentarz",
-    "Optional nickname...":
-        "Opcjonalny nick...",
+    "Optional nickname":
+        "Opcjonalny nick",
     "Post comment":
         "Wyślij komentarz",
-    "Sending comment...":
-        "Wysyłanie komentarza...",
+    "Sending comment":
+        "Wysyłanie komentarza",
     "Comment posted.":
         "Wysłano komentarz.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "bląd serwera lub brak odpowiedzi",
     "Could not post comment: %s":
         "Nie udało się wysłać komentarza: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)...",
-    "Sending paste...":
-        "Wysyłanie wklejki...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Wysyłanie wklejki (proszę poruszać myszą aby uzyskać większą entropię)",
+    "Sending paste":
+        "Wysyłanie wklejki",
     "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>",
     "Delete data":

+ 151 - 0
i18n/pt.json

@@ -0,0 +1,151 @@
+{
+    "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 5.3.0 or above to work. Sorry.":
+        "%s requer php 5.3.0 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 (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'",
+    "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…",
+    "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>."
+}

+ 9 - 9
i18n/ru.json

@@ -96,12 +96,12 @@
         "Анонимный аватар (Vizhash IP адреса)",
     "Add comment":
         "Добавить комментарий",
-    "Optional nickname...":
-        "Опциональный никнейм...",
+    "Optional nickname":
+        "Опциональный никнейм",
     "Post comment":
         "Отправить комментарий",
-    "Sending comment...":
-        "Отправка комментария...",
+    "Sending comment":
+        "Отправка комментария",
     "Comment posted.":
         "Комментарий опубликован.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "ошибка сервера или нет ответа",
     "Could not post comment: %s":
         "Не удалось опубликовать комментарий: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Отправка записи (Пожалуйста двигайте мышкой для большей энтропии)...",
-    "Sending paste...":
-        "Отправка записи...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Отправка записи (Пожалуйста двигайте мышкой для большей энтропии)",
+    "Sending paste":
+        "Отправка записи",
     "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>",
     "Delete data":
@@ -155,5 +155,5 @@
     "Enter password":
         "Введите пароль",
     "Uploading paste… Please wait.":
-        "Отправка записи... Пожалуйста подождите."
+        "Отправка записи Пожалуйста подождите."
 }

+ 7 - 7
i18n/sl.json

@@ -96,12 +96,12 @@
         "Anonimen avatar (Vizhash IP naslova)",
     "Add comment":
         "Dodaj komentar",
-    "Optional nickname...":
+    "Optional nickname":
         "Uporabniško ime (lahko izpustiš)",
     "Post comment":
         "Objavi komentar",
-    "Sending comment...":
-        "Pošiljam komentar ...",
+    "Sending comment":
+        "Pošiljam komentar ",
     "Comment posted.":
         "Komentar poslan.",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "napaka na strežniku, ali pa se strežnik ne odziva",
     "Could not post comment: %s":
         "Komentarja ni bilo mogoče objaviti : %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "Pošiljam prilepek (prosim premakni svojo miško za več entropije) ...",
-    "Sending paste...":
-        "Pošiljam prilepek...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "Pošiljam prilepek (prosim premakni svojo miško za več entropije) ",
+    "Sending paste":
+        "Pošiljam prilepek",
     "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>",
     "Delete data":

+ 15 - 15
i18n/zh.json

@@ -96,12 +96,12 @@
         "匿名头像 (由IP地址生成Vizhash)",
     "Add comment":
         "添加评论",
-    "Optional nickname...":
-        "可选昵称...",
+    "Optional nickname":
+        "可选昵称",
     "Post comment":
         "评论",
-    "Sending comment...":
-        "评论发送中...",
+    "Sending comment":
+        "评论发送中",
     "Comment posted.":
         "评论已发送。",
     "Could not refresh display: %s":
@@ -112,10 +112,10 @@
         "服务器错误或无回应",
     "Could not post comment: %s":
         "无法发送评论: %s",
-    "Sending paste (Please move your mouse for more entropy)...":
-        "粘贴提交中 (请移动鼠标以产生更多熵)...",
-    "Sending paste...":
-        "粘贴提交中...",
+    "Sending paste (Please move your mouse for more entropy)":
+        "粘贴提交中 (请移动鼠标以产生更多熵)",
+    "Sending paste":
+        "粘贴提交中",
     "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>",
     "Delete data":
@@ -137,15 +137,15 @@
     "Invalid attachment.": "无效的附件",
     "Options": "选项",
     "Shorten URL": "缩短链接",
-    "Editor": "編輯",
-    "Preview": "預習",
+    "Editor": "编辑",
+    "Preview": "预览",
     "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.":
-        "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.",
+        "%s 的 PATH 变量必须结束于 \"%s\"。 请修改你的 index.php 中的 PATH 变量。",
     "Decrypt":
-        "Decrypt",
+        "解密",
     "Enter password":
-        "Enter password",
-    "Loading…": "Loading…",
+        "输入密码",
+    "Loading…": "载入中…",
     "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> (英文版)。"
 }

Разница между файлами не показана из-за своего большого размера
+ 1053 - 1011
js/privatebin.js


+ 56 - 40
js/test.js

@@ -11,7 +11,9 @@ var jsc = require('jsverify'),
         a2zString.map(function(c) {
             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.sjcl = require('./sjcl-1.0.6');
@@ -20,75 +22,101 @@ global.RawDeflate = require('./rawdeflate-0.5');
 require('./rawinflate-0.3');
 require('./privatebin');
 
-describe('helper', function () {
+describe('Helper', function () {
     describe('secondsToHuman', function () {
         after(function () {
             cleanup();
         });
 
         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) &&
                 result.length === 2 &&
                 result[0] === parseInt(result[0], 10) &&
                 typeof result[1] === 'string';
         });
         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) {
-            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) {
-            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) {
-            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) {
-            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) {
-            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) {
-            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) {
-            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
         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) {
-            return $.PrivateBin.helper.secondsToHuman(number)[1] === 'month';
+            return $.PrivateBin.Helper.secondsToHuman(number)[1] === 'month';
         });
     });
 
-    describe('scriptLocation', function () {
+    describe('baseUri', function () {
+        before(function () {
+            $.PrivateBin.Helper.reset();
+        });
+
         jsc.property(
             'returns the URL without query & fragment',
-            jsc.nearray(jsc.elements(a2zString)),
+            jsc.elements(schemas),
             jsc.nearray(jsc.elements(a2zString)),
             jsc.array(jsc.elements(queryString)),
             'string',
             function (schema, address, query, fragment) {
-                var expected = schema.join('') + '://' + address.join('') + '/',
+                var expected = schema + '://' + address.join('') + '/',
                     clean = jsdom('', {url: expected + '?' + query.join('') + '#' + fragment}),
-                    result = $.PrivateBin.helper.scriptLocation();
+                    result = $.PrivateBin.Helper.baseUri();
+                $.PrivateBin.Helper.reset();
                 clean();
                 return expected === result;
             }
         );
     });
 
-    describe('pasteId', function () {
+    describe('htmlEntities', function () {
+        after(function () {
+            cleanup();
+        });
+
+        jsc.property(
+            'removes all HTML entities from any given string',
+            'string',
+            function (string) {
+                var result = $.PrivateBin.Helper.htmlEntities(string);
+                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.array(jsc.elements(queryString)),
+            jsc.nearray(jsc.elements(queryString)),
             'string',
             function (schema, address, query, fragment) {
                 var queryString = query.join(''),
@@ -96,27 +124,29 @@ describe('helper', function () {
                         url: schema.join('') + '://' + address.join('') +
                              '/?' + queryString + '#' + fragment
                     }),
-                    result = $.PrivateBin.helper.pasteId();
+                    result = $.PrivateBin.Model.getPasteId();
+                $.PrivateBin.Model.reset();
                 clean();
                 return queryString === result;
             }
         );
     });
 
-    describe('pageKey', function () {
+    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.array(jsc.elements(base64String)),
+            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.helper.pageKey();
+                    result = $.PrivateBin.Model.getPasteKey();
+                $.PrivateBin.Model.reset();
                 clean();
                 return fragmentString === result;
             }
@@ -126,7 +156,7 @@ describe('helper', function () {
             jsc.nearray(jsc.elements(a2zString)),
             jsc.nearray(jsc.elements(a2zString)),
             jsc.array(jsc.elements(queryString)),
-            jsc.array(jsc.elements(base64String)),
+            jsc.nearray(jsc.elements(base64String)),
             jsc.array(jsc.elements(queryString)),
             function (schema, address, query, fragment, trail) {
                 var fragmentString = fragment.join(''),
@@ -134,25 +164,11 @@ describe('helper', function () {
                         url: schema.join('') + '://' + address.join('') + '/?' +
                              query.join('') + '#' + fragmentString + '&' + trail.join('')
                     }),
-                    result = $.PrivateBin.helper.pageKey();
+                    result = $.PrivateBin.Model.getPasteKey();
+                $.PrivateBin.Model.reset();
                 clean();
                 return fragmentString === result;
             }
         );
     });
-
-    describe('htmlEntities', function () {
-        after(function () {
-            cleanup();
-        });
-
-        jsc.property(
-            'removes all HTML entities from any given string',
-            'string',
-            function (string) {
-                var result = $.PrivateBin.helper.htmlEntities(string);
-                return !(/[<>"'`=\/]/.test(result)) && !(string.indexOf('&') > -1 && !(/&amp;/.test(result)));
-            }
-        );
-    });
 });

+ 1 - 2
lib/.htaccess

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

+ 0 - 15
lib/Filter.php

@@ -21,21 +21,6 @@ use Exception;
  */
 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)
      *

+ 1 - 1
lib/I18n.php

@@ -304,7 +304,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);
             case 'sl':
                 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:
                 return $n != 1 ? 1 : 0;
         }

+ 2 - 2
lib/PrivateBin.php

@@ -120,8 +120,8 @@ class PrivateBin
      */
     public function __construct()
     {
-        if (version_compare(PHP_VERSION, '5.3.0') < 0) {
-            throw new Exception(I18n::_('%s requires php 5.3.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1);
+        if (version_compare(PHP_VERSION, '5.4.0') < 0) {
+            throw new Exception(I18n::_('%s requires php 5.4.0 or above to work. Sorry.', I18n::_('PrivateBin')), 1);
         }
         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);

+ 0 - 7
lib/Request.php

@@ -80,13 +80,6 @@ class Request
      */
     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
         $this->_isJsonApi = $this->_detectJsonRequest();
 

+ 1 - 0
tpl/.htaccess

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

+ 107 - 92
tpl/bootstrap.php

@@ -4,7 +4,7 @@ $isCpct = substr($template, 9, 8) === '-compact';
 $isDark = substr($template, 9, 5) === '-dark';
 $isPage = substr($template, -5) === '-page';
 ?><!DOCTYPE html>
-<html lang="en">
+<html>
 	<head>
 		<meta charset="utf-8" />
 		<meta http-equiv="X-UA-Compatible" content="IE=edge">
@@ -69,7 +69,7 @@ if ($MARKDOWN):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vYYJYraxQNOf41XtehLBU2JbIQ2Uffe+n8TjHyWkpqoZdZX4aL5zyABrUNvRUP02+AxoRsmNJkpvIbmeQqcIXg==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-7/bLmiTErcH3lMJi1JxXk/lb20NUO7TCqasWeFybnNhnfOUnJ9GQGqPVJxPf+7fnw9dgLa1s18FXQuRanLD8Hw==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![endif]-->
@@ -94,7 +94,7 @@ endif;
 						<form id="passwordform" role="form">
 							<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>
-								<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>
 							<button type="submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span> <?php echo I18n::_('Decrypt') ?></button>
 						</form>
@@ -121,8 +121,8 @@ endif;
 			<div id="navbar" class="navbar-collapse collapse">
 				<ul class="nav navbar-nav">
 					<li id="loadingindicator" class="navbar-text hidden">
-						<span class="glyphicon glyphicon-upload" aria-hidden="true"></span>
-						<?php echo I18n::_('Uploading paste… Please wait.'), PHP_EOL; ?>
+						<span class="glyphicon glyphicon-time" aria-hidden="true"></span>
+						<?php echo I18n::_('Loading…'), PHP_EOL; ?>
 					</li>
 					<li>
 <?php
@@ -132,7 +132,7 @@ if ($isPage):
 							<span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?php echo I18n::_('Send'), PHP_EOL;
 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;
 endif;
 ?>
@@ -198,7 +198,7 @@ if ($isCpct):
 <?php
     if ($DISCUSSION):
 ?>
-							<li id="opendisc" class="checkbox hidden">
+							<li id="opendiscussionoption" class="checkbox hidden">
 								<label>
 									<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
         if ($OPENDISCUSSION):
@@ -230,17 +230,6 @@ if ($isCpct):
 ?>
 						</ul>
 						<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>
 					</li>
 <?php
@@ -262,7 +251,7 @@ else:
     if ($DISCUSSION):
 ?>
 					<li>
-						<div id="opendisc" class="navbar-text checkbox hidden">
+						<div id="opendiscussionoption" class="navbar-text checkbox hidden">
 							<label>
 								<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
         if ($OPENDISCUSSION):
@@ -295,6 +284,7 @@ if ($FILEUPLOAD):
 									<input type="file" id="file" name="file" />
 								</div>
 							</li>
+							<li id="customattachment" class="hidden"></li>
 							<li>
 								<a id="fileremovebutton"  href="#">
 									<?php echo I18n::_('Remove attachment'), PHP_EOL; ?>
@@ -383,99 +373,124 @@ if ($isCpct):
 ?></div><?php
 endif;
 ?></nav>
-		<header class="container">
+		<main>
+			<section class="container">
 <?php
 if (strlen($NOTICE)):
 ?>
-			<div role="alert" class="alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> <?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
-			</div>
+				<div role="alert" class="alert alert-info">
+					<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
+					<?php echo htmlspecialchars($NOTICE), PHP_EOL; ?>
+				</div>
 <?php
 endif;
 ?>
-			<div id="remainingtime" role="alert" class="hidden alert alert-info">
-				<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
-			</div>
+				<div id="remainingtime" role="alert" class="hidden alert alert-info">
+					<span class="glyphicon glyphicon-fire" aria-hidden="true"></span>
+				</div>
 <?php
 if ($FILEUPLOAD):
 ?>
-			<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>
-			</div>
-<?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 id="attachment" role="alert" class="hidden alert alert-info">
+					<span class="glyphicon glyphicon-download-alt" aria-hidden="true"></span>
+					<a class="alert-link"><?php echo I18n::_('Download attachment'), PHP_EOL; ?></a>
+				</div>
 <?php
 endif;
 ?>
-			<div id="errormessage" role="alert" class="<?php
-if (!strlen($ERROR)):
-?>hidden <?php
-endif;
-?>alert alert-danger"><span class="glyphicon glyphicon-alert" aria-hidden="true"></span> <?php echo htmlspecialchars($ERROR); ?></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)); ?></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)); ?></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.opera.com/">Opera</a>,
-				<a href="https://www.google.com/chrome">Chrome</a>,
-				<a href="https://www.apple.com/safari">Safari</a>...
-			</div>
-			<div id="pasteresult" role="alert" class="hidden alert alert-success">
-				<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
-				<div id="deletelink"></div>
-				<div id="pastelink">
+				<div id="status" role="alert" class="statusmessage alert alert-info<?php echo empty($STATUS) ? ' hidden' : '' ?>">
+					<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span>
+					<?php echo htmlspecialchars($STATUS), PHP_EOL; ?>
+				</div>
+				<div id="errormessage" role="alert" class="statusmessage<?php echo empty($ERROR) ? ' hidden' : '' ?> alert alert-danger">
+					<span class="glyphicon glyphicon-alert" aria-hidden="true"></span>
+					<?php echo htmlspecialchars($ERROR), 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.opera.com/">Opera</a>,
+					<a href="https://www.google.com/chrome">Chrome</a>…
+				</div>
+				<div id="pasteSuccess" role="alert" class="hidden alert alert-success">
+					<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
+					<div id="deletelink"></div>
+					<div id="pastelink">
 <?php
 if (strlen($URLSHORTENER)):
 ?>
-					<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-<?php echo $isDark ? 'warning' : 'primary'; ?>">
-						<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
-					</button>
+						<button id="shortenbutton" data-shortener="<?php echo htmlspecialchars($URLSHORTENER); ?>" type="button" class="btn btn-<?php echo $isDark ? 'warning' : 'primary'; ?>">
+							<span class="glyphicon glyphicon-send" aria-hidden="true"></span> <?php echo I18n::_('Shorten URL'), PHP_EOL; ?>
+						</button>
 <?php
 endif;
 ?>
+					</div>
 				</div>
-			</div>
-			<ul id="preview" class="nav nav-tabs hidden">
-				<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>
-			</ul>
-		</header>
-		<section class="container">
-			<article class="row">
-				<div id="image" class="col-md-12 text-center hidden"></div>
-				<div id="prettymessage" class="col-md-12 hidden">
-					<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
+				<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"><a id="messagepreview" href="#"><?php echo I18n::_('Preview'); ?></a></li>
+				</ul>
+			</section>
+			<section class="container">
+				<article class="row">
+					<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">
+						<pre id="prettyprint" class="col-md-12 prettyprint linenums:1"></pre>
+					</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>
+				</article>
+			</section>
+			<section class="container">
+				<div id="discussion" class="hidden">
+					<h4><?php echo I18n::_('Discussion'); ?></h4>
+					<div id="commentcontainer"></div>
 				</div>
-				<div id="cleartext" 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>
-			</article>
-		</section>
-		<section class="container">
-			<div id="discussion" class="hidden">
-				<h4><?php echo I18n::_('Discussion'); ?></h4>
-				<div id="comments"></div>
-			</div>
-		</section>
-		<section class="container">
-			<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 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>
-		</section>
-		<footer class="container">
-			<div class="row">
-				<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
-				<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
-				<p id="aboutbox" class="col-md-6 col-xs-12">
-					<?php echo I18n::_('%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>.', I18n::_($NAME)), PHP_EOL; ?>
-				</p>
+			</section>
+			<section class="container">
+				<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…'); ?><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>
+				</div>
+			</section>
+			<footer class="container">
+				<div class="row">
+					<h4 class="col-md-5 col-xs-8"><?php echo I18n::_($NAME); ?> <small>- <?php echo I18n::_('Because ignorance is bliss'); ?></small></h4>
+					<p class="col-md-1 col-xs-4 text-center"><?php echo $VERSION; ?></p>
+					<p id="aboutbox" class="col-md-6 col-xs-12">
+						<?php echo I18n::_('%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>.', I18n::_($NAME)), PHP_EOL; ?>
+					</p>
+				</div>
+			</footer>
+		</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>
-		</footer>
-		<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
+<?php
+endif;
+?>
+		</div>
 	</body>
 </html>

+ 21 - 7
tpl/page.php

@@ -47,7 +47,7 @@ if ($MARKDOWN):
 <?php
 endif;
 ?>
-		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-vYYJYraxQNOf41XtehLBU2JbIQ2Uffe+n8TjHyWkpqoZdZX4aL5zyABrUNvRUP02+AxoRsmNJkpvIbmeQqcIXg==" crossorigin="anonymous"></script>
+		<script type="text/javascript" src="js/privatebin.js?<?php echo rawurlencode($VERSION); ?>" integrity="sha512-7/bLmiTErcH3lMJi1JxXk/lb20NUO7TCqasWeFybnNhnfOUnJ9GQGqPVJxPf+7fnw9dgLa1s18FXQuRanLD8Hw==" crossorigin="anonymous"></script>
 		<!--[if lt IE 10]>
 		<style type="text/css">body {padding-left:60px;padding-right:60px;} #ienotice {display:block;} #oldienotice {display:block;}</style>
 		<![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; ?>
 				<a href="https://www.mozilla.org/firefox/">Firefox</a>,
 				<a href="https://www.opera.com/">Opera</a>,
-				<a href="https://www.google.com/chrome">Chrome</a>,
-				<a href="https://www.apple.com/safari">Safari</a>...
+				<a href="https://www.google.com/chrome">Chrome</a>…
 			</div>
 		</header>
 		<section>
 			<article>
+				<div id="loadingindicator" class="hidden"><?php echo I18n::_('Loading…'); ?></div>
 				<div id="status"><?php echo htmlspecialchars($STATUS); ?></div>
 				<div id="errormessage" class="hidden"><?php echo htmlspecialchars($ERROR); ?></div>
 				<div id="toolbar">
@@ -125,7 +125,7 @@ endif;
 <?php
 if ($DISCUSSION):
 ?>
-					<div id="opendisc" class="button hidden">
+					<div id="opendiscussionoption" class="button hidden">
 						<input type="checkbox" id="opendiscussion" name="opendiscussion"<?php
     if ($OPENDISCUSSION):
 ?> checked="checked"<?php
@@ -216,17 +216,31 @@ endif;
 				<div id="prettymessage" class="hidden">
 					<pre id="prettyprint" class="prettyprint linenums:1"></pre>
 				</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>
 			</article>
 		</section>
 		<section>
 			<div id="discussion" class="hidden">
 				<h4 class="title"><?php echo I18n::_('Discussion'); ?></h4>
-				<div id="comments"></div>
+				<div id="commentcontainer"></div>
 			</div>
 		</section>
-		<div id="cipherdata" class="hidden"><?php echo htmlspecialchars($CIPHERDATA, ENT_NOQUOTES); ?></div>
+		<div id="serverdata" class="hidden" aria-hidden="true">
+			<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">
 			<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>

+ 0 - 8
tst/FilterTest.php

@@ -4,14 +4,6 @@ use PrivateBin\Filter;
 
 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()
     {
         $this->assertEquals('5 minutes', Filter::formatHumanReadableTime('5min'));

+ 25 - 25
tst/PrivateBinTest.php

@@ -739,10 +739,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         new PrivateBin;
         $content = ob_get_contents();
         ob_end_clean();
-        $this->assertContains(
-            '<div id="cipherdata" class="hidden">' .
-            htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES) .
-            '</div>',
+        $this->assertRegExp(
+            '#<div id="cipherdata"[^>]*>' .
+            preg_quote(htmlspecialchars(Helper::getPasteAsJson(), ENT_NOQUOTES)) .
+            '</div>#',
             $content,
             'outputs data correctly'
         );
@@ -760,7 +760,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $this->assertRegExp(
-            '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#',
+            '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
             $content,
             'outputs error correctly'
         );
@@ -778,7 +778,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $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,
             'outputs error correctly'
         );
@@ -798,7 +798,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $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,
             'outputs error correctly'
         );
@@ -818,10 +818,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         unset($burnPaste['meta']['salt']);
-        $this->assertContains(
-            '<div id="cipherdata" class="hidden">' .
-            htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES) .
-            '</div>',
+        $this->assertRegExp(
+            '#<div id="cipherdata"[^>]*>' .
+            preg_quote(htmlspecialchars(Helper::getPasteAsJson($burnPaste['meta']), ENT_NOQUOTES)) .
+            '</div>#',
             $content,
             'outputs data correctly'
         );
@@ -889,10 +889,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $meta['formatter'] = 'syntaxhighlighting';
-        $this->assertContains(
-            '<div id="cipherdata" class="hidden">' .
-            htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES) .
-            '</div>',
+        $this->assertRegExp(
+            '#<div id="cipherdata"[^>]*>' .
+            preg_quote(htmlspecialchars(Helper::getPasteAsJson($meta), ENT_NOQUOTES)) .
+            '</div>#',
             $content,
             'outputs data correctly'
         );
@@ -914,10 +914,10 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         ob_end_clean();
         $oldPaste['meta']['formatter'] = 'plaintext';
         unset($oldPaste['meta']['salt']);
-        $this->assertContains(
-            '<div id="cipherdata" class="hidden">' .
-            htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES) .
-            '</div>',
+        $this->assertRegExp(
+            '#<div id="cipherdata"[^>]*>' .
+            preg_quote(htmlspecialchars(Helper::getPasteAsJson($oldPaste['meta']), ENT_NOQUOTES)) .
+            '</div>#',
             $content,
             'outputs data correctly'
         );
@@ -939,7 +939,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $this->assertRegExp(
-            '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s',
+            '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
             $content,
             'outputs deleted status correctly'
         );
@@ -960,7 +960,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $this->assertRegExp(
-            '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.</div>#',
+            '#<div[^>]*id="errormessage"[^>]*>.*Invalid paste ID\.#s',
             $content,
             'outputs delete error correctly'
         );
@@ -980,7 +980,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $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,
             'outputs delete error correctly'
         );
@@ -1000,7 +1000,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $this->assertRegExp(
-            '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token[^<]*</div>#',
+            '#<div[^>]*id="errormessage"[^>]*>.*Wrong deletion token\. Paste was not deleted\.#s',
             $content,
             'outputs delete error correctly'
         );
@@ -1067,7 +1067,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $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,
             'outputs error correctly'
         );
@@ -1091,7 +1091,7 @@ class PrivateBinTest extends PHPUnit_Framework_TestCase
         $content = ob_get_contents();
         ob_end_clean();
         $this->assertRegExp(
-            '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted[^<]*</div>#s',
+            '#<div[^>]*id="status"[^>]*>.*Paste was properly deleted\.#s',
             $content,
             'outputs deleted status correctly'
         );

+ 6 - 6
tst/ViewTest.php

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

+ 1 - 0
vendor/.htaccess

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

Некоторые файлы не были показаны из-за большого количества измененных файлов