Просмотр исходного кода

Added the ability to web users to change the theme

Ribas160 1 год назад
Родитель
Сommit
c7a4ced9e1
45 измененных файлов с 367 добавлено и 43 удалено
  1. 2 1
      i18n/ar.json
  2. 2 1
      i18n/bg.json
  3. 2 1
      i18n/ca.json
  4. 2 1
      i18n/co.json
  5. 2 1
      i18n/cs.json
  6. 2 1
      i18n/de.json
  7. 2 1
      i18n/el.json
  8. 2 1
      i18n/en.json
  9. 2 1
      i18n/es.json
  10. 2 1
      i18n/et.json
  11. 2 1
      i18n/fi.json
  12. 2 1
      i18n/fr.json
  13. 2 1
      i18n/he.json
  14. 2 1
      i18n/hi.json
  15. 2 1
      i18n/hu.json
  16. 2 1
      i18n/id.json
  17. 2 1
      i18n/it.json
  18. 2 1
      i18n/ja.json
  19. 2 1
      i18n/jbo.json
  20. 2 1
      i18n/ko.json
  21. 2 1
      i18n/ku.json
  22. 2 1
      i18n/la.json
  23. 2 1
      i18n/lt.json
  24. 2 1
      i18n/nl.json
  25. 2 1
      i18n/no.json
  26. 2 1
      i18n/oc.json
  27. 2 1
      i18n/pl.json
  28. 2 1
      i18n/pt.json
  29. 2 1
      i18n/ro.json
  30. 2 1
      i18n/ru.json
  31. 2 1
      i18n/sk.json
  32. 2 1
      i18n/sl.json
  33. 2 1
      i18n/sv.json
  34. 2 1
      i18n/th.json
  35. 2 1
      i18n/tr.json
  36. 2 1
      i18n/uk.json
  37. 2 1
      i18n/zh.json
  38. 18 2
      js/privatebin.js
  39. 3 2
      lib/Configuration.php
  40. 32 2
      lib/Controller.php
  41. 117 0
      lib/TemplateSwitcher.php
  42. 22 0
      tpl/bootstrap.php
  43. 24 0
      tpl/bootstrap5.php
  44. 21 0
      tpl/page.php
  45. 56 0
      tst/TemplateSwitcherTest.php

+ 2 - 1
i18n/ar.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/bg.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ca.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/co.json

@@ -226,5 +226,6 @@
     "Copy link": "Cupià u liame",
     "Link copied to clipboard": "U liame hè statu cupiatu in u preme’papei",
     "Paste text": "Testu di l’appiccicu",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cum’è un caratteru (Appughjate nant’à <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "U tastu di tabulazione ghjova cum’è un caratteru (Appughjate nant’à <kbd>Ctrl</kbd>+<kbd>m</kbd> o <kbd>Scapp</kbd> per scambià)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/cs.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/de.json

@@ -226,5 +226,6 @@
     "Copy link": "Verknüpfung kopieren",
     "Link copied to clipboard": "Verknüpfung wurde in die Zwischenablage kopiert.",
     "Paste text": "Text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortaste als Zeichen interpretieren (Umschalten durch <kbd>Strg</kbd>+<kbd>m</kbd> oder <kbd>Esc</kbd>)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/el.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/en.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/es.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/et.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/fi.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/fr.json

@@ -226,5 +226,6 @@
     "Copy link": "Copier le lien",
     "Link copied to clipboard": "Lien copié dans le presse-papier",
     "Paste text": "Texte du paste",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "La touche de tabulation sert de caractère (Presser <kbd>Ctrl</kbd>+<kbd>m</kbd> ou <kbd>Esc</kbd> pour basculer)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "La touche de tabulation sert de caractère (Presser <kbd>Ctrl</kbd>+<kbd>m</kbd> ou <kbd>Esc</kbd> pour basculer)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/he.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/hi.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/hu.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/id.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/it.json

@@ -226,5 +226,6 @@
     "Copy link": "Copia il link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Testo del messaggio",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ja.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/jbo.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ko.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ku.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/la.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/lt.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/nl.json

@@ -226,5 +226,6 @@
     "Copy link": "Kopieer link",
     "Link copied to clipboard": "Link gekopieerd naar klembord",
     "Paste text": "Tekst plakken",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortoets dient als teken (gebruik <kbd>Ctrl</kbd>+<kbd>m</kbd> of <kbd>Esc</kbd> om te schakelen)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulatortoets dient als teken (gebruik <kbd>Ctrl</kbd>+<kbd>m</kbd> of <kbd>Esc</kbd> om te schakelen)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/no.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/oc.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/pl.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/pt.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ro.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/ru.json

@@ -226,5 +226,6 @@
     "Copy link": "Скопировать ссылку",
     "Link copied to clipboard": "Ссылка скопирована в буфер обмена",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Тема"
 }

