r36747 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r36746‎ | r36747 | r36748 >
Date:14:08, 27 June 2008
Author:jojo
Status:old
Tags:
Comment:
Generalize code to allow other output formats than just PDF. Includes fix for problem with pdfserver.py on Windows, reported by kometa_triatlon.
Modified paths:
  • /trunk/extensions/Collection/Collection.body.php (modified) (history)
  • /trunk/extensions/Collection/Collection.i18n.php (modified) (history)
  • /trunk/extensions/Collection/Collection.php (modified) (history)
  • /trunk/extensions/Collection/pdf-server/pdfserver.py (modified) (history)

Diff [purge]

Index: trunk/extensions/Collection/Collection.i18n.php
@@ -38,6 +38,7 @@
3939 'coll-page' => 'page',
4040 'coll-pages' => 'pages',
4141 'coll-download_as_pdf' => 'Download as PDF',
 42+ 'coll-download_as_odf' => 'Download as ODF',
4243 'coll-noscript_text' => '<h1>JavaScript is Required!</h1>
4344 <strong>Your browser does not support JavaScript or JavaScript has been turned off.
4445 This page will not work correctly, unless JavaScript is enabled.</strong>',
@@ -45,7 +46,7 @@
4647
4748 See the [[{{MediaWiki:Coll-helppage}}|help page about collections]] for more information.",
4849 'coll-helppage' => 'Help:Collections',
49 - 'coll-pdftoobigcat' => 'The category contains more than %PARAM% pages, only the first %PARAM% pages can be added to your collection.
 50+ 'coll-too_big_cat' => 'The category contains more than %PARAM% pages, only the first %PARAM% pages can be added to your collection.
5051 Do you want to add them?',
5152 'coll-my_collection' => 'My Collection',
5253 'coll-download_title' => 'Download collection as PDF',
@@ -96,24 +97,22 @@
9798 'coll-limit_exceeded_title' => 'Collection too big',
9899 'coll-limit_exceeded_text' => 'Your page collection is too big.
99100 No more pages can be added.',
100 - 'coll-generating_pdf_title' => 'Generating PDF',
101 - 'coll-generating_pdf_text' => "'''Please wait while the PDF file is being generated.'''
 101+ 'coll-rendering_title' => 'Rendering',
 102+ 'coll-rendering_text' => "'''Please wait while the document is being generated.'''
102103
103104 Progress: '''$1%'''.
104105
105106 This page should automatically refresh every few seconds.
106107 If this does not work, please press refresh button of your browser.",
107 - 'coll-pdf_finished_title' => 'PDF Generation Finished',
108 - 'coll-pdf_finished_text' => "'''The PDF file has been generated.'''
 108+ 'coll-rendering_finished_title' => 'Rendering Finished',
 109+ 'coll-rendering_finished_text' => "'''The document file has been generated.'''
109110 [$1 Click here] to download it to your computer.
110111
111 -Not satisfied with the PDF output?
 112+Not satisfied with the output?
112113 See [[{{MediaWiki:Coll-helppage}}|the help page about collections]] for possibilities to improve it.",
113114 'coll-notfound_title' => 'Collection not found',
114115 'coll-notfound_text' => 'Could not find collection page.',
115116 'coll-return_to_collection' => 'Return to <a href="$1">$2</a>',
116 - 'coll-pdf_error_text' => 'Error in PDF Generation',
117 - 'coll-pdf_error_text' => 'There was an error when generating the PDF file: $1',
118117 'coll-book_title' => 'Order printed book',
119118 'coll-book_text' => 'You can order a printed book containing your page collection by visiting one of the following print-on-demand partners:',
120119 'coll-order_from_pp' => 'Order book from $1',
Index: trunk/extensions/Collection/Collection.php
@@ -55,6 +55,9 @@
5656 /** Template blacklist article */
5757 $wgPDFTemplateBlacklist = 'MediaWiki:PDF Template Blacklist';
5858
 59+/** Add experimental support to output ODF documents (in addition to PDFs) */
 60+$wgCollectionUseODF = false;
 61+
