GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( 328c62...8f9f4a )
by Miguel Angel
04:44
created

removeUnneededFootnotesItem()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.3245

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 24
ccs 8
cts 11
cp 0.7273
rs 8.6846
cc 4
eloc 9
nc 4
nop 0
crap 4.3245
1
<?php
2
/*
3
 * This file is part of the trefoil application.
4
 *
5
 * (c) Miguel Angel Gabriel <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace Trefoil\Plugins\Optional;
11
12
use Easybook\Events\EasybookEvents;
13
use Easybook\Events\ParseEvent;
14
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
15
use Trefoil\Plugins\BasePlugin;
16
use Trefoil\Util\Toolkit;
17
18
/**
19
 * This plugin extends footnotes to support several formats.
20
 *
21
 * - type 'end': This is the normal Markdown-rendered footnotes.
22
 *   They will be at the end of each book item, separated by a <hr/> tag.
23
 *   This is the default.
24
 *
25
 * - type 'inject: This is a variant of thpe 'end', where each item's
26
 *   footnotes will be injected to a certain injection point.
27
 *   Just write '<div class="footnotes"></div>' anywhere in each item
28
 *   where the footnotes should be injected.
29
 *
30
 * - type 'item': All the footnotes in the book will be collected and
31
 *   rendered in a separated item called 'footnotes' that need to
32
 *   exist in the book.
33
 *
34
 * - type 'inline: PrinceXML support inline footnotes, where the text
35
 *   of the note must be inlined into the text, instead of just a
36
 *   reference. Prince will manage the numbering.   
37
 */