+ 2 - 1
i18n/sk.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/sl.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/sv.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/th.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/tr.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/uk.json

@@ -226,5 +226,6 @@
     "Copy link": "Copy link",
     "Link copied to clipboard": "Link copied to clipboard",
     "Paste text": "Paste text",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)",
+    "Theme": "Theme"
 }

+ 2 - 1
i18n/zh.json

@@ -226,5 +226,6 @@
     "Copy link": "复制链接",
     "Link copied to clipboard": "链接已复制到剪贴板",
     "Paste text": "粘贴文本",
-    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tab 键可作为字符(按 <kbd>Ctrl</kbd>+<kbd>m</kbd> 或 <kbd>Esc</kbd> 切换开关)"
+    "Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "Tab 键可作为字符(按 <kbd>Ctrl</kbd>+<kbd>m</kbd> 或 <kbd>Esc</kbd> 切换开关)",
+    "Theme": "Theme"
 }

+ 18 - 2
js/privatebin.js

@@ -3933,7 +3933,18 @@ jQuery.PrivateBin = (function($, RawDeflate) {
          */
         function setLanguage(event)
         {
-            document.cookie = 'lang=' + $(event.target).data('lang') + '; SameSite=Lax; Secure';
+            let lang = $(event.target).data('lang') || event.target.value;
+
+            document.cookie = 'lang=' + lang + '; SameSite=Lax; Secure';
+            window.location.reload();
+            event.preventDefault();
+        }
+
+        function setTemplate(event)
+        {
+            let template = $(event.target).data('template') || event.target.value;
+
+            document.cookie = 'template=' + template + '; SameSite=Lax; Secure';
             window.location.reload();
             event.preventDefault();
         }
@@ -4625,7 +4636,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
             // bootstrap template drop down
             $('#language ul.dropdown-menu li a').click(setLanguage);
             // page template drop down
-            $('#language select option').click(setLanguage);
+            $('#language select').change(setLanguage);
+
+            // bootstrap template drop down
+            $('#template ul.dropdown-menu li a').click(setTemplate);
+            // page template drop down
+            $('#template select').change(setTemplate);
 
             // bind events
             $burnAfterReading.change(changeBurnAfterReading);

+ 3 - 2
lib/Configuration.php

@@ -45,7 +45,8 @@ class Configuration
             'defaultformatter'         => 'plaintext',
             'syntaxhighlightingtheme'  => '',
             'sizelimit'                => 10485760,
-            'template'                 => 'bootstrap',
+            'templateselection'        => false,
+            'templatedefault'          => 'bootstrap',
             'info'                     => 'More information on the <a href=\'https://privatebin.info/\'>project page</a>.',
             'notice'                   => '',
             'languageselection'        => false,
@@ -108,7 +109,7 @@ class Configuration
             'js/kjua-0.9.0.js'       => 'sha512-CVn7af+vTMBd9RjoS4QM5fpLFEOtBCoB0zPtaqIDC7sF4F8qgUSRFQQpIyEDGsr6yrjbuOLzdf20tkHHmpaqwQ==',
             'js/legacy.js'           => 'sha512-UxW/TOZKon83n6dk/09GsYKIyeO5LeBHokxyIq+r7KFS5KMBeIB/EM7NrkVYIezwZBaovnyNtY2d9tKFicRlXg==',
             'js/prettify.js'         => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==',
-            'js/privatebin.js'       => 'sha512-POa+8KNXFFwJFsqp7r9APmR5Rc1w2l363y+OScSzLCySrHN7UhOOgt1VH/o8mVddFvvUozj3FZVmdkTxRlrS5g==',
+            'js/privatebin.js'       => 'sha512-YDgd0cFfFHP/DLHQH2CWkGMjdFC5YjwDuWSoEsc1gU4h5sRd+T88mPYva1cdoviKEoty6F8pDe3E6chcMEzRag==',
             'js/purify-3.2.4.js'     => 'sha512-Mu9BqoHURMeycg6AgqTpokUv9guq88pajfaFqz53fx1OxohyROkydXPLEIbdKCQ7EdDs9hgcrYeZ9zTiPQQ4CA==',
             'js/rawinflate-0.3.js'   => 'sha512-g8uelGgJW9A/Z1tB6Izxab++oj5kdD7B4qC7DHwZkB6DGMXKyzx7v5mvap2HXueI2IIn08YlRYM56jwWdm2ucQ==',
             'js/showdown-2.1.0.js'   => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==',

+ 32 - 2
lib/Controller.php

@@ -179,7 +179,14 @@ class Controller
         $this->_request = new Request;
         $this->_urlBase = $this->_request->getRequestUri();
 
-        // set default language
+        $this->_setDefaultLanguage();
+        $this->_setDefaultTemplate();
+    }
+
+    private function _setDefaultLanguage()
+    {
+        $this->_conf = new Configuration;
+
         $lang = $this->_conf->getKey('languagedefault');
         I18n::setLanguageFallback($lang);
         // force default language, if language selection is disabled and a default is set
@@ -189,6 +196,20 @@ class Controller
         }
     }
 
