diff --git a/extension.json b/extension.json index 75e4b5f..8a98267 100644 --- a/extension.json +++ b/extension.json @@ -1,76 +1,70 @@ { "name": "ElectronPdfService", "version": "0.0.1", "author": [ "WMDE" ], "url": "https://www.mediawiki.org/wiki/Extension:ElectronPdfService", "descriptionmsg": "electronPdfService-desc", "license-name": "GPL-2.0+", "type": "other", "manifest_version": 1, "AutoloadClasses": { "ElectronPdfServiceHooks": "ElectronPdfService.hooks.php", "SpecialElectronPdf": "specials/SpecialElectronPdf.php" }, "config": { - "ElectronPdfService": { - "serviceUrl":"https://pdf-electron.wmflabs.org", - "format":"pdf", - "key":"secret", - "pageUrl":"" - }, - "ElectronPdfServiceMaxDocumentSize": 1073741824 + "ElectronPdfServiceRESTbaseURL": "/api/rest_v1/page/pdf/" }, "ExtensionMessagesFiles": { "ElectronPdfServiceAlias": "ElectronPdfService.i18n.alias.php" }, "Hooks": { "SidebarBeforeOutput": [ "ElectronPdfServiceHooks::onSidebarBeforeOutput" ] }, "MessagesDirs": { "ElectronPdfService": [ "i18n" ] }, "ResourceModules": { "ext.ElectronPdfService.special": { "scripts": [ "modules/ext.ElectronPdfService.special.js" ], "styles": [ "modules/ext.ElectronPdfService.special.less" ], "dependencies": [ "ext.ElectronPdfService.special.selectionImages" ] }, "ext.ElectronPdfService.special.selectionImages": { "class": "ResourceLoaderImageModule", "selector": ".mw-electronPdfService-selection-{name}", "images": { "single-column-image": { "file": { "ltr": "resources/selection-single-col-ltr.svg", "rtl": "resources/selection-single-col-rtl.svg" } }, "two-column-image": { "file": { "ltr": "resources/selection-two-col-ltr.svg", "rtl": "resources/selection-two-col-rtl.svg" } } } } }, "ResourceFileModulePaths": { "localBasePath": "", "remoteExtPath": "ElectronPdfService" }, "SpecialPages": { "ElectronPdf": "SpecialElectronPdf" } } diff --git a/i18n/en.json b/i18n/en.json index 95a26c6..17ea04c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1,22 +1,20 @@ { "@metadata": { "authors": [ "WMDE" ] }, "electronPdfService": "ElectronPdfService", "electronPdfService-desc": "Adds browser based PDF rendering support through Electron PDF service", "electronPdfService-special-page-headline": "Download as PDF", "electronPdfService-select-layout-header": "Select a layout", "electronPdfService-single-column-label": "Single column", "electronPdfService-two-column-label": "Two column", "electronPdfService-single-column-desc": "Includes tables and infoboxes", "electronPdfService-two-column-desc": "Without tables and infoboxes", "electronPdfService-download-button": "Download", "electronPdfService-sidebar-portlet-heading": "Print/export", "electronPdfService-sidebar-portlet-print-text": "Download as PDF", "electronPdfService-invalid-page-title": "Invalid page", - "electronPdfService-invalid-page-text": "The specified page is not valid.", - "electronPdfService-page-notfound-title": "Page not found by service", - "electronPdfService-page-notfound-text": "The service was not able to resolve the specified link." + "electronPdfService-invalid-page-text": "The specified page is not valid." } \ No newline at end of file diff --git a/i18n/qqq.json b/i18n/qqq.json index 6e7c3c6..c328165 100644 --- a/i18n/qqq.json +++ b/i18n/qqq.json @@ -1,25 +1,23 @@ { "@metadata": { "authors": [ "WMDE", "Robby", "Raymond", "Liuxinyu970226" ] }, "electronPdfService": "electronPdfService", "electronPdfService-desc": "{{desc|name=ElectronPdfService|url=}}", "electronPdfService-special-page-headline": "Title for the special page.", "electronPdfService-select-layout-header": "Header for layout selection.", "electronPdfService-single-column-label": "Label for the single column option.", "electronPdfService-two-column-label": "Label for the two column option.", "electronPdfService-single-column-desc": "Short description for the single column option.", "electronPdfService-two-column-desc": "Short description for the two column option.", "electronPdfService-download-button": "Label for the download button.\n{{Identical|Download}}", "electronPdfService-sidebar-portlet-heading": "Title of the portlet in which the link is shown.\n\n{{Identical|Print/Export}}", "electronPdfService-sidebar-portlet-print-text": "Text of print-pdf-link in sidebar.", "electronPdfService-invalid-page-title": "Used as title for the error message when specified page was not a valid article.", - "electronPdfService-invalid-page-text": "Used as error message when specified page was not a valid article.", - "electronPdfService-page-notfound-title": "Used as title for the error message when Electron service was not able to resolve the given URL to the article.", - "electronPdfService-page-notfound-text": "Used as error message when Electron service was not able to resolve the given URL to the article." + "electronPdfService-invalid-page-text": "Used as error message when specified page was not a valid article." } diff --git a/specials/SpecialElectronPdf.php b/specials/SpecialElectronPdf.php index 8c5797f..e16b070 100644 --- a/specials/SpecialElectronPdf.php +++ b/specials/SpecialElectronPdf.php @@ -1,298 +1,204 @@ config = MediaWikiServices::getInstance()->getMainConfig(); } /** * @param null|string $subPage */ public function execute( $subPage ) { $request = $this->getRequest(); $parts = ( $subPage === '' ) ? [] : explode( '/', $subPage, 2 ); $page = trim( $request->getVal( 'page', isset( $parts[0] ) ? $parts[0] : '' ) ); $collectionDownloadUrl = trim( $request->getVal( 'coll-download-url', isset( $parts[0] ) ? $parts[0] : '' ) ); $title = Title::newFromText( $page ); if ( $title === null ) { $this->getOutput()->showErrorPage( 'electronPdfService-invalid-page-title', 'electronPdfService-invalid-page-text' ); return; } $action = $request->getVal( 'action', 'default' ); $stats = MediaWikiServices::getInstance()->getStatsdDataFactory(); $stats->increment( 'electronpdf.action.' . $action ); switch ( $action ) { - case 'download-electron-pdf': - $this->renderAndShowPdf( $title ); + case 'redirect-to-electron': + $this->redirectToElectron( $title ); return; case 'redirect-to-collection': $this->redirectToCollection( $collectionDownloadUrl ); return; default: $this->showRenderModeSelectionPage( $title, $collectionDownloadUrl ); } } /** * @param Title $title page to download as PDF * @param string $collectionDownloadUrl URL to the download page of the Collection extension */ public function showRenderModeSelectionPage( Title $title, $collectionDownloadUrl ) { $this->setHeaders(); $out = $this->getOutput(); $out->enableOOUI(); $out->setPageTitle( $this->msg( 'electronPdfService-special-page-headline' )->text() ); $out->addSubtitle( $title->getText() ); $form = new OOUI\FormLayout( [ 'method' => 'POST', 'action' => $this->getPageTitle()->getLocalURL(), ] ); $form->addClasses( [ 'mw-electronPdfService-selection-form' ] ); $form->appendContent( ( new OOUI\Tag() ) ->addClasses( [ 'mw-electronPdfService-selection-header' ] ) ->appendContent( $this->msg( 'electronPdfService-select-layout-header' )->text() ), ( new OOUI\Tag() ) ->addClasses( [ 'mw-electronPdfService-selection-body' ] ) ->appendContent( - $this->getLabeledOptionField( 'download-electron-pdf', 'single', true ), + $this->getLabeledOptionField( 'redirect-to-electron', 'single', true ), $this->getLabeledOptionField( 'redirect-to-collection', 'two' ), $this->getHiddenField( 'page', $title->getText() ), $this->getHiddenField( 'coll-download-url', $collectionDownloadUrl ), new OOUI\ButtonGroupWidget( [ 'items' => [ new OOUI\ButtonInputWidget( [ 'type' => 'submit', 'flags' => [ 'primary', 'progressive' ], 'label' => $this->msg( 'electronPdfService-download-button' )->text(), ] ), ], ] ) ) ); $out->addHTML( $form ); } /** * @param string $action * @param string $name * @param boolean $selected * @return OOUI\Tag */ private function getLabeledOptionField( $action, $name, $selected = false ) { $image = ( new OOUI\Tag() )->addClasses( [ 'mw-electronPdfService-selection-image', 'mw-electronPdfService-selection-' . $name . '-column-image' ] ); $field = ( new OOUI\Tag() )->addClasses( [ 'mw-electronPdfService-selection-field' ] ); $field->appendContent( new OOUI\RadioInputWidget( [ 'name' => 'action', 'value' => $action, 'selected' => $selected ] ), ( new OOUI\Tag( 'b' ) )->addClasses( [ 'mw-electronPdfService-selection-label-text' ] ) ->appendContent( $this->msg( 'electronPdfService-' . $name . '-column-label' )->text() ), ( new OOUI\Tag() )->addClasses( [ 'mw-electronPdfService-selection-label-desc' ] ) ->appendContent( $this->msg( 'electronPdfService-' . $name . '-column-desc' )->text() ) ); $labelBox = ( new OOUI\Tag( 'label' ) )->appendContent( $image, $field ); return $labelBox; } /** * @param string $name * @param string $value * @return OOUI\Tag */ private function getHiddenField( $name, $value ) { $element = new OOUI\Tag( 'input' ); $element->setAttributes( [ 'type' => 'hidden', 'name' => $name, 'value' => $value ] ); return $element; } - /** - * @param Title $title page to download as PDF - */ - public function renderAndShowPdf( Title $title ) { - if ( !$this->getRequest()->checkUrlExtension() ) { - $this->getOutput()->showErrorPage( - 'electronPdfService-page-notfound-title', - 'electronPdfService-page-notfound-text' - ); - return; - } - $tempFile = TempFSFile::factory( 'electron_', 'pdf' ); - $this->tempFileHandle = fopen( $tempFile->getPath(), 'w+' ); - $this->totalBytesWritten = 0; - - $request = MWHttpRequest::factory( $this->constructServiceUrl( $title ) ); - $request->setCallback( [ $this, 'writeToTempFile' ] ); - - if ( $request->execute()->isOK() ) { - $this->sendPdfToOutput( $title->getPrefixedText() ); - } else { - $this->getOutput()->showErrorPage( - 'electronPdfService-page-notfound-title', - 'electronPdfService-page-notfound-text' - ); - } - - fclose( $this->tempFileHandle ); - $tempFile->purge(); - return; - } - - /** - * Callback used by the MWHttpRequest to the Electron service writing the result into a file - * If writing fails or the $maxDocumentSize is reached it returns -1 to abort the HTTP fetch. - * - * @param resource $res - * @param string $content - * @return int - */ - public function writeToTempFile( $res, $content ) { - $maxDocumentSize = $this->config->get( 'ElectronPdfServiceMaxDocumentSize' ); - $bytes = fwrite( - $this->tempFileHandle, - $content, - $maxDocumentSize - $this->totalBytesWritten + 1 - ); - - if ( $bytes === false ) { - return -1; - } - - $this->totalBytesWritten += $bytes; - - if ( $this->totalBytesWritten > $maxDocumentSize ) { - return -1; - } - - return $bytes; - } - public function setHeaders() { parent::setHeaders(); $this->addModules(); } protected function addModules() { $out = $this->getOutput(); $rl = $out->getResourceLoader(); $specialModuleName = 'ext.ElectronPdfService.special'; if ( $rl->isModuleRegistered( $specialModuleName ) ) { $out->addModules( $specialModuleName ); } } /** * @param Title $title * @return string */ - private function constructServiceUrl( Title $title ) { - $electronPdfService = $this->config->get( 'ElectronPdfService' ); - - // for testing the functionality on localhost please set - // $wgElectronPdfService["pageUrl"] to a publicly accessible URL in your LocalSettings.php! - if ( !isset( $electronPdfService["pageUrl"] ) ) { - $pageUrl = $title->getCanonicalURL(); - } else { - $pageUrl = $electronPdfService["pageUrl"]; - } - $serviceUrl = - $electronPdfService["serviceUrl"] . '/' . - $electronPdfService["format"] . - '?accessKey=' . $electronPdfService["key"] . - '&url=' . urlencode( $pageUrl ); + private function getServiceUrl( Title $title ) { + $restBaseUrl = $this->config->get( 'ElectronPdfServiceRESTbaseURL' ); - return $serviceUrl; + return $restBaseUrl . urlencode( $title->getPrefixedText() ); } /** - * @param string $page + * @param Title $title */ - private function sendPdfToOutput( $page ) { - $fileMetaData = stream_get_meta_data( $this->tempFileHandle ); - $contentDisposition = FileBackend::makeContentDisposition( 'inline', $page . '.pdf' ); - - $headers = [ - 'Content-Type:application/pdf', - 'Content-Length: ' . filesize( $fileMetaData['uri'] ), - 'Content-Disposition: ' . $contentDisposition - ]; - - StreamFile::stream( $fileMetaData['uri'], $headers ); + private function redirectToElectron( Title $title ) { + $this->getOutput()->redirect( + $this->getServiceUrl( $title ) + ); } /** * @param string $collectionDownloadUrl */ private function redirectToCollection( $collectionDownloadUrl ) { $queryString = parse_url( urldecode( $collectionDownloadUrl ), PHP_URL_QUERY ); parse_str( $queryString, $params ); unset( $params['title'] ); $this->getOutput()->redirect( wfAppendQuery( SkinTemplate::makeSpecialUrl( 'Book' ), http_build_query( $params ) ) ); } } diff --git a/tests/browser/features/support/pages/selectionscreen_page.rb b/tests/browser/features/support/pages/selectionscreen_page.rb index 7f9f5b3..d2a1be0 100644 --- a/tests/browser/features/support/pages/selectionscreen_page.rb +++ b/tests/browser/features/support/pages/selectionscreen_page.rb @@ -1,8 +1,8 @@ class SelectionScreenPage include PageObject div(:selectionscreen_header, css: '.mw-electronPdfService-selection-header') - radio_button(:singlecolumn_selection, css: '.mw-electronPdfService-selection-form [value=download-electron-pdf]') + radio_button(:singlecolumn_selection, css: '.mw-electronPdfService-selection-form [value=redirect-to-electron]') radio_button(:twocolumn_selection, css: '.mw-electronPdfService-selection-form [value=redirect-to-collection]') button(:pdf_download_button, css: '.mw-electronPdfService-selection-form .oo-ui-buttonElement-button') end