38
class FootnotesExtendPlugin extends BasePlugin implements EventSubscriberInterface
39
{
40
    const FOOTNOTES_TYPE_END = 'end';
41
    const FOOTNOTES_TYPE_ITEM = 'item';
42
    const FOOTNOTES_TYPE_INJECT = 'inject';
43
    const FOOTNOTES_TYPE_INLINE = 'inline';
44
45
    /**
46
     * @var string Type of footnotes to generate
47
     */
48
    protected $footnotesType = '';
49
50
    /**
51
     * @var array The extracted footnotes the current book item
52
     */
53
    protected $footnotesCurrentItem = array();
54
    
55
    /**
56
     * @var string The current item footnotes (as text)
57
     */
58
    protected $itemFootnotesText = '';
59
60
    /* ********************************************************************************
61
     * Event handlers
62
     * ********************************************************************************
63
     */
64 4
    public static function getSubscribedEvents()
65
    {
66
        return array(
67 4
            EasybookEvents::POST_PARSE => array('onItemPostParse')
68 4
        );
69
    }
70
71 4
    public function onItemPostParse(ParseEvent $event)
72
    {
73 4
        $this->init($event);
74
75 4
        $this->processItem();
76
77
        // reload changed item
78 4
        $event->setItem($this->item);
79 4
    }
80
81
    /* ********************************************************************************
82
     * Implementation
83
     * ********************************************************************************
84
     */
85
86
    /**
87
     * Process a content item
88
     */
89 4
    protected function processItem()
90
    {
91
        // lazy initialize
92 4
        if (!isset($this->app['publishing.footnotes.items'])) {
93 4
            $this->app['publishing.footnotes.items'] = array();
94 4
        }
95
96 4
        $this->footnotesCurrentItem = array();
97
98
        // options
99 4
        $this->footnotesType = $this->getEditionOption('plugins.options.FootnotesExtend.type', 'end');
100
101 4
        $this->fixFootnotes();
102
103 4
        switch ($this->footnotesType) {
104 4
            case self::FOOTNOTES_TYPE_END:
105
                // nothing else to do
106 1
                break;
107
108 3
            case self::FOOTNOTES_TYPE_INLINE:
109
110 1
                $this->extractFootnotes();
111 1
                $this->inlineFootnotes();
112 1
                break;
113
114 2
            case self::FOOTNOTES_TYPE_ITEM:
115
116 1
                $this->extractFootnotes();
117 1
                $this->renumberReferences();
118 1
                break;
119
120 1
            case self::FOOTNOTES_TYPE_INJECT:
121
122 1
                $this->saveInjectionTarget();
123 1
                $this->extractFootnotes();
124 1
                $this->restoreInjectionTarget();
125 1
                $this->injectFootnotes();
126 1
                break;
127 4
        }
128
129
        // look if we need to remove the footnotes book item
130 4
        $this->removeUnneededFootnotesItem();
131 4
    }
132
133
    /**
134
     * Replace character ':' by '-' in footnotes ids because epubcheck does not like it.
135
     */
136 4 View Code Duplication
    protected function fixFootnotes()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137
    {
138 4
        $content = $this->item['content'];
139
140
        // fix footnotes ref in text
141 4
        $content = preg_replace('/id="fnref(\d*):/', 'id="fnref$1-', $content);
142 4
        $content = str_replace('href="#fn:', 'href="#fn-', $content);
143
144
        // fix footnotes
145 4
        $content = str_replace('id="fn:', 'id="fn-', $content);
146 4
        $content = preg_replace('/href="#fnref(\d*):/', 'href="#fnref$1-', $content);
147
148
        // fix return sign used
149 4
        $content = str_replace('&#8617;', '[&crarr;]', $content);
150
151 4
        $this->item['content'] = $content;
152 4
    }
153
154
    /**
155
     * Extracts anc collects all footnotes in the item.
156
     */
157 3
    protected function extractFootnotes()
158
    {
159 3
        $content = $this->item['content'];
160
161 3
        $this->itemFootnotesText = '';
162
163 3
        $regExp = '/';
164 3
        $regExp .= '<div class="footnotes">.*<ol>(?<fns>.*)<\/ol>.*<\/div>';
165 3
        $regExp .= '/Ums'; // Ungreedy, multiline, dotall
166
167
        // PHP 5.3 compat
168 3
        $me = $this;
169
170 3
        $content = preg_replace_callback(
171 3
            $regExp,
172
            function ($matches) use ($me) {
173
174 3
                $this->itemFootnotesText = $matches[0];
175
176 3
                $regExp2 = '/';
177 3
                $regExp2 .= '<li.*id="(?<id>.*)">.*';
178 3
                $regExp2 .= '<p>(?<text>.*)&#160;<a .*href="#(?<backref>.*)"';
179 3
                $regExp2 .= '/Ums'; // Ungreedy, multiline, dotall
180
181 3
                preg_match_all($regExp2, $matches[0], $matches2, PREG_SET_ORDER);
182
183 3
                if ($matches2) {
184 3
                    foreach ($matches2 as $match2) {
185
                        $footnote = array(
186 3
                            'item'       => $this->item['toc'][0]['slug'],
187 3
                            'text'       => $match2['text'],
188 3
                            'id'         => $match2['id'],
189 3
                            'text'       => $match2['text'],
190 3
                            'backref'    => $match2['backref'],
191 3
                            'new_number' => count($this->app['publishing.footnotes.items']) + 1
192 3
                        );
193
194
                        // save for current item
195 3
                        $this->footnotesCurrentItem[$match2['id']] = $footnote;
196
197
                        // save for all items
198 3
                        $footnotes = $this->app['publishing.footnotes.items'];
199 3
                        $footnotes[$match2['id']] = $footnote;
200 3
                        $this->app['publishing.footnotes.items'] = $footnotes;
201 3
                    }
202 3
                }
203
204 3
                return '';
205 3
            },
206
            $content
207 3
        );
208
209 3
        $this->item['content'] = $content;
210 3
    }
211
212
    /**
213
     * Inline footnotes in the text, after the note reference.
214
     *
215
     * This is only useful for renderers that support automatic
216
     * inline footnotes, like PrinceXML.
217
     */
218 1 View Code Duplication
    protected function inlineFootnotes()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
219
    {
220 1
        $content = $this->item['content'];
221
222 1
        $regExp = '/';
223 1
        $regExp .= '<sup id="(?<supid>fnref.?-.*)">';
224 1
        $regExp .= '<a(?<prev>.*)href="#(?<href>fn-.*)"(?<post>.*)>(?<number>.*)<\/a><\/sup>';
225 1
        $regExp .= '/Ums'; // Ungreedy, multiline, dotall
226
227
        // PHP 5.3 compat
228 1
        $me = $this;
229
230 1
        $content = preg_replace_callback(
231 1
            $regExp,
232
            function ($matches) use ($me) {
233 1
                $footnotes = $me->footnotesCurrentItem;
234 1
                $footnote = $footnotes[$matches['href']];
235 1
                $text = $footnote['text'];
236
237
                // replace <p>...</p> with <br/> because no block elements are 
238
                // allowed inside a <span>.
239
                // The paragraph contents are also put inside a fake paragraph <span>
240
                // so they can be styled.
241
242 1
                $text = str_replace(
243 1
                    ['<p>', '</p>'],
244 1
                    ['<span class="p">', '<br/></span>'],
245
                    $text
246 1
                );
247 1
                $text = '<span class="p" >' . $text . '</span>';
248
249 1
                $html = sprintf(
250 1
                    '<span class="fn">%s</span>',
251
                    $text
252 1
                );
253
254 1
                return $html;
255 1
            },
256
            $content
257 1
        );
258
259 1
        $this->item['content'] = $content;
260 1
    }
261
262
    /**
263
     * Renumber all footnotes references to be correlative for the whole book.
264
     */
265 1
    protected function renumberReferences()
266
    {
267 1
        $content = $this->item['content'];
268
269 1
        $regExp = '/';
270 1
        $regExp .= '<sup id="(?<supid>fnref.?-.*)">';
271 1
        $regExp .= '<a(?<prev>.*)href="#(?<href>fn-.*)"(?<post>.*)>(?<number>.*)<\/a>';
272 1
        $regExp .= '/Ums'; // Ungreedy, multiline, dotall
273
274
        // PHP 5.3 compat
275 1
        $me = $this;
276
277 1
        $content = preg_replace_callback(
278 1
            $regExp,
279
            function ($matches) use ($me) {
280 1
                $newNumber = $this->app['publishing.footnotes.items'][$matches['href']]['new_number'];
281
282 1
                $html = sprintf(
283 1
                    '<sup id="%s"><a%shref="#%s"%s>%s</a>',
284 1
                    $matches['supid'],
285 1
                    $matches['prev'],
286 1
                    $matches['href'],
287 1
                    $matches['post'],
288
                    $newNumber
289 1
                );
290
291 1
                return $html;
292 1
            },
293
            $content
294 1
        );
295
296 1
        $this->item['content'] = $content;
297 1
    }
298
299
    /**
300
     * Replace the footnotes injection target to keep extractFootnotes() from removing it.
301
     */
302 1 View Code Duplication
    protected function saveInjectionTarget()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
303
    {
304 1
        $content = $this->item['content'];
305
306 1
        $content = preg_replace('/<div class="footnotes">\s*<\/div>/', '<div class="__footnotes"></div>', $content);
307
308 1
        $this->item['content'] = $content;
309 1
    }
310
311
    /**
312
     * Restore the injection target
313
     */
314 1 View Code Duplication
    protected function restoreInjectionTarget()
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
315
    {
316 1
        $content = $this->item['content'];
317
318 1
        $content = preg_replace('/<div class="__footnotes">\s*<\/div>/', '<div class="footnotes"></div>', $content);
319
320 1
        $this->item['content'] = $content;
321 1
    }
322
323
    /**
324
     * Inject footnotes at the injection target.
325
     * 
326
     * The injection target is a '<div class="footnotes"></div>' placed anywhere in the item text.
327
     */
328 1
    protected function injectFootnotes()
329
    {
330 1
        $content = $this->item['content'];
331
332 1
        $regExp = '/';
333 1
        $regExp .= '<div class="footnotes">\s*<\/div>';
334 1
        $regExp .= '/Ums'; // Ungreedy, multiline, dotall
335
336
        // PHP 5.3 compat
337 1
        $me = $this;
338
339 1
        $content = preg_replace_callback(
340 1
            $regExp,
341 1
            function ($matches) use ($me) {
0 ignored issues
show
Unused Code introduced by
The parameter $matches is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
342
343 1
                $footnotes = $this->app->render(
344 1
                    '_footnotes.twig',
345 1
                    array('footnotes' => $this->footnotesCurrentItem)
346 1
                );
347
348 1
                return Toolkit::renderHTMLTag(
349 1
                    'div',
350 1
                    $footnotes,
351 1
                    array('class' => 'footnotes')
352 1
                );
353 1
            },
354
            $content
355 1
        );
356
357 1
        $this->item['content'] = $content;
358 1
    }
359
360
    /**
361
     * Ensure the footnotes item is removed from the book if it is not needed.
362
     */
363 4
    protected function removeUnneededFootnotesItem()
364
    {
365
        // only for footnotes item
366 4
        if ($this->item['config']['element'] !== 'footnotes') {
367 4
            return;
368
        }
369
370
        // instruct the publisher to remove 'footnotes' item from book
371
        // if footnotes type is not 'item'
372 4
        if ($this->footnotesType !== self::FOOTNOTES_TYPE_ITEM) {
373
374 3
            $this->item['remove'] = true;
375
376 3
            return;
377
        }
378
379
        // instruct the publisher to remove 'footnotes' item from book
380
        // if footnotes type is 'item' but not footnotes
381 1
        if (count($this->app['publishing.footnotes.items']) == 0) {
382
            $this->item['remove'] = true;
383
            $this->writeLn("No footnotes found in text.", 'info');
384
        }
385
386 1
    }
387
}
388