Merge branch 'master' into dompurify-3.2.6

# Conflicts:
#	lib/Configuration.php
This commit is contained in:
Ribas160 2025-06-26 13:37:00 +03:00
commit c7b9ce0bc2
9 changed files with 88 additions and 21 deletions

View file

@ -24,7 +24,7 @@ jobs:
steps: steps:
- name: Download and Extract Artifacts - name: Download and Extract Artifacts
uses: dawidd6/action-download-artifact@4c1e823582f43b179e2cbb49c3eade4e41f992e2 uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5
with: with:
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
path: artifacts path: artifacts

View file

@ -6,6 +6,7 @@
* CHANGED: Removed use of ctype functions and polyfill library for ctype * CHANGED: Removed use of ctype functions and polyfill library for ctype
* CHANGED: Upgrading libraries to: DOMpurify 3.2.6, ip-lib 1.20.0 * CHANGED: Upgrading libraries to: DOMpurify 3.2.6, ip-lib 1.20.0
* CHANGED: Support for multiple file uploads (#1060) * CHANGED: Support for multiple file uploads (#1060)
* CHANGED: Documented CSP change necessary to allow PDF attachment preview (#1552)
* FIXED: Hide Reply button in the discussions once clicked to avoid losing the text input (#1508) * FIXED: Hide Reply button in the discussions once clicked to avoid losing the text input (#1508)
## 1.7.6 (2025-02-01) ## 1.7.6 (2025-02-01)

View file

@ -118,7 +118,15 @@ languageselection = false
; for details. ; for details.
; - The 'wasm-unsafe-eval' is used to enable webassembly support (used for zlib ; - The 'wasm-unsafe-eval' is used to enable webassembly support (used for zlib
; compression). You can remove it if compression doesn't need to be supported. ; compression). You can remove it if compression doesn't need to be supported.
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads" ; - The 'unsafe-inline' style-src is used by Chrome when displaying PDF previews
; and can be omitted if attachment upload is disabled (which is the default).
; See https://issues.chromium.org/issues/343754409
; - To allow displaying PDF previews in Firefox or Chrome, sandboxing must also
; get turned off. The following CSP allows PDF previews:
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; img-src 'self' data: blob:; media-src blob:; object-src blob:"
;
; The recommended and default used CSP is:
; cspheader = "default-src 'none'; base-uri 'self'; form-action 'none'; manifest-src 'self'; connect-src * blob:; script-src 'self' 'wasm-unsafe-eval'; style-src 'self'; font-src 'self'; frame-ancestors 'none'; frame-src blob:; img-src 'self' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads"
; stay compatible with PrivateBin Alpha 0.19, less secure ; stay compatible with PrivateBin Alpha 0.19, less secure
; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of ; if enabled will use base64.js version 1.7 instead of 2.1.9 and sha1 instead of

View file

@ -168,13 +168,13 @@
"Plain Text": "نص عادي", "Plain Text": "نص عادي",
"Source Code": "كود مصدر", "Source Code": "كود مصدر",
"Markdown": "ماركداون", "Markdown": "ماركداون",
"Download attachment": "تنزيل المرفقات", "Download attachment": "نزّل المرفق",
"Cloned: '%s'": "مستنسخ: '%s'", "Cloned: '%s'": "مستنسخ: '%s'",
"The cloned file '%s' was attached to this paste.": "تم إرفاق المِلَفّ المستنسخ '%s' بهذا اللصق.", "The cloned file '%s' was attached to this paste.": "تم إرفاق الملف المستنسخ '%s' بهذا اللصق.",
"Attach a file": "إرفاق مِلَفّ", "Attach a file": "أرفق ملف",
"alternatively drag & drop a file or paste an image from the clipboard": "بدلاً من ذلك، اسحب ملفًا وأسقطه أو الصق صورة من الحافظة", "alternatively drag & drop a file or paste an image from the clipboard": "بدلاً من ذلك، اسحب ملفًا وأسقطه أو الصق صورة من الحافظة",
"File too large, to display a preview. Please download the attachment.": "المِلَفّ كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.", "File too large, to display a preview. Please download the attachment.": "الملف كبير جدًا، بحيث لا يمكن عرض معاينة. الرجاء تنزيل المرفق.",
"Remove attachment": "إزالة المرفق", "Remove attachment": "أزِل المرفق",
"Your browser does not support uploading encrypted files. Please use a newer browser.": "متصفحك لا يدعم رفع الملفات المشفرة. الرجاء استخدام متصفح أحدث.", "Your browser does not support uploading encrypted files. Please use a newer browser.": "متصفحك لا يدعم رفع الملفات المشفرة. الرجاء استخدام متصفح أحدث.",
"Invalid attachment.": "مرفق غير صحيح.", "Invalid attachment.": "مرفق غير صحيح.",
"Options": "الخيارات", "Options": "الخيارات",
@ -215,16 +215,16 @@
"Trying to shorten a URL that isn't pointing at our instance.": "محاولة تقصير عنوان URL لا يشير إلى خادمنا.", "Trying to shorten a URL that isn't pointing at our instance.": "محاولة تقصير عنوان URL لا يشير إلى خادمنا.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "خطأ في الاتصال بـ YOURLS. ربما تكون هناك مشكلة في التضبيط، مثل \"apiurl\" أو \"التوقيع\" الخاطئ أو المفقود.", "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "خطأ في الاتصال بـ YOURLS. ربما تكون هناك مشكلة في التضبيط، مثل \"apiurl\" أو \"التوقيع\" الخاطئ أو المفقود.",
"Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS.", "Error parsing YOURLS response.": "خطأ في تحليل استجابة YOURLS.",
"This secret message can only be displayed once. Would you like to see it now?": "لا يمكن عرض اللصق احرقه بعد قراءته إلا مرة واحدة عند تحميله. هل تريد فتحه الآن؟", "This secret message can only be displayed once. Would you like to see it now?": "يمكن عرض هذه الرسالة السرية مرة واحدة فقط. هل ترغب في رؤيتها الآن؟",
"Yes, see it": "نعم، حمله", "Yes, see it": "نعم، دعني اراها",
"Dark Mode": "الوضع الداكن", "Dark Mode": "الوضع الداكن",
"Error compressing paste, due to missing WebAssembly support.": "خطأ في ضغط اللصق، بسبب فقدان دعم WebAssembly.", "Error compressing paste, due to missing WebAssembly support.": "خطأ في ضغط اللصق، بسبب فقدان دعم WebAssembly.",
"Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "خطأ في فك ضغط اللصق، متصفحك لا يدعم WebAssembly. الرجاء استخدام متصفح آخر لعرض هذه اللصقة.", "Error decompressing paste, your browser does not support WebAssembly. Please use another browser to view this paste.": "خطأ في فك ضغط اللصق، متصفحك لا يدعم WebAssembly. الرجاء استخدام متصفح آخر لعرض هذه اللصقة.",
"Start over": "ابدأ من جديد", "Start over": "ابدأ من جديد",
"Paste copied to clipboard": "تم نسخ اللصق إلى الحافظة", "Paste copied to clipboard": "نُسخ اللصق إلى الحافظة",
"To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "لنسخ اللصق انقر على زر النسخ أو استخدم اختصار الحافظة <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>", "To copy paste press on the copy button or use the clipboard shortcut <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>": "لنسخ اللصق انقر على زر النسخ أو استخدم اختصار الحافظة <kbd>Ctrl</kbd>+<kbd>c</kbd>/<kbd>Cmd</kbd>+<kbd>c</kbd>",
"Copy link": "نسخ الرابط", "Copy link": "نسخ الرابط",
"Link copied to clipboard": "تم نسخ الرابط إلى الحافظة", "Link copied to clipboard": "نُسخ الرابط إلى الحافظة",
"Paste text": "لصق النص", "Paste text": "لصق النص",
"Tabulator key serves as character (Hit <kbd>Ctrl</kbd>+<kbd>m</kbd> or <kbd>Esc</kbd> to toggle)": "مفتاح التبويب يعمل كشخصية (انقر <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)": "مفتاح التبويب يعمل كشخصية (انقر <kbd>Ctrl</kbd>+<kbd>m</kbd> أو <kbd>Esc</kbd> للتبديل)",
"Theme": "السمة" "Theme": "السمة"

View file

@ -65,7 +65,7 @@ class Configuration
'qrcode' => true, 'qrcode' => true,
'email' => true, 'email' => true,
'icon' => 'identicon', 'icon' => 'identicon',
'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads', 'cspheader' => 'default-src \'none\'; base-uri \'self\'; form-action \'none\'; manifest-src \'self\'; connect-src * blob:; script-src \'self\' \'wasm-unsafe-eval\'; style-src \'self\'; font-src \'self\'; frame-ancestors \'none\'; frame-src blob:; img-src \'self\' data: blob:; media-src blob:; object-src blob:; sandbox allow-same-origin allow-scripts allow-forms allow-popups allow-modals allow-downloads',
'zerobincompatibility' => false, 'zerobincompatibility' => false,
'httpwarning' => true, 'httpwarning' => true,
'compression' => 'zlib', 'compression' => 'zlib',

View file

@ -25,7 +25,7 @@ class TemplateSwitcher
* @static * @static
* @var string * @var string
*/ */
protected static $_templateFallback; protected static $_templateFallback = 'bootstrap';
/** /**
* available templates * available templates
@ -59,6 +59,11 @@ class TemplateSwitcher
{ {
if (self::isTemplateAvailable($template)) { if (self::isTemplateAvailable($template)) {
self::$_templateFallback = $template; self::$_templateFallback = $template;
if (!in_array($template, self::getAvailableTemplates())) {
// Add custom template to the available templates list
self::$_availableTemplates[] = $template;
}
} }
} }
@ -96,7 +101,14 @@ class TemplateSwitcher
*/ */
public static function isTemplateAvailable(string $template): bool public static function isTemplateAvailable(string $template): bool
{ {
return in_array($template, self::getAvailableTemplates()); $available = in_array($template, self::getAvailableTemplates());
if (!$available && !View::isBootstrapTemplate($template)) {
$path = View::getTemplateFilePath($template);
$available = file_exists($path);
}
return $available;
} }
/** /**

View file

@ -49,8 +49,7 @@ class View
*/ */
public function draw($template) public function draw($template)
{ {
$file = substr($template, 0, 10) === 'bootstrap-' ? 'bootstrap' : $template; $path = self::getTemplateFilePath($template);
$path = PATH . 'tpl' . DIRECTORY_SEPARATOR . $file . '.php';
if (!file_exists($path)) { if (!file_exists($path)) {
throw new Exception('Template ' . $template . ' not found!', 80); throw new Exception('Template ' . $template . ' not found!', 80);
} }
@ -58,6 +57,31 @@ class View
include $path; include $path;
} }
/**
* Get template file path
*
* @access public
* @param string $template
* @return string
*/
public static function getTemplateFilePath(string $template): string
{
$file = self::isBootstrapTemplate($template) ? 'bootstrap' : $template;
return PATH . 'tpl' . DIRECTORY_SEPARATOR . $file . '.php';
}
/**
* Is the template a variation of the bootstrap template
*
* @access public
* @param string $template
* @return bool
*/
public static function isBootstrapTemplate(string $template): bool
{
return substr($template, 0, 10) === 'bootstrap-';
}
/** /**
* echo script tag incl. SRI hash for given script file * echo script tag incl. SRI hash for given script file
* *

View file

@ -10,15 +10,21 @@ class TemplateSwitcherTest extends TestCase
{ {
$conf = new Configuration; $conf = new Configuration;
$defaultTemplateFallback = 'bootstrap';
$existingTemplateFallback = 'bootstrap-dark'; $existingTemplateFallback = 'bootstrap-dark';
$wrongTemplateFallback = 'bootstrap-wrong'; $wrongBootstrapTemplateFallback = 'bootstrap-wrong';
$wrongTemplateFallback = 'wrong-template';
TemplateSwitcher::setAvailableTemplates($conf->getKey('availabletemplates')); TemplateSwitcher::setAvailableTemplates($conf->getKey('availabletemplates'));
TemplateSwitcher::setTemplateFallback($existingTemplateFallback);
$this->assertEquals($existingTemplateFallback, TemplateSwitcher::getTemplate(), 'Correct template fallback'); TemplateSwitcher::setTemplateFallback($wrongBootstrapTemplateFallback);
$this->assertEquals($defaultTemplateFallback, TemplateSwitcher::getTemplate(), 'Wrong bootstrap template fallback');
TemplateSwitcher::setTemplateFallback($wrongTemplateFallback); TemplateSwitcher::setTemplateFallback($wrongTemplateFallback);
$this->assertEquals($existingTemplateFallback, TemplateSwitcher::getTemplate(), 'Wrong template fallback'); $this->assertEquals($defaultTemplateFallback, TemplateSwitcher::getTemplate(), 'Wrong template fallback');
TemplateSwitcher::setTemplateFallback($existingTemplateFallback);
$this->assertEquals($existingTemplateFallback, TemplateSwitcher::getTemplate(), 'Correct template fallback');
} }
public function testSetAvailableTemplates() public function testSetAvailableTemplates()

View file

@ -142,4 +142,20 @@ class ViewTest extends TestCase
$this->expectExceptionCode(80); $this->expectExceptionCode(80);
$test->draw('123456789 does not exist!'); $test->draw('123456789 does not exist!');
} }
public function testTemplateFilePath()
{
$template = 'bootstrap';
$templatePath = PATH . 'tpl' . DIRECTORY_SEPARATOR . $template . '.php';
$path = View::getTemplateFilePath($template);
$this->assertEquals($templatePath, $path, 'Template file path');
}
public function testIsBootstrapTemplate()
{
$bootstrapTemplate = 'bootstrap-dark';
$nonBootstrapTemplate = 'page';
$this->assertTrue(View::isBootstrapTemplate($bootstrapTemplate), 'Is bootstrap template');
$this->assertFalse(View::isBootstrapTemplate($nonBootstrapTemplate), 'Is not bootstrap template');
}
} }