mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-26 17:52:59 +02:00
add translations and error messages for encrypted/password protected files being edited
This commit is contained in:
parent
f412a3a60e
commit
c9ac919c55
@ -74,6 +74,8 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
|
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
|
||||||
|
"encryptedPdfMustRemovePassword": "This PDF is encrypted or password-protected. Please unlock it before converting to PDF/A.",
|
||||||
|
"incorrectPasswordProvided": "The PDF password is incorrect or not provided.",
|
||||||
"_value": "Error",
|
"_value": "Error",
|
||||||
"dismissAllErrors": "Dismiss All Errors",
|
"dismissAllErrors": "Dismiss All Errors",
|
||||||
"sorry": "Sorry for the issue!",
|
"sorry": "Sorry for the issue!",
|
||||||
|
@ -68,6 +68,8 @@
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
|
"pdfPassword": "The PDF Document is passworded and either the password was not provided or was incorrect",
|
||||||
|
"encryptedPdfMustRemovePassword": "This PDF is encrypted or password-protected. Please unlock it before converting to PDF/A.",
|
||||||
|
"incorrectPasswordProvided": "The PDF password is incorrect or not provided.",
|
||||||
"_value": "Error",
|
"_value": "Error",
|
||||||
"sorry": "Sorry for the issue!",
|
"sorry": "Sorry for the issue!",
|
||||||
"needHelp": "Need help / Found an issue?",
|
"needHelp": "Need help / Found an issue?",
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { alert } from '../components/toast';
|
import { alert } from '../components/toast';
|
||||||
import { broadcastErroredFiles, extractErrorFileIds, normalizeAxiosErrorData } from './errorUtils';
|
import { broadcastErroredFiles, extractErrorFileIds, normalizeAxiosErrorData } from './errorUtils';
|
||||||
|
import { showSpecialErrorToast } from './specialErrorToasts';
|
||||||
|
|
||||||
const FRIENDLY_FALLBACK = 'There was an error processing your request.';
|
const FRIENDLY_FALLBACK = 'There was an error processing your request.';
|
||||||
|
|
||||||
@ -49,6 +50,11 @@ function extractAxiosErrorMessage(error: any): { title: string; body: string } {
|
|||||||
if (typeof raw === 'string') return raw;
|
if (typeof raw === 'string') return raw;
|
||||||
try { return JSON.stringify(data); } catch { return ''; }
|
try { return JSON.stringify(data); } catch { return ''; }
|
||||||
})();
|
})();
|
||||||
|
// Specific friendly mapping for encrypted PDFs (centralized toast also fires in interceptor)
|
||||||
|
if (ENCRYPTION_ERROR_REGEX.test(body)) {
|
||||||
|
const title = titleForStatus(error.response?.status);
|
||||||
|
return { title, body: ENCRYPTION_FRIENDLY };
|
||||||
|
}
|
||||||
const ids = extractIds();
|
const ids = extractIds();
|
||||||
const title = titleForStatus(status);
|
const title = titleForStatus(status);
|
||||||
if (ids && ids.length > 0) {
|
if (ids && ids.length > 0) {
|
||||||
@ -64,6 +70,9 @@ function extractAxiosErrorMessage(error: any): { title: string; body: string } {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const msg = (error?.message || String(error)) as string;
|
const msg = (error?.message || String(error)) as string;
|
||||||
|
if (ENCRYPTION_ERROR_REGEX.test(msg)) {
|
||||||
|
return { title: 'Request error', body: ENCRYPTION_FRIENDLY };
|
||||||
|
}
|
||||||
return { title: 'Network error', body: isUnhelpfulMessage(msg) ? FRIENDLY_FALLBACK : msg };
|
return { title: 'Network error', body: isUnhelpfulMessage(msg) ? FRIENDLY_FALLBACK : msg };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore extraction errors
|
// ignore extraction errors
|
||||||
@ -109,7 +118,17 @@ const __INTERCEPTOR_ID__ = axios.interceptors.response.use(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Show specialized friendly toasts if matched; otherwise show the generic one
|
||||||
|
const raw = (error?.response?.data) as any;
|
||||||
|
let rawString: string | undefined;
|
||||||
|
try {
|
||||||
|
if (typeof raw === 'string') rawString = raw;
|
||||||
|
else rawString = await normalizeAxiosErrorData(raw).then((d) => (typeof d === 'string' ? d : JSON.stringify(d)));
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
const handled = showSpecialErrorToast(rawString, { status });
|
||||||
|
if (!handled) {
|
||||||
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
|
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
|
||||||
|
}
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
57
frontend/src/services/specialErrorToasts.ts
Normal file
57
frontend/src/services/specialErrorToasts.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { alert } from '../components/toast';
|
||||||
|
|
||||||
|
interface ErrorToastMapping {
|
||||||
|
regex: RegExp;
|
||||||
|
i18nKey: string;
|
||||||
|
defaultMessage: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Centralized list of special backend error message patterns → friendly, translated toasts
|
||||||
|
const MAPPINGS: ErrorToastMapping[] = [
|
||||||
|
{
|
||||||
|
regex: /pdf contains an encryption dictionary/i,
|
||||||
|
i18nKey: 'errors.encryptedPdfMustRemovePassword',
|
||||||
|
defaultMessage: 'This PDF is encrypted. Please unlock it using the Unlock PDF Forms tool.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /the pdf document is passworded and either the password was not provided or was incorrect/i,
|
||||||
|
i18nKey: 'errors.incorrectPasswordProvided',
|
||||||
|
defaultMessage: 'The PDF password is incorrect or not provided.'
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function titleForStatus(status?: number): string {
|
||||||
|
if (!status) return 'Network error';
|
||||||
|
if (status >= 500) return 'Server error';
|
||||||
|
if (status >= 400) return 'Request error';
|
||||||
|
return 'Request failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match a raw backend error string against known patterns and show a friendly toast.
|
||||||
|
* Returns true if a special toast was shown, false otherwise.
|
||||||
|
*/
|
||||||
|
export function showSpecialErrorToast(rawError: string | undefined, options?: { status?: number }): boolean {
|
||||||
|
const message = (rawError || '').toString();
|
||||||
|
if (!message) return false;
|
||||||
|
|
||||||
|
for (const mapping of MAPPINGS) {
|
||||||
|
if (mapping.regex.test(message)) {
|
||||||
|
// Best-effort translation without hard dependency on i18n config
|
||||||
|
let body = mapping.defaultMessage;
|
||||||
|
try {
|
||||||
|
const anyGlobal: any = (globalThis as any);
|
||||||
|
const i18next = anyGlobal?.i18next;
|
||||||
|
if (i18next && typeof i18next.t === 'function') {
|
||||||
|
body = i18next.t(mapping.i18nKey, { defaultValue: mapping.defaultMessage });
|
||||||
|
}
|
||||||
|
} catch { /* ignore translation errors */ }
|
||||||
|
const title = titleForStatus(options?.status);
|
||||||
|
alert({ alertType: 'error', title, body, expandable: true, isPersistentPopup: false });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user