Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 121
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
CollectionRenderingAPI
0.00% covered (danger)
0.00%
0 / 121
0.00% covered (danger)
0.00%
0 / 11
992
0.00% covered (danger)
0.00%
0 / 1
 instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeRequest
n/a
0 / 0
n/a
0 / 0
0
 getBaseUrl
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 render
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 forceRender
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 doRender
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 postZip
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getRenderStatus
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 download
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getLicenseInfos
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 buildJsonCollection
0.00% covered (danger)
0.00%
0 / 63
0.00% covered (danger)
0.00%
0 / 1
272
1<?php
2
3namespace MediaWiki\Extension\Collection\Rendering;
4
5use FormatJson;
6use MediaWiki\MediaWikiServices;
7use RequestContext;
8
9/**
10 * Base class for API that interacts with book rendering service
11 */
12abstract class CollectionRenderingAPI {
13    /** @var self */
14    private static $inst;
15
16    /** @var string|false */
17    protected $writer;
18
19    /**
20     * @param string|false $writer Name of a writer, or false if none specified/needed.
21     *
22     * @return self
23     */
24    public static function instance( $writer = false ) {
25        if ( !self::$inst ) {
26            self::$inst = new MWServeRenderingAPI( $writer );
27        }
28        return self::$inst;
29    }
30
31    /**
32     * @param string|false $writer Name of a writer, or false if none specified/needed.
33     */
34    public function __construct( $writer ) {
35        $this->writer = $writer;
36    }
37
38    /**
39     * When overridden in derived class, performs a request to the service
40     *
41     * @param string $command
42     * @param array $params
43     * @return CollectionAPIResult
44     */
45    abstract protected function makeRequest( $command, array $params );
46
47    /**
48     * @return string Expanded wgScriptPath to work around T39868
49     */
50    private function getBaseUrl() {
51        global $wgScriptPath;
52
53        return wfExpandUrl( $wgScriptPath ?: '/', PROTO_CANONICAL );
54    }
55
56    /**
57     * Requests a collection to be rendered
58     * @param array $collection
59     *
60     * @return CollectionAPIResult
61     */
62    public function render( array $collection ) {
63        return $this->doRender( [
64                'metabook' => $this->buildJsonCollection( $collection ),
65            ]
66        );
67    }
68
69    /**
70     * Requests a queued collection to be immediately rendered
71     *
72     * @param string $collectionId
73     * @return CollectionAPIResult
74     */
75    public function forceRender( $collectionId ) {
76        return $this->doRender( [
77                'collection_id' => $collectionId,
78                'force_render' => true
79            ]
80        );
81    }
82
83    protected function doRender( array $params ) {
84        $params['base_url'] = $this->getBaseUrl();
85        $params['script_extension'] = '.php';
86        $params['language'] = MediaWikiServices::getInstance()->getContentLanguage()
87            ->getCode();
88        return $this->makeRequest( 'render', $params );
89    }
90
91    /**
92     * Requests the service to create a collection package and send it to an external server
93     * e.g. for printing
94     *
95     * @param array $collection
96     * @param string $url
97     *
98     * @return CollectionAPIResult
99     */
100    public function postZip( array $collection, $url ) {
101        return $this->makeRequest( 'zip_post',
102            [
103                'metabook' => $this->buildJsonCollection( $collection ),
104                'base_url' => $this->getBaseUrl(),
105                'script_extension' => '.php',
106                'pod_api_url' => $url,
107            ]
108        );
109    }
110
111    /**
112     * Returns information about a collection's rendering status
113     *
114     * @param string $collectionId
115     * @return CollectionAPIResult
116     */
117    public function getRenderStatus( $collectionId ) {
118        return $this->makeRequest(
119            'render_status',
120            [
121                'collection_id' => $collectionId,
122            ]
123        );
124    }
125
126    /**
127     * Requests a download of rendered collection
128     *
129     * @param string $collectionId
130     * @return CollectionAPIResult
131     */
132    public function download( $collectionId ) {
133        return $this->makeRequest( 'download',
134            [
135                'collection_id' => $collectionId,
136            ]
137        );
138    }
139
140    /**
141     * @return array
142     */
143    protected function getLicenseInfos() {
144        global $wgCollectionLicenseName, $wgCollectionLicenseURL, $wgRightsIcon;
145        global $wgRightsPage, $wgRightsText, $wgRightsUrl;
146
147        $licenseInfo = [
148            'type' => 'license',
149        ];
150
151        $fromMsg = wfMessage( 'coll-license_url' )->inContentLanguage();
152        if ( !$fromMsg->isDisabled() ) {
153            $licenseInfo['mw_license_url'] = $fromMsg->text();
154            return [ $licenseInfo ];
155        }
156
157        if ( $wgCollectionLicenseName ) {
158            $licenseInfo['name'] = $wgCollectionLicenseName;
159        } else {
160            $licenseInfo['name'] = wfMessage( 'coll-license' )->inContentLanguage()->text();
161        }
162
163        if ( $wgCollectionLicenseURL ) {
164            $licenseInfo['mw_license_url'] = $wgCollectionLicenseURL;
165        } else {
166            $licenseInfo['mw_rights_icon'] = $wgRightsIcon;
167            $licenseInfo['mw_rights_page'] = $wgRightsPage;
168            $licenseInfo['mw_rights_url'] = $wgRightsUrl;
169            $licenseInfo['mw_rights_text'] = $wgRightsText;
170        }
171
172        return [ $licenseInfo ];
173    }
174
175    /**
176     * @param array $collection
177     * @return string
178     */
179    protected function buildJsonCollection( array $collection ) {
180        $result = [
181            'type' => 'collection',
182            'licenses' => $this->getLicenseInfos()
183        ];
184
185        if ( isset( $collection['title'] ) ) {
186            $result['title'] = $collection['title'];
187        }
188        if ( isset( $collection['subtitle'] ) ) {
189            $result['subtitle'] = $collection['subtitle'];
190        }
191        if ( isset( $collection['settings'] ) ) {
192            foreach ( $collection['settings'] as $key => $val ) {
193                $result[$key] = $val;
194            }
195            // compatibility with old mw-serve
196            $result['settings'] = $collection['settings'];
197        }
198
199        $items = [];
200        if ( isset( $collection['items'] ) ) {
201            $currentChapter = null;
202            foreach ( $collection['items'] as $item ) {
203                if ( $item['type'] == 'article' ) {
204                    if ( $currentChapter === null ) {
205                        $items[] = $item;
206                    } else {
207                        $currentChapter['items'][] = $item;
208                    }
209                } elseif ( $item['type'] == 'chapter' ) {
210                    if ( $currentChapter !== null ) {
211                        $items[] = $currentChapter;
212                    }
213                    $currentChapter = $item;
214                }
215            }
216            if ( $currentChapter !== null ) {
217                $items[] = $currentChapter;
218            }
219        }
220        $result['items'] = $items;
221
222        $result['wikis'] = [
223            [
224                'type' => 'wikiconf',
225                'baseurl' => $this->getBaseUrl(),
226                'script_extension' => '.php',
227                'format' => 'nuwiki',
228            ],
229        ];
230
231        // Prefer VRS configuration if present.
232        $context = RequestContext::getMain();
233        $vrs = $context->getConfig()->get( 'VirtualRestConfig' );
234        if ( isset( $vrs['modules']['restbase']['url'] ) ) {
235            // if restbase is available, use it
236            $params = $vrs['modules']['restbase'];
237            $domain = preg_replace(
238                '/^(https?:\/\/)?([^\/:]+?)(\/|:\d+\/?)?$/',
239                '$2',
240                $params['domain'] ?? 'localhost'
241            );
242            $url = preg_replace(
243                '#/?$#',
244                '/' . $domain . '/v1/',
245                $params['url']
246            );
247            for ( $i = 0, $count = count( $result['wikis'] ); $i < $count; $i++ ) {
248                $result['wikis'][$i]['restbase1'] = $url;
249            }
250        } elseif ( isset( $vrs['modules']['parsoid']['url'] ) ) {
251            // there's a global parsoid config, use it next
252            $params = $vrs['modules']['parsoid'];
253            $domain = preg_replace(
254                '/^(https?:\/\/)?([^\/:]+?)(\/|:\d+\/?)?$/',
255                '$2',
256                $params['domain'] ?? 'localhost'
257            );
258            for ( $i = 0, $count = count( $result['wikis'] ); $i < $count; $i++ ) {
259                $result['wikis'][$i]['parsoid'] = $params['url'];
260                $result['wikis'][$i]['prefix'] = $params['prefix'] ?? null;
261                $result['wikis'][$i]['domain'] = $domain;
262            }
263        }
264
265        return FormatJson::encode( $result );
266    }
267}