+
+    private function _setDefaultTemplate()
+    {
+        $this->_conf = new Configuration;
+
+        $template = $this->_conf->getKey('templatedefault');
+        TemplateSwitcher::setTemplateFallback($template);
+        // force default template, if template selection is disabled and a default is set
+        if (!$this->_conf->getKey('languageselection') && strlen($template) == 2) {
+            $_COOKIE['template'] = $template;
+            setcookie('template', $template, array('SameSite' => 'Lax', 'Secure' => true));
+        }
+    }
+
     /**
      * Turn off browser caching
      *
@@ -400,6 +421,13 @@ class Controller
             setcookie('lang', $languageselection, array('SameSite' => 'Lax', 'Secure' => true));
         }
 
+        // set template cookie if that functionality was enabled
+        $templateselection = '';
+        if ($this->_conf->getKey('templateselection')) {
+            $templateselection = TemplateSwitcher::getTemplate();
+            setcookie('template', $templateselection, array('SameSite' => 'Lax', 'Secure' => true));
+        }
+
         // strip policies that are unsupported in meta tag
         $metacspheader = str_replace(
             array(
@@ -438,6 +466,8 @@ class Controller
         $page->assign('ZEROBINCOMPATIBILITY', $this->_conf->getKey('zerobincompatibility'));
         $page->assign('LANGUAGESELECTION', $languageselection);
         $page->assign('LANGUAGES', I18n::getLanguageLabels(I18n::getAvailableLanguages()));
+        $page->assign('TEMPLATESELECTION', $templateselection);
+        $page->assign('TEMPLATES', TemplateSwitcher::getAvailableTemplates());
         $page->assign('EXPIRE', $expire);
         $page->assign('EXPIREDEFAULT', $this->_conf->getKey('default', 'expire'));
         $page->assign('URLSHORTENER', $this->_conf->getKey('urlshortener'));
@@ -447,7 +477,7 @@ class Controller
         $page->assign('HTTPSLINK', 'https://' . $this->_request->getHost() . $this->_request->getRequestUri());
         $page->assign('COMPRESSION', $this->_conf->getKey('compression'));
         $page->assign('SRI', $this->_conf->getSection('sri'));
-        $page->draw($this->_conf->getKey('template'));
+        $page->draw(TemplateSwitcher::getTemplate());
     }
 
     /**

+ 117 - 0
lib/TemplateSwitcher.php

@@ -0,0 +1,117 @@
+<?php declare(strict_types=1);
+/**
+ * PrivateBin
+ *
+ * a zero-knowledge paste bin
+ *
+ * @link      https://github.com/PrivateBin/PrivateBin
+ * @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
+ * @license   https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
+ */
+
+namespace PrivateBin;
+
+
+/**
+ * TemplateSwitcher
+ *
+ * Provides tool to change application template
+ */
+class TemplateSwitcher {
+
+    /**
+     * template fallback
+     *
+     * @access protected
+     * @static
+     * @var    string
+     */
+    protected static $_templateFallback = "bootstrap";
+
+
+    /**
+     * available templates
+     *
+     * @access protected
+     * @static
+     * @var    array
+     */
+    protected static $_availableTemplates = [
+        'page',
+        'bootstrap',
+        'bootstrap-compact',
+        'bootstrap-dark',
+        'bootstrap-page',
+        'bootstrap5',
+    ];
+
+    /**
+     * set the default template
+     *
+     * @access public
+     * @static
+     * @param  string $template
+     */
+    public static function setTemplateFallback(string $template)
+    {
+        if (self::isTemplateAvailable($template)) {
+            self::$_templateFallback = $template;
+        }
+    }
+
+    /**
+     * get currently loaded template
+     *
+     * @access public
+     * @static
+     * @return string
+     */
+    public static function getTemplate(): string
+    {
+        $selectedTemplate = self::getSelectedByUserTemplate();
+        return $selectedTemplate ?? self::$_templateFallback;
+    }
+
+    /**
+     * get list of available templates
+     *
+     * @access public
+     * @static
+     * @return array
+     */
+    public static function getAvailableTemplates(): array
+    {
+        return self::$_availableTemplates;
+    }
+
+    /**
+     * check if the provided template is available
+     *
+     * @access public
+     * @static
+     * @return bool
+     */
+    public static function isTemplateAvailable(string $template): bool
+    {
+        return in_array($template, self::getAvailableTemplates());
+    }
+
+    /**
+     * get the template selected by user
+     *
+     * @access private
+     * @static
+     * @return string
+     */
+    private static function getSelectedByUserTemplate(): ?string
+    {
+        $selectedTemplate = null;
+        $templateCookieValue = $_COOKIE['template'] ?? "";
+
+        if (self::isTemplateAvailable($templateCookieValue)) {
+            $selectedTemplate = $templateCookieValue;
+        }
+
+        return $selectedTemplate;
+    }
+}

