diff --git a/gulpfile.mjs b/gulpfile.mjs index 4f3af9d64..496df438b 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -203,6 +203,7 @@ function createWebpackAlias(defines) { "web-pdf_attachment_viewer": "web/pdf_attachment_viewer.js", "web-pdf_cursor_tools": "web/pdf_cursor_tools.js", "web-pdf_document_properties": "web/pdf_document_properties.js", + "web-pdf_save_cfaz_dialog": "web/pdf_save_cfaz_dialog.js", "web-pdf_find_bar": "web/pdf_find_bar.js", "web-pdf_layer_viewer": "web/pdf_layer_viewer.js", "web-pdf_outline_viewer": "web/pdf_outline_viewer.js", diff --git a/web/app.js b/web/app.js index 491761d69..e21e628ee 100644 --- a/web/app.js +++ b/web/app.js @@ -69,6 +69,7 @@ import { PasswordPrompt } from "./password_prompt.js"; import { PDFAttachmentViewer } from "web-pdf_attachment_viewer"; import { PDFCursorTools } from "web-pdf_cursor_tools"; import { PDFDocumentProperties } from "web-pdf_document_properties"; +import { PDFSaveCfazDialog } from "web-pdf_save_cfaz_dialog"; import { PDFFindBar } from "web-pdf_find_bar"; import { PDFFindController } from "./pdf_find_controller.js"; import { PDFHistory } from "./pdf_history.js"; @@ -117,6 +118,8 @@ const PDFViewerApplication = { pdfPresentationMode: null, /** @type {PDFDocumentProperties} */ pdfDocumentProperties: null, + /** @type {PDFSaveCfazDialog} */ + pdfSaveCfazDialog: null, /** @type {PDFLinkService} */ pdfLinkService: null, /** @type {PDFHistory} */ @@ -400,19 +403,19 @@ const PDFViewerApplication = { const annotationEditorMode = AppOptions.get("annotationEditorMode"); const pageColors = AppOptions.get("forcePageColors") || - window.matchMedia("(forced-colors: active)").matches + window.matchMedia("(forced-colors: active)").matches ? { - background: AppOptions.get("pageColorsBackground"), - foreground: AppOptions.get("pageColorsForeground"), - } + background: AppOptions.get("pageColorsBackground"), + foreground: AppOptions.get("pageColorsForeground"), + } : null; const altTextManager = appConfig.altTextDialog ? new AltTextManager( - appConfig.altTextDialog, - container, - this.overlayManager, - eventBus - ) + appConfig.altTextDialog, + container, + this.overlayManager, + eventBus + ) : null; const pdfViewer = new PDFViewer({ @@ -500,7 +503,15 @@ const PDFViewerApplication = { this.overlayManager, eventBus, l10n, - /* fileNameLookup = */ () => this._docFilename + /* fileNameLookup = */() => this._docFilename + ); + } + + if (appConfig.saveCfazDialog) { + this.pdfSaveCfazDialog = new PDFSaveCfazDialog( + appConfig.saveCfazDialog, + this.overlayManager, + l10n ); } @@ -613,9 +624,8 @@ const PDFViewerApplication = { const { appConfig, eventBus } = this; let file; if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { - const queryString = document.location.search.substring(1); - const params = parseQueryString(queryString); - file = params.get("file") ?? AppOptions.get("defaultUrl"); + const achiveUrl = document.getElementById('archive_url')?.value; + file = achiveUrl ?? AppOptions.get("defaultUrl"); validateFileURL(file); } else if (PDFJSDev.test("MOZCENTRAL")) { file = window.location.href; @@ -1042,6 +1052,99 @@ const PDFViewerApplication = { throw new Error("PDF document not downloaded."); }, + /** + * @private + */ + async _sendFileToStorage(directUpload) { + return new Promise((resolve, reject) => { + directUpload.create((error, blob) => { + if (error) { + reject(new Error(`Não foi possível atualizar o arquivo. ${error}`)); + } else { + resolve(blob) + } + }) + }); + }, + + /** + * @private + */ + async _updateArchiveCfaz(archive_id, document_signed_id) { + return new Promise((resolve, reject) => { + const params = JSON.stringify({ archive: { document: document_signed_id } }) + let xhr = new XMLHttpRequest(); + xhr.open('PUT', `/archives/${archive_id}.json`, true); + + const csrfToken = document.head.querySelector("[name='csrf-token']")?.content; + if (csrfToken) { + xhr.setRequestHeader('X-CSRF-Token', csrfToken); + } + + xhr.onload = function () { + if (xhr.status === 200) { + resolve(); + } else { + reject(new Error("Não foi possível atualizar o arquivo.")); + } + }; + + xhr.onerror = function (e) { + reject(e); + }; + + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(params); + }); + }, + + async saveCfaz() { + if (this._saveInProgress) { + return; + } + this._saveInProgress = true; + await this.pdfScriptingManager.dispatchWillSave(); + + const ac_url = document.getElementById('active_storage_url')?.value, + archive_name = document.getElementById('archive_name')?.value, + archiveId = document.getElementById('archive_id')?.value; + + this.pdfSaveCfazDialog?.open(); + try { + this._ensureDownloadComplete(); + + const data = await this.pdfDocument.saveDocument(); + const blob = new Blob([data], { type: "application/pdf" }); + + const doc_file = new File([blob], archive_name, { type: "application/pdf" }) + if(ac_url && archiveId){ + const directUpload = new ActiveStorage.DirectUpload(doc_file, ac_url) + await this._sendFileToStorage(directUpload).then((document)=>{ + this._updateArchiveCfaz(archiveId, document.signed_id); + }); + } + + this.pdfSaveCfazDialog?.setMessageContent('Arquivo enviado com sucesso.'); + } catch (reason) { + // When the PDF document isn't ready, or the PDF file is still + // downloading, simply fallback to a "regular" download. + this.pdfSaveCfazDialog?.setMessageContent(`Error when saving the document: ${reason.message}`); + console.error(`Error when saving the document: ${reason.message}`); + // await this.download(); + } finally { + this.pdfSaveCfazDialog?.setCloseButtonToggle(true) + await this.pdfScriptingManager.dispatchDidSave(); + this._saveInProgress = false; + } + + if (this._hasAnnotationEditors) { + this.externalServices.reportTelemetry({ + type: "editing", + data: { type: "save" }, + }); + } + }, + async download(options = {}) { const url = this._downloadUrl, filename = this._docFilename; @@ -1104,6 +1207,9 @@ const PDFViewerApplication = { } }, + backButton() { + window.history.back(); + }, /** * Report the error; used for errors affecting loading and/or parsing of * the entire PDF document. @@ -1507,8 +1613,8 @@ const PDFViewerApplication = { // Provides some basic debug information console.log( `PDF ${pdfDocument.fingerprints[0]} [${info.PDFFormatVersion} ` + - `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` + - `(PDF.js: ${version || "?"} [${build || "?"}])` + `${(info.Producer || "-").trim()} / ${(info.Creator || "-").trim()}] ` + + `(PDF.js: ${version || "?"} [${build || "?"}])` ); let pdfTitle = info.Title; @@ -1914,6 +2020,8 @@ const PDFViewerApplication = { ); eventBus._on("reporttelemetry", webViewerReportTelemetry, { signal }); } + eventBus._on("saveCfaz", webViewerSaveCfaz); + eventBus._on("backButton", webViewerBackButton); }, bindWindowEvents() { @@ -2117,7 +2225,7 @@ const PDFViewerApplication = { document.blockUnblockOnload?.(false); // Ensure that this method is only ever run once. - this._unblockDocumentLoadEvent = () => {}; + this._unblockDocumentLoadEvent = () => { }; }, /** @@ -2156,9 +2264,9 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { // Removing of the following line will not guarantee that the viewer will // start accepting URLs from foreign origin -- CORS headers on the remote // server must be properly configured. - if (fileOrigin !== viewerOrigin) { - throw new Error("file origin does not match viewer's"); - } + // if (fileOrigin !== viewerOrigin) { + // throw new Error("file origin does not match viewer's"); + // } } catch (ex) { PDFViewerApplication._documentError("pdfjs-loading-error", { message: ex.message, @@ -2396,7 +2504,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { // eslint-disable-next-line no-var var webViewerOpenFile = function (evt) { - PDFViewerApplication._openFileInput?.click(); + // PDFViewerApplication._openFileInput?.click(); }; } @@ -3214,6 +3322,14 @@ function webViewerAnnotationEditorStatesChanged(data) { PDFViewerApplication.externalServices.updateEditorStates(data); } +function webViewerSaveCfaz() { + PDFViewerApplication.saveCfaz(); +} + +function webViewerBackButton() { + PDFViewerApplication.backButton(); +} + function webViewerReportTelemetry({ details }) { PDFViewerApplication.externalServices.reportTelemetry(details); } diff --git a/web/app_options.js b/web/app_options.js index 61311172c..6cb04859d 100644 --- a/web/app_options.js +++ b/web/app_options.js @@ -377,10 +377,7 @@ const defaultOptions = { if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { defaultOptions.defaultUrl = { /** @type {string} */ - value: - typeof PDFJSDev !== "undefined" && PDFJSDev.test("CHROME") - ? "" - : "compressed.tracemonkey-pldi-09.pdf", + value: "", kind: OptionKind.VIEWER, }; defaultOptions.sandboxBundleSrc = { @@ -405,7 +402,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { }; defaultOptions.locale = { /** @type {string} */ - value: navigator.language || "en-US", + value: navigator.language || "pt-BR", kind: OptionKind.VIEWER, }; } else if (PDFJSDev.test("CHROME")) { diff --git a/web/pdf_save_cfaz_dialog.js b/web/pdf_save_cfaz_dialog.js new file mode 100644 index 000000000..e97e9f926 --- /dev/null +++ b/web/pdf_save_cfaz_dialog.js @@ -0,0 +1,94 @@ +/** + * @typedef {Object} PDFSaveCfazDialogOptions + * @property {HTMLDialogElement} dialog - The overlay's DOM element. + * @property {Object} message - Message's DOM element. + * @property {HTMLButtonElement} closeButton - Button for closing the overlay. + */ + +class PDFSaveCfazDialog { + /** + * @param {PDFSaveCfazDialogOptions} options + * @param {OverlayManager} overlayManager - Manager for the viewer overlays. + * @param {EventBus} eventBus - The application event bus. + * @param {IL10n} l10n - Localization service. + */ + constructor( + { dialog, message, closeButton }, + overlayManager, + l10n, + ) { + this.dialog = dialog; + this.message = message; + this.closeButton = closeButton; + this.overlayManager = overlayManager; + this.l10n = l10n; + + this.#reset(); + + // Bind the event listener for the Close button. + this.closeButton.addEventListener("click", this.close.bind(this)); + + this.overlayManager.register(this.dialog); + } + + /** + * Set messageContent to show in overlay + * + * @param {String} messageContent - Message string + */ + setMessageContent(messageContent) { + this.messageContent = messageContent; + + this.#updateUI(); + } + + /** + * Set closeButtonToggle to show in overlay + * + * @param {Boolean} closeButtonToggle - closeButtonToggle Boolean + */ + setCloseButtonToggle(closeButtonToggle) { + this.closeButtonToggle = closeButtonToggle; + + this.#updateUI(); + } + + #reset() { + this.closeButtonToggle = false; + this.messageContent = "Enviando arquivo ..."; + } + + /** + * Open the document properties overlay. + */ + async open() { + this.#reset(); + this.#updateUI(); + await Promise.all([ + this.overlayManager.open(this.dialog), + ]); + } + + /** + * Close the document properties overlay. + */ + async close() { + this.overlayManager.close(this.dialog); + } + + /** + * Always updates all of the dialog fields, to prevent inconsistent UI state. + */ + #updateUI() { + this.message.textContent = this.messageContent; + + if(this.closeButtonToggle){ + this.closeButton.parentElement.style = 'display: block' + } + else { + this.closeButton.parentElement.style = 'display: none' + } + } +} + +export { PDFSaveCfazDialog }; diff --git a/web/toolbar.js b/web/toolbar.js index e92b546c4..276faae01 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -61,6 +61,8 @@ class Toolbar { { element: options.zoomOut, eventName: "zoomout" }, { element: options.print, eventName: "print" }, { element: options.download, eventName: "download" }, + { element: options.saveCfaz, eventName: "saveCfaz" }, + { element: options.backButton, eventName: "backButton" }, { element: options.editorFreeTextButton, eventName: "switchannotationeditormode", diff --git a/web/viewer.css b/web/viewer.css index 56a32ad65..b6d5f95ba 100644 --- a/web/viewer.css +++ b/web/viewer.css @@ -861,6 +861,14 @@ body { left: 6px; } +.toolbarButton.customButton { + width: auto; +} + +.toolbarButton.customButton::before { + content: none; +} + .toolbarButton:is(:hover, :focus-visible)::before, .secondaryToolbarButton:is(:hover, :focus-visible)::before { background-color: var(--toolbar-icon-hover-bg-color); diff --git a/web/viewer.html b/web/viewer.html index 963818a1d..d5999642c 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -21,37 +21,41 @@ Adobe CMap resources are covered by their own copyright but the same license: See https://github.com/adobe-type-tools/cmap-resources --> - - - - - - - PDF.js viewer - - - - - - - + + + + + + + PDF.js viewer + + + - - - - - - + + + + + + + - - - - - - + + + + + + - - - - + + + + + - -
+ +
-
-
-
-
- - - - -
-
- -
-
-
- - -
-
-
-
-
-
- - - -
-
-
- -
- - - - - - - - - - +
+
+
+ + + +
+
+
-
-
-
-
- +
+ +
+
+ +
+ + + + +
+
+ + + + +
+ +
+ + +
+
+ + + + + + + + + + + +
+
+
+
+ +
+ + +
+ -
- -
- -
- -
+
+
- +
-
-
- - - - -
- -
- -
+
+
+ - - - -
- - +
-
-
- -
- -
- - + +
+ + + + + +
+ + +
+
+
+ +
+ +
+ +
+ + + +
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ + +
+
+ +
+ File name: +

-

+
+
+ File size: +

-

+
+
+
+ Title: +

-

+
+
+ Author: +

-

+
+
+ Subject: +

-

+
+
+ Keywords: +

-

+
+
+ Creation Date: +

-

+
+
+ Modification + Date: +

-

+
+
+ Creator: +

-

+
+
+
+ PDF Producer: +

-

+
+
+ PDF Version: +

-

+
+
+ Page Count: +

-

+
+
+ Page Size: +

-

+
+
+
+ Fast Web View: +

-

+
+
+ +
+
+ +
+
+ Choose an + option + + Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. + +
+
+
+
+ + +
+
+ + Aim for 1-2 sentences that describe the subject, setting, or actions.
-
-
-
-
+
+ +
+
+
+
+
+ + +
+
+ + This is used for ornamental images, like borders or watermarks. +
+
+ + +
- -
-
+
+ + +
+ Preparing document for printing…
-
+
+ + 0% +
+
+ +
+ + + +
+ Enviando arquivo… +
+ +
+ + + +
-
- -
- -
-
- -
-
- - -
-
- -
- File name: -

-

-
-
- File size: -

-

-
-
-
- Title: -

-

-
-
- Author: -

-

-
-
- Subject: -

-

-
-
- Keywords: -

-

-
-
- Creation Date: -

-

-
-
- Modification Date: -

-

-
-
- Creator: -

-

-
-
-
- PDF Producer: -

-

-
-
- PDF Version: -

-

-
-
- Page Count: -

-

-
-
- Page Size: -

-

-
-
-
- Fast Web View: -

-

-
-
- -
-
- -
-
- Choose an option - - Alt text (alternative text) helps when people can’t see the image or when it doesn’t load. - -
-
-
-
- - -
-
- - Aim for 1-2 sentences that describe the subject, setting, or actions. - -
-
-
- -
-
-
-
-
- - -
-
- - This is used for ornamental images, like borders or watermarks. - -
-
-
-
- - -
-
-
- - -
- Preparing document for printing… -
-
- - 0% -
-
- -
-
- - - - -
+
+
-
-
- - + + + + + \ No newline at end of file diff --git a/web/viewer.js b/web/viewer.js index ff40485c3..38f53aefa 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -69,6 +69,8 @@ function getViewerConfiguration() { "editorStampParamsToolbar" ), download: document.getElementById("download"), + saveCfaz: document.getElementById("saveCfaz"), + backButton: document.getElementById("backButton"), }, secondaryToolbar: { toolbar: document.getElementById("secondaryToolbar"), @@ -176,6 +178,11 @@ function getViewerConfiguration() { editorHighlightShowAll: document.getElementById("editorHighlightShowAll"), }, printContainer: document.getElementById("printContainer"), + saveCfazDialog: { + dialog: document.getElementById("saveCfazDialog"), + message: document.getElementById("saveCfazDialogMessage"), + closeButton: document.getElementById("saveCfazDialogClose") + } }; }