5962 # ==============================================================================
6063
6164
Index: trunk/extensions/Collection/Collection.body.php
@@ -160,7 +160,7 @@
161161 $this->outputSaveOverwrite( $title );
162162 }
163163 return;
164 - } else if ( $par == 'generate_pdf/' ) {
 164+ } else if ( $par == 'render/' ) {
165165 $title = $wgRequest->getVal( 'downloadTitle', '' );
166166 if ( $title ) {
167167 $_SESSION['wsCollection']['title'] = $title;
@@ -169,21 +169,28 @@
170170 if ( $subtitle ) {
171171 $_SESSION['wsCollection']['subtitle'] = $subtitle;
172172 }
173 - return $this->generatePDF();
174 - } else if ( $par == 'generating_pdf/' ) {
175 - return $this->generatingPDF();
176 - } else if ( $par == 'download_pdf/' ) {
177 - return $this->downloadPDF();
178 - } else if ( $par == 'download_article_pdf/' ) {
 173+ return $this->renderCollection(
 174+ $_SESSION['wsCollection'],
 175+ Title::makeTitle( NS_SPECIAL, 'Collection' ),
 176+ $wgRequest->getVal( 'downloadWriter', '' )
 177+ );
 178+ } else if ( $par == 'rendering/' ) {
 179+ return $this->rendering();
 180+ } else if ( $par == 'download/' ) {
 181+ return $this->download();
 182+ } else if ( $par == 'render_article/' ) {
179183 $title = Title::newFromText( $wgRequest->getVal( 'arttitle', '' ) );
180184 $oldid = $wgRequest->getInt( 'oldid', 0 );
181 - return $this->downloadArticlePDF( $title, $oldid );
182 - } else if ( $par == 'download_collection_pdf/' ) {
 185+ return $this->renderArticle( $title, $oldid, $wgRequest->getVal( 'writer', 'rl' ) );
 186+ } else if ( $par == 'render_collection/' ) {
183187 $title = Title::newFromText( $wgRequest->getVal( 'colltitle', '' ) );
184 - return $this->downloadCollectionPDF( $title );
185 - } else if ( $par == 'post_pdf/' ) {
 188+ $collection = $this->loadCollection( $title );
 189+ if ( $collection ) {
 190+ $this->renderCollection( $collection, $title, $wgRequest->getVal( 'writer', 'rl' ) );
 191+ }
 192+ } else if ( $par == 'post_zip/' ) {
186193 $partner = $wgRequest->getVal( 'partner', '' );
187 - return $this->postPDF( $partner );
 194+ return $this->postZIP( $partner );
188195 }
189196
190197 $this->setHeaders();
@@ -512,31 +519,34 @@
513520 return $json->encode( $result );
514521 }
515522
516 - function generatePDFFromCollection( $collection, $referrer ) {
 523+ function renderCollection( $collection, $referrer, $writer ) {
517524 global $wgOut;
518525 global $wgPDFTemplateBlacklist;
519526 global $wgServer;
520527 global $wgScriptPath;
521528
 529+ if ( !$writer ) {
 530+ $writer = 'rl';
 531+ }
 532+
522533 $response = self::pdfServerCommand( 'render', array(
523534 'metabook' => $this->buildJSONCollection( $collection ),
524535 'base_url' => $wgServer . $wgScriptPath,
525536 'template_blacklist' => $wgPDFTemplateBlacklist,
526 - 'writer' => 'rl',
527 - 'content_type' => 'application/pdf',
 537+ 'writer' => $writer,
528538 ) );
529539
530540 if ( !$response ) {
531541 return;
532542 }
533543
534 - $redirect = SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'generating_pdf/' );
 544+ $redirect = SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'rendering/' );
535545 $wgOut->redirect( wfAppendQuery( $redirect,
536546 'return_to=' . urlencode( $referrer->getPrefixedText() )
537547 . '&collection_id=' . urlencode( $response->collection_id ) ) );
538548 }
539549
540 - function generatingPDF() {
 550+ function rendering() {
541551 global $wgOut;
542552 global $wgRequest;
543553 global $wgServer;
@@ -560,20 +570,20 @@
561571 $params .= '&return_to=' . urlencode( $wgRequest->getVal( 'return_to' ) );
562572 }
563573 $url = wfAppendQuery(
564 - SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'generating_pdf/' ),
 574+ SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'rendering/' ),