+ 22 - 0
tpl/bootstrap.php

@@ -457,6 +457,28 @@ if (!empty($LANGUAGESELECTION)) :
 					</li>
 <?php
 endif;
+?>
+<?php
+if (!empty($TEMPLATESELECTION)) :
+?>
+					<li id="template" class="dropdown">
+						<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><?php echo I18n::_('Theme'); ?>: <?php echo $TEMPLATESELECTION; ?> <span class="caret"></span></a>
+						<ul class="dropdown-menu dropdown-menu-right">
+<?php
+    foreach ($TEMPLATES as $value) :
+?>
+							<li>
+								<a href="#" data-template="<?php echo $value; ?>">
+									<?php echo $value; ?>
+								</a>
+							</li>
+<?php
+    endforeach;
+?>
+						</ul>
+					</li>
+<?php
+endif;
 ?>
 				</ul>
 			</div>

+ 24 - 0
tpl/bootstrap5.php

@@ -323,6 +323,30 @@ if (!empty($LANGUAGESELECTION)) :
 						</li>
 <?php
 endif;
+?>
+<?php
+if (!empty($TEMPLATESELECTION)) :
+?>
+						<li id="template" class="nav-item dropdown">
+							<a href="#" class="nav-link dropdown-toggle d-flex align-items-center gap-1" data-bs-toggle="dropdown" role="button" aria-expanded="false">
+								<?php echo I18n::_('Theme'); ?>: <?php echo $TEMPLATESELECTION, PHP_EOL; ?>
+							</a>
+							<ul class="dropdown-menu dropdown-menu-end" role="menu">
+<?php
+    foreach ($TEMPLATES as $value) :
+?>
+								<li>
+									<a href="#" class="dropdown-item" data-template="<?php echo $value; ?>">
+										<?php echo $value; ?>
+									</a>
+								</li>
+<?php
+    endforeach;
+?>
+							</ul>
+						</li>
+<?php
+endif;
 ?>
 					</ul>
 				</div>