565575 $params
566576 );
567577 $wgOut->addMeta( 'http:refresh', '2; URL=' . $url );
568 - $wgOut->setPageTitle( wfMsg( 'coll-generating_pdf_title' ) );
569 - $wgOut->addWikiText( wfMsgNoTrans( 'coll-generating_pdf_text', $response->status->progress ) );
 578+ $wgOut->setPageTitle( wfMsg( 'coll-rendering_title' ) );
 579+ $wgOut->addWikiText( wfMsgNoTrans( 'coll-rendering_text', $response->status->progress ) );
570580 break;
571581 case 'finished':
572 - $wgOut->setPageTitle( wfMsg( 'coll-pdf_finished_title' ) );
 582+ $wgOut->setPageTitle( wfMsg( 'coll-rendering_finished_title' ) );
573583 $url = wfAppendQuery(
574 - SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'download_pdf/' ),
 584+ SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'download/' ),
575585 'collection_id=' . urlencode( $response->collection_id )
576586 );
577 - $wgOut->addWikiText( wfMsgNoTrans( 'coll-pdf_finished_text', $wgServer . $url ) );
 587+ $wgOut->addWikiText( wfMsgNoTrans( 'coll-rendering_finished_text', $wgServer . $url ) );
578588 if ( $return_to ) {
579589 // We are doing this the hard way (i.e. via the HTML detour), to prevent
580590 // the parser from replacing [[:Special:Collection]] with a selflink.
@@ -590,7 +600,7 @@
591601 }
592602 }
593603
594 - function downloadPDF() {
 604+ function download() {
595605 global $wgOut;
596606 global $wgRequest;
597607
@@ -605,23 +615,20 @@
606616 // Cancel output buffering and gzipping if set
607617 wfResetOutputBuffers();
608618
609 - header( 'Content-type: application/pdf');
610 - header( 'Content-Disposition: inline;filename=mw.pdf' );
611 - header( 'Content-Length: ' . strlen( $response ) );
612 - print $response;
 619+ if ( isset( $response['headers']['content-type'] ) ) {
 620+ header( 'Content-type: ' . $response['headers']['content-type']);
 621+ }
 622+ if ( isset( $reponse['headers']['content-disposition'] ) ) {
 623+ header( 'Content-Disposition: ' . $response['headers']['content-disposition']);
 624+ }
 625+ header( 'Content-Length: ' . strlen( $response['content'] ) );
 626+ print $response['content'];
613627 $wgOut->disable();
614628 }
615629
616 - function generatePDF() {
617 - $this->generatePDFFromCollection(
618 - $_SESSION['wsCollection'],
619 - Title::makeTitle( NS_SPECIAL, 'Collection' )
620 - );
621 - }
622 -
623 - function downloadArticlePDF( $title, $oldid=0 ) {
 630+ function renderArticle( $title, $oldid, $writer ) {
624631 global $wgOut;
625 -
 632+
626633 if ( is_null( $title ) ) {
627634 $wgOut->showErrorPage( 'notitle_title', 'notitle_msg' );
628635 return;
@@ -638,17 +645,10 @@
639646 $revision = Revision::newFromTitle( $title, $oldid );
640647 $article['timestamp'] = wfTimestamp( TS_UNIX, $revision->mTimestamp );
641648
642 - $this->generatePDFFromCollection( array( 'items' => array( $article ) ), $title );
 649+ $this->renderCollection( array( 'items' => array( $article ) ), $title, $writer );
643650 }
644651
645 - function downloadCollectionPDF( $title ) {
646 - $collection = $this->loadCollection( $title );
647 - if ( $collection ) {
648 - $this->generatePDFFromCollection( $collection, $title );
649 - }
650 - }
651 -
652 - function postPDF( $partner ) {
 652+ function postZIP( $partner ) {
653653 global $wgServer;
654654 global $wgScriptPath;
655655 global $wgOut;
@@ -663,9 +663,10 @@
664664
665665 $url = $this->mPODPartners[$partner]['posturl'];
666666 $errorMessage = '';
667 - $response = self::post( $url, array(), $errorMessage );
 667+ $contentType = '';
 668+ $response = self::post( $url, array(), $errorMessage, $contentType );
668669 if ( !$response ) {
669 - $wgOut->showErrorPage( 'coll-post_failed_title', 'coll-post_failed_msg', array( $url, $errorMessage ) );
 670+ $wgOut->showErrorPage( 'coll-post_failed_title', 'coll-post_failed_msg', array( $url, $errorMessage ) );
670671 return;
671672 }
672673 $postData = $json->decode( $response );
@@ -783,7 +784,7 @@
784785 $downloadTitle = wfMsgHtml( 'coll-download_title' );
785786 $downloadText = wfMsgHtml( 'coll-download_text' );
786787 $buttonLabel = wfMsgHtml( 'coll-download_pdf' );
787 - $url = htmlspecialchars( SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'generate_pdf/' ) );
 788+ $url = htmlspecialchars( SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'render/' ) );
788789 $html = <<<EOS
789790 <h2><span class="mw-headline">$downloadTitle</span></h2>
790791 <p>$downloadText</p>
@@ -905,7 +906,7 @@
906907 ;
907908
908909 foreach( $this->mPODPartners as $partner => $partnerData ) {
909 - $formurl = htmlspecialchars( SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'post_pdf/' ) );
 910+ $formurl = htmlspecialchars( SkinTemplate::makeSpecialUrlSubpage( 'Collection', 'post_zip/' ) );
910911 $encPartner = htmlspecialchars( $partner );
911912 $url = htmlspecialchars( $partnerData['url'] );
912913 $logoURL = htmlspecialchars( $partnerData['logourl'] );
@@ -949,6 +950,7 @@
950951 function createNavURLs( &$skinTemplate, &$nav_urls, &$revid1, &$revid2 ) {
951952 global $wgArticle;
952953 global $wgRequest;
 954+ global $wgCollectionUseODF;
953955
954956 wfLoadExtensionMessages( 'Collection' );
955957
@@ -960,10 +962,19 @@
961963 $nav_urls['download_as_pdf'] = array(
962964 'href' => wfAppendQuery( SkinTemplate::makeSpecialUrlSubpage(
963965 'Collection',
964 - 'download_collection_pdf/'
 966+ 'render_collection/'
965967 ), $params ),
966968 'text' => wfMsg( 'coll-download_as_pdf' ),
967969 );
 970+ if ( $wgCollectionUseODF ) {
 971+ $nav_urls['download_as_odf'] = array(
 972+ 'href' => wfAppendQuery( SkinTemplate::makeSpecialUrlSubpage(
 973+ 'Collection',
 974+ 'render_collection/'
 975+ ), $params . '&writer=odf' ),
 976+ 'text' => wfMsg( 'coll-download_as_odf' ),
 977+ );
 978+ }
968979 } else {
969980 $params = 'arttitle=' . $skinTemplate->mTitle->getPrefixedURL();
970981 if( $wgArticle ) {
@@ -975,10 +986,19 @@
976987 $nav_urls['download_as_pdf'] = array(
977988 'href' => wfAppendQuery( SkinTemplate::makeSpecialUrlSubpage(
978989 'Collection',
979 - 'download_article_pdf/'
 990+ 'render_article/'
980991 ), $params ),
981992 'text' => wfMsg( 'coll-download_as_pdf' )
982993 );
 994+ if ( $wgCollectionUseODF ) {
 995+ $nav_urls['download_as_odf'] = array(
 996+ 'href' => wfAppendQuery( SkinTemplate::makeSpecialUrlSubpage(
 997+ 'Collection',
 998+ 'render_article/'
 999+ ), $params . '&writer=odf' ),
 1000+ 'text' => wfMsg( 'coll-download_as_odf' )
 1001+ );
 1002+ }
9831003 }
9841004 }
9851005 return true;
@@ -997,6 +1017,14 @@
9981018 EOS
9991019 ;
10001020 }
 1021+ if ( !empty( $skinTemplate->data['nav_urls']['download_as_odf']['href'] ) ) {
 1022+ $href = htmlspecialchars( $skinTemplate->data['nav_urls']['download_as_odf']['href'] );
 1023+ $label = htmlspecialchars( $skinTemplate->data['nav_urls']['download_as_odf']['text'] );
 1024+ print <<<EOS
 1025+<li id="t-pdf"><a href="$href">$label</a></li>
 1026+EOS
 1027+ ;
 1028+ }
10011029 return true;
10021030 }
10031031
@@ -1068,7 +1096,7 @@
10691097 # disable caching
10701098 $wgOut->setSquidMaxage( 0 );
10711099 $wgOut->enableClientCache( false );
1072 - }
 1100+ }
10731101 if ( $numArticles == 1 ){
10741102 $articles = $numArticles . ' ' . wfMsgHtml( 'coll-page' );
10751103 } else {
@@ -1078,7 +1106,7 @@
10791107 $showURL = htmlspecialchars( SkinTemplate::makeSpecialUrl( 'Collection') );
10801108 print <<<EOS
10811109 <li><a href="$showURL">$showCollection<br />
1082 - ($articles)</a></li>
 1110+ ($articles)</a></li>
10831111 EOS
10841112 ;
10851113 $helpCollections = wfMsgHtml( 'coll-help_collections' );
@@ -1099,7 +1127,8 @@
11001128
11011129 $args['command'] = $command;
11021130 $errorMessage = '';
1103 - $response = self::post( $wgPDFServer, $args, $errorMessage );
 1131+ $headers = array();
 1132+ $response = self::post( $wgPDFServer, $args, $errorMessage, $headers );
11041133 if ( !$response ) {
11051134 $wgOut->showErrorPage(
11061135 'coll-post_failed_title',
@@ -1110,7 +1139,10 @@
11111140 }
11121141
11131142 if (!$decode) {
1114 - return $response;
 1143+ return array(
 1144+ 'content' => $response,
 1145+ 'headers' => $headers,
 1146+ );
11151147 }
11161148
11171149 $json = new Services_JSON();
@@ -1137,7 +1169,7 @@
11381170 return $json_response;
11391171 }
11401172
1141 - static function post( $url, $postFields, &$errorMessage ) {
 1173+ static function post( $url, $postFields, &$errorMessage, &$headers ) {
11421174 global $wgHTTPTimeout, $wgHTTPProxy, $wgVersion, $wgTitle;
11431175
11441176 $c = curl_init( $url );
@@ -1146,27 +1178,32 @@
11471179 curl_setopt( $c, CURLOPT_POST, true );
11481180 curl_setopt( $c, CURLOPT_POSTFIELDS, $postFields );
11491181 curl_setopt( $c, CURLOPT_HTTPHEADER, array( 'Expect:' ) );
1150 -
11511182 if ( is_object( $wgTitle ) ) {
11521183 curl_setopt( $c, CURLOPT_REFERER, $wgTitle->getFullURL() );
11531184 }
1154 -
1155 - ob_start();
1156 - curl_exec( $c );
1157 - $text = ob_get_contents();
1158 - ob_end_clean();
 1185+ curl_setopt( $c, CURLOPT_HEADER, true );
 1186+ curl_setopt( $c, CURLOPT_RETURNTRANSFER, true );
11591187
1160 - $errorMessage = '';
 1188+ $response = curl_exec( $c );
11611189
1162 - if ( curl_getinfo( $c, CURLINFO_HTTP_CODE ) != 200 ) {
1163 - $text = false;
1164 - $errorMessage = 'HTTP status ' . curl_getinfo( $c, CURLINFO_HTTP_CODE );
1165 - }
1166 - # Don't return truncated output
11671190 if ( curl_errno( $c ) != CURLE_OK ) {
11681191 $text = false;
11691192 $errorMessage = curl_error( $c );
1170 - }
 1193+ } else if ( curl_getinfo( $c, CURLINFO_HTTP_CODE ) != 200 ) {
 1194+ $text = false;
 1195+ $errorMessage = 'HTTP status ' . curl_getinfo( $c, CURLINFO_HTTP_CODE );
 1196+ } else {
 1197+ $headerSize = curl_getinfo( $c, CURLINFO_HEADER_SIZE );
 1198+ $headerLines = explode( "\n", substr( $response, 0, $headerSize ) );
 1199+ foreach( $headerLines as $line ) {
 1200+ if ( preg_match( "/^(.+?):\s+(.+)$/", trim( $line ), $matches ) ) {
 1201+ $headers[ strtolower( $matches[1] ) ] = $matches[2];
 1202+ }
 1203+ unset( $matches );
 1204+ }
 1205+ $text = substr( $response, $headerSize );
 1206+ $errorMessage = '';
 1207+ }
11711208 curl_close( $c );
11721209 return $text;
11731210 }
Index: trunk/extensions/Collection/pdf-server/pdfserver.py
@@ -60,7 +60,6 @@
6161
6262 class PDFServer(object):
6363 metabook_filename = 'metabook.json'
64 - contenttype_filename = 'content_type.txt'
6564 error_filename = 'errors.txt'
6665 status_filename = 'status.txt'
6766 output_filename = 'output'
@@ -129,6 +128,8 @@
130129
131130 if 'content_type' in result:
132131 print 'Content-Type: %s' % result['content_type']
 132+ if 'file_extension' in result:
 133+ print 'Content-Disposition: inline;filename="collection.%s"' % result['file_extension']
133134 content = result.get('content', '')
134135 print 'Content-Length: %d' % len(content)
135136 print # end of headers
@@ -144,9 +145,6 @@
145146 writer = self.form.getvalue('writer')
146147 if not writer:
147148 return self.error_response('writer argument required')
148 - content_type = self.form.getvalue('content_type')
149 - if not content_type:
150 - return self.error_response('content_type argument required')
151149 writer_options = self.form.getvalue('writer_options')
152150 template_blacklist = self.form.getvalue('template_blacklist')
153151
@@ -157,11 +155,6 @@
158156 f.write(metabook_data)
159157 f.close()
160158
161 - contenttype_path = self.get_path(collection_id, self.contenttype_filename)
162 - f = open(contenttype_path, 'wb')
163 - f.write(content_type)
164 - f.close()
165 -
166159 args=[
167160 mwrender_cmd,
168161 '--daemonize',
@@ -188,6 +181,14 @@
189182 return self.json_response({
190183 'collection_id': collection_id,
191184 })
 185+
 186+ def read_status_file(self, collection_id):
 187+ try:
 188+ f = open(self.get_path(collection_id, self.status_filename), 'rb')
 189+ return simplejson.loads(f.read())
 190+ f.close()
 191+ except (IOError, ValueError):
 192+ return {'progress': 0}
192193
193194 def do_render_status(self):
194195 collection_id = self.get_collection()
@@ -207,26 +208,24 @@
208209 'error': text,
209210 })
210211
211 - try:
212 - f = open(self.get_path(collection_id, self.status_filename), 'rb')
213 - status = simplejson.loads(f.read())
214 - f.close()
215 - except (IOError, ValueError):
216 - status = {'progress': 0}
217212 return self.json_response({
218213 'collection_id': collection_id,
219214 'state': 'progress',
220 - 'status': status,
 215+ 'status': self.read_status_file(collection_id),
221216 })
222 -
 217+
223218 def do_download(self):
224219 collection_id = self.get_collection()
225 - content_type = open(self.get_path(collection_id, self.contenttype_filename), 'rb').read()
226220 content = open(self.get_path(collection_id, self.output_filename), 'rb').read()
227 - return {
228 - 'content_type': content_type,
 221+ status = self.read_status_file(collection_id)
 222+ result = {
229223 'content': content,
230224 }
 225+ if 'content_type' in status:
 226+ result['content_type'] = status['content_type']
 227+ if 'file_extension' in status:
 228+ result['file_extension'] = status['file_extension']
 229+ return result
231230
232231 def do_zip_post(self):
233232 metabook_data = self.form.getvalue('metabook')
@@ -266,4 +265,7 @@
267266
268267
269268 if __name__ == '__main__':
 269+ if os.name == 'nt':
 270+ import msvcrt
 271+ msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
270272 PDFServer(cgi.FieldStorage()).dispatch()

Status & tagging log