+ 21 - 0
tpl/page.php

@@ -222,6 +222,27 @@ if (!empty($LANGUAGESELECTION)):
 					</div>
 <?php
 endif;
+?>
+<?php
+if (!empty($TEMPLATESELECTION)):
+?>
+					<div id="template" class="button"><?php echo I18n::_('Theme'); ?>:
+						<select name="template">
+<?php
+    foreach ($TEMPLATES as $value):
+?>
+							<option data-template="<?php echo $value; ?>" value="<?php echo $value; ?>"<?php
+        if ($value == $TEMPLATESELECTION):
+?> selected="selected"<?php
+        endif;
+?>><?php echo $value; ?></option>
+<?php
+    endforeach;
+?>
+						</select>
+					</div>
+<?php
+endif;
 ?>
 				</div>
 <?php

+ 56 - 0
tst/TemplateSwitcherTest.php

@@ -0,0 +1,56 @@
+<?php declare(strict_types=1);
+
+use PHPUnit\Framework\TestCase;
+use PrivateBin\TemplateSwitcher;
+
+
+class TemplateSwitcherTest extends TestCase
+{
+
+    public function testSetTemplateFallback()
+    {
+        $defaultTemplateFallback = "bootstrap";
+        $customTemplateFallback = "bootstrap-dark";
+        $wrongTemplateFallback = "bootstrap-wrong";
+
+        $this->assertEquals($defaultTemplateFallback, TemplateSwitcher::getTemplate(), "Default template fallback");
+
+        TemplateSwitcher::setTemplateFallback($wrongTemplateFallback);
+        $this->assertEquals($defaultTemplateFallback, TemplateSwitcher::getTemplate(), "Wrong template fallback");
+
+        TemplateSwitcher::setTemplateFallback($customTemplateFallback);
+        $this->assertEquals($customTemplateFallback, TemplateSwitcher::getTemplate(), "Custom template fallback");
+    }
+
+
+    public function testGetTemplate()
+    {
+        $defaultTemplateFallback = "bootstrap";
+        $customTemplate = "bootstrap-dark";
+        $customWrongTemplate = "bootstrap-wrong";
+
+        TemplateSwitcher::setTemplateFallback($defaultTemplateFallback);
+
+        $_COOKIE['template'] = $customWrongTemplate;
+        $this->assertEquals($defaultTemplateFallback, TemplateSwitcher::getTemplate(), "Custom wrong template");
+
+        $_COOKIE['template'] = $customTemplate;
+        $this->assertEquals($customTemplate, TemplateSwitcher::getTemplate(), "Custom template");
+    }
+
+
+    public function testGetAvailableTemplates()
+    {
+        $this->assertNotEmpty(TemplateSwitcher::getAvailableTemplates(), "Available templates");
+    }
+
+
+    public function testIsTemplateAvailable()
+    {
+        $existingTemplate = "bootstrap";
+        $nonExistentTemplate = "bootstrap-wrong";
+
+        $this->assertTrue(TemplateSwitcher::isTemplateAvailable($existingTemplate), "Existing template");
+        $this->assertFalse(TemplateSwitcher::isTemplateAvailable($nonExistentTemplate), "Non-existent template");
+    }
+}