Completed
Push — master ( d5b8a7...cdd9b5 )
by Dispositif
02:37
created

OuvrageOptimize::processIsbn()   D

Complexity

Conditions 21
Paths 111

Size

Total Lines 95
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 1
Metric Value
cc 21
eloc 53
c 6
b 0
f 1
nc 111
nop 0
dl 0
loc 95
rs 4.075

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of dispositif/wikibot application
4
 * 2019 : Philippe M. <[email protected]>
5
 * For the full copyright and MIT license information, please view the LICENSE file.
6
 */
7
8
declare(strict_types=1);
9
10
namespace App\Domain;
11
12
use App\Domain\Enums\Language;
13
use App\Domain\Models\Wiki\GoogleLivresTemplate;
14
use App\Domain\Models\Wiki\OuvrageTemplate;
15
use App\Domain\Utils\NumberUtil;
16
use App\Domain\Utils\TextUtil;
17
use App\Domain\Utils\WikiTextUtil;
18
use App\Infrastructure\FileManager;
19
use Exception;
20
use Throwable;
21
22
use function mb_strlen;
23
24
/**
25
 * Legacy.
26
 * TODO move methods to OuvrageClean setters
27
 * TODO AbstractProcess
28
 * TODO observer/event (log, MajorEdition)
29
 * Class OuvrageProcess.
30
 */
31
class OuvrageOptimize
32
{
33
    const CONVERT_GOOGLEBOOK_TEMPLATE = false; // change OuvrageOptimizeTest !!
34
35
    const WIKI_LANGUAGE = 'fr';
36
37
    protected $original;
38
39
    private $wikiPageTitle;
40
41
    private $log = [];
42
43
    public $notCosmetic = false;
44
45
    public $major = false;
46
47
    private $ouvrage;
48
49
    private $currentTask;
50
51
    // todo inject TextUtil + ArticleVersion ou WikiRef
52
    public function __construct(OuvrageTemplate $ouvrage, $wikiPageTitle = null)
53
    {
54
        $this->original = $ouvrage;
55
        $this->ouvrage = clone $ouvrage;
56
        $this->wikiPageTitle = ($wikiPageTitle) ?? null;
57
    }
58
59
    public function doTasks(): self
60
    {
61
        $this->cleanAndPredictErrorParameters();
62
63
        $this->processAuthors();
64
65
        $this->processTitle();
66
        $this->processEditeur();
67
        $this->processDates();
68
        $this->externalTemplates();
69
        $this->predictFormatByPattern();
70
71
//        $this->convertRoman('tome');
72
//        $this->convertRoman('volume');
73
74
        $this->processIsbn();
75
        $this->processBnf();
76
77
        $this->processLang();
78
        $this->processLocation(); // 'lieu'
79
80
        $this->GoogleBookURL('lire en ligne');
81
        $this->GoogleBookURL('présentation en ligne');
82
83
        return $this;
84
    }
85
86
    /**
87
     * Todo: injection dep.
88
     * Todo : "[s. l.]" sans lieu "s.l.n.d." sans lieu ni date.
89
     *
90
     * @throws Exception
91
     */
92
    private function processLocation()
93
    {
94
        $location = $this->getParam('lieu');
95
        if (empty($location)) {
96
            return;
97
        }
98
99
        // typo and unwikify
100
        $memo = $location;
101
        $location = WikiTextUtil::unWikify($location);
102
        $location = TextUtil::mb_ucfirst($location);
103
        if ($memo !== $location) {
104
            $this->setParam('lieu', $location);
105
            $this->log('±lieu');
106
            $this->notCosmetic = true;
107
        }
108
109
        // french translation : "London"->"Londres"
110
        $manager = new FileManager();
111
        $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
112
        if (!empty($row) && !empty($row[1])) {
113
            $this->setParam('lieu', $row[1]);
114
            $this->log('lieu francisé');
115
            $this->notCosmetic = true;
116
        }
117
    }
118
119
    private function processBnf()
120
    {
121
        $bnf = $this->getParam('bnf');
122
        if (!$bnf) {
123
            return;
124
        }
125
        $bnf = str_ireplace('FRBNF', '', $bnf);
126
        $this->setParam('bnf', $bnf);
127
    }
128
129
    private function convertRoman(string $param): void
0 ignored issues
show
Unused Code introduced by
The method convertRoman() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
130
    {
131
        $value = $this->getParam($param);
132
        // note : strval() condition because intval('4c') = 4
133
        if ($value && intval($value) > 0 && intval($value) <= 10 && strval(intval($value)) === $value) {
134
            $number = abs(intval($value));
135
            $roman = NumberUtil::arab2roman($number);
0 ignored issues
show
Bug introduced by
It seems like $number can also be of type double; however, parameter $number of App\Domain\Utils\NumberUtil::arab2roman() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
            $roman = NumberUtil::arab2roman(/** @scrutinizer ignore-type */ $number);
Loading history...
136
//            if ($number > 10) {
137
//                $roman = '{{'.$roman.'}}';
138
//            }
139
            $this->setParam($param, $roman);
140
            $this->log('romain');
141
            $this->notCosmetic = true;
142
        }
143
    }
144
145
    /**
146
     * @throws Exception
147
     */
148
    private function processAuthors()
149
    {
150
        $this->distinguishAuthors();
151
        //$this->fusionFirstNameAndName(); // desactived : no consensus
152
    }
153
154
    /**
155
     * Detect and correct multiple authors in same parameter.
156
     * Like "auteurs=J. M. Waller, M. Bigger, R. J. Hillocks".
157
     *
158
     * @throws Exception
159
     */
160
    private function distinguishAuthors()
161
    {
162
        // merge params of author 1
163
        $auteur1 = $this->getParam('auteur') ?? '';
164
        $auteur1 .= $this->getParam('auteurs') ?? '';
165
        $auteur1 .= $this->getParam('prénom1') ?? '';
166
        $auteur1 .= ' '.$this->getParam('nom1') ?? '';
167
        $auteur1 = trim($auteur1);
168
        // of authors 2
169
        $auteur2 = $this->getParam('auteur2') ?? '';
170
        $auteur2 .= $this->getParam('prénom2') ?? '';
171
        $auteur2 .= ' '.$this->getParam('nom2') ?? '';
172
        $auteur2 = trim($auteur2);
173
174
        // skip if wikilink in author
175
        if (empty($auteur1) || WikiTextUtil::isWikify($auteur1)) {
176
            return;
177
        }
178
179
        $machine = new PredictAuthors();
180
        $res = $machine->predictAuthorNames($auteur1);
181
182
        if (1 === count($res)) {
183
            // auteurs->auteur?
184
            return;
185
        }
186
        // Many authors... and empty "auteur2"
187
        if (count($res) >= 2 && empty($auteur2)) {
188
            // delete author-params
189
            array_map(
190
                function ($param) {
191
                    $this->unsetParam($param);
192
                },
193
                ['auteur', 'auteurs', 'prénom1', 'nom1']
194
            );
195
            // iterate and edit new values
196
            for ($i = 0; $i < count($res); ++$i) {
1 ignored issue
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
197
                $this->setParam(sprintf('auteur%s', $i + 1), $res[$i]);
198
            }
199
            $this->log('distinction auteurs');
200
            $this->major = true;
201
            $this->notCosmetic = true;
202
        }
203
    }
204
205
    /**
206
     * todo: move/implement.
207
     *
208
     * @throws Exception
209
     */
210
    private function processLang()
211
    {
212
        $lang = $this->getParam('langue') ?? null;
213
214
        if ($lang) {
215
            $lang2 = Language::all2wiki($lang);
216
217
            if ($lang2 && $lang !== $lang2) {
218
                $this->setParam('langue', $lang2);
219
                if (self::WIKI_LANGUAGE !== $lang2) {
220
                    $this->log('±langue');
221
                }
222
            }
223
        }
224
    }
225
226
    /**
227
     * Validate or correct ISBN.
228
     *
229
     * @throws Exception
230
     */
231
    private function processIsbn()
232
    {
233
        $isbn = $this->getParam('isbn') ?? '';
234
        if (empty($isbn)) {
235
            return;
236
        }
237
238
        // ISBN-13 à partir de 2007
239
        $year = $this->findBookYear();
240
        if ($year && $year < 2007 && 10 === strlen($this->stripIsbn($isbn))) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $year of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
241
            // juste mise en forme ISBN-10 pour 'isbn'
242
            try {
243
                $isbnMachine = new IsbnFacade($isbn);
244
                $isbnMachine->validate();
245
                $isbn10pretty = $isbnMachine->format('ISBN-10');
246
                if ($isbn10pretty !== $isbn) {
247
                    $this->setParam('isbn', $isbn10pretty);
248
                    $this->log('ISBN10');
249
//                    $this->notCosmetic = true;
250
                }
251
            } catch (\Throwable $e) {
252
                // ISBN not validated
253
                $this->setParam(
254
                    'isbn invalide',
255
                    sprintf('%s %s', $isbn, $e->getMessage() ?? '')
256
                );
257
                $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
258
                $this->notCosmetic = true;
259
            }
260
261
            return;
262
        }
263
264
        try {
265
            $isbnMachine = new IsbnFacade($isbn);
266
            $isbnMachine->validate();
267
            $isbn13 = $isbnMachine->format('ISBN-13');
268
        } catch (Throwable $e) {
269
            // ISBN not validated
270
            // TODO : bot ISBN invalide (queue, message PD...)
271
            $this->setParam(
272
                'isbn invalide',
273
                sprintf('%s %s', $isbn, $e->getMessage() ?? '')
274
            );
275
            $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
276
            $this->notCosmetic = true;
277
278
            // TODO log file ISBNinvalide
279
            return;
280
        }
281
282
        // Si $isbn13 et 'isbn2' correspond à ISBN-13 => suppression
283
        if (isset($isbn13)
284
            && $this->getParam('isbn2')
285
            && $this->stripIsbn($this->getParam('isbn2')) === $this->stripIsbn($isbn13)
0 ignored issues
show
Bug introduced by
It seems like $this->getParam('isbn2') can also be of type null; however, parameter $isbn of App\Domain\OuvrageOptimize::stripIsbn() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
            && $this->stripIsbn(/** @scrutinizer ignore-type */ $this->getParam('isbn2')) === $this->stripIsbn($isbn13)
Loading history...
286
        ) {
287
            $this->unsetParam('isbn2');
288
        }
289
290
291
        // ISBN-10 ?
292
        $stripIsbn = $this->stripIsbn($isbn);
293
        if (10 === mb_strlen($stripIsbn)) {
294
            // ajout des tirets
295
            $isbn10pretty = $isbn;
1 ignored issue
show
Unused Code introduced by
The assignment to $isbn10pretty is dead and can be removed.
Loading history...
296
            try {
297
                $isbn10pretty = $isbnMachine->format('ISBN-10');
298
                if ($isbn10pretty !== $isbn) {
299
//                    $this->notCosmetic = true;
300
                }
301
            } catch (\Throwable $e) {
302
                unset($e);
303
            }
304
305
            // archivage ISBN-10 dans 'isbn2'
306
            if (!$this->getParam('isbn2')) {
307
                $this->setParam('isbn2', $isbn10pretty);
308
            }
309
            // sinon dans 'isbn3'
310
            if (!empty($this->getParam('isbn2'))
311
                && $this->stripIsbn($this->getParam('isbn2')) !== $stripIsbn
312
                && empty($this->getParam('isbn3'))
313
            ) {
314
                $this->setParam('isbn3', $isbn10pretty);
315
            }
316
            // delete 'isbn10' (en attendant modification modèle)
317
            if (!empty($this->getParam('isbn10')) && $this->stripIsbn($this->getParam('isbn10')) === $stripIsbn) {
318
                $this->unsetParam('isbn10');
319
            }
320
        }
321
322
        // ISBN correction
323
        if ($isbn13 !== $isbn) {
324
            $this->setParam('isbn', $isbn13);
325
            $this->log('ISBN');
326
//            $this->notCosmetic = true;
327
        }
328
    }
329
330
    /**
331
     * Find year of book publication.
332
     *
333
     * @return int|null
334
     * @throws Exception
335
     */
336
    private function findBookYear(): ?int
337
    {
338
        $annee = $this->getParam('année');
339
        if (!empty($annee) && is_numeric($annee)) {
340
            return intval($annee);
341
        }
342
        $date = $this->getParam('date');
343
        if ($date && preg_match('#[^0-9]?([12][0-9][0-9][0-9])[^0-9]?#', $date, $matches) > 0) {
344
            return intval($matches[1]);
345
        }
346
347
        return null;
348
    }
349
350
    private function stripIsbn(string $isbn): string
351
    {
352
        return trim(preg_replace('#[^0-9Xx]#', '', $isbn));
353
    }
354
355
    private function processTitle()
356
    {
357
        $this->currentTask = 'titres';
358
359
        $oldtitre = $this->getParam('titre');
360
        $this->langInTitle();
361
        $this->deWikifyExternalLink('titre');
362
        $this->upperCaseFirstLetter('titre');
363
        $this->typoDeuxPoints('titre');
364
365
        $this->extractSubTitle();
366
367
        // 20-11-2019 : Retiré majuscule à sous-titre
368
369
        if ($this->getParam('titre') !== $oldtitre) {
370
            $this->log('±titre');
371
        }
372
373
        $this->valideNumeroChapitre();
374
        $this->deWikifyExternalLink('titre chapitre');
375
        $this->upperCaseFirstLetter('titre chapitre');
376
    }
377
378
    private function detectColon($param): bool
379
    {
380
        // > 0 don't count a starting colon ":bla"
381
        if (!empty($this->getParam($param)) && mb_strrpos($this->getParam('titre'), ':') > 0) {
382
            return true;
383
        }
384
385
        return false;
386
    }
387
388
    private function extractSubTitle(): void
389
    {
390
        // FIXED bug [[fu:bar]]
391
        if (!$this->getParam('titre') || WikiTextUtil::isWikify($this->getParam('titre'))) {
1 ignored issue
show
Bug introduced by
It seems like $this->getParam('titre') can also be of type null; however, parameter $text of App\Domain\Utils\WikiTextUtil::isWikify() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

391
        if (!$this->getParam('titre') || WikiTextUtil::isWikify(/** @scrutinizer ignore-type */ $this->getParam('titre'))) {
Loading history...
392
            return;
393
        }
394
395
        if (!$this->detectColon('titre')) {
396
            return;
397
        }
398
        // Que faire si déjà un sous-titre ?
399
        if (!empty($this->getParam('sous-titre'))) {
400
            return;
401
        }
402
403
        // titre>5 and sous-titre>5 and sous-titre<40
404
        if (preg_match('#^(?<titre>[^:]{5,}):(?<st>.{5,40})$#', $this->getParam('titre'), $matches) > 0) {
405
            $this->setParam('titre', trim($matches['titre']));
406
            $this->setParam('sous-titre', trim($matches['st']));
407
            $this->log('>sous-titre');
408
        }
409
    }
410
411
    /**
412
     * Normalize a Google Book links.
413
     * Clean the useless URL parameters or transform into wiki-template.
414
     *
415
     * @param $param
416
     *
417
     * @throws Exception
418
     */
419
    private function googleBookUrl(string $param): void
420
    {
421
        $url = $this->getParam($param);
422
        if (empty($url)
423
            || !GoogleLivresTemplate::isGoogleBookURL($url)
424
        ) {
425
            return;
426
        }
427
428
        if (self::CONVERT_GOOGLEBOOK_TEMPLATE) {
429
            $template = GoogleLivresTemplate::createFromURL($url);
430
            if ($template) {
1 ignored issue
show
introduced by
$template is of type App\Domain\Models\Wiki\GoogleLivresTemplate, thus it always evaluated to true.
Loading history...
431
                $this->setParam($param, $template->serialize());
432
                $this->log('{Google}');
433
                $this->notCosmetic = true;
434
435
                return;
436
            }
437
        }
438
439
        $goo = GoogleLivresTemplate::simplifyGoogleUrl($url);
440
        if (!empty($goo) && $goo !== $url ) {
441
            $this->setParam($param, $goo);
442
            $this->log('Google');
443
            $this->notCosmetic = true;
444
        }
445
    }
446
447
    /**
448
     * - {{lang|...}} dans titre => langue=... puis titre nettoyé
449
     *  langue=L’utilisation de ce paramètre permet aussi aux synthétiseurs vocaux de reconnaître la langue du titre de
450
     * l’ouvrage.
451
     * Il est possible d'afficher plusieurs langues, en saisissant le nom séparé par des espaces ou des virgules. La première langue doit être celle du titre.
452
     *
453
     * @throws Exception
454
     */
455
    private function langInTitle()
456
    {
457
        if (preg_match(
458
                '#^{{ ?(?:lang|langue) ?\| ?([a-z-]{2,5}) ?\| ?(?:texte=)?([^{}=]+)(?:\|dir=rtl)?}}$#i',
459
                $this->getParam('titre'),
460
                $matches
461
            ) > 0
462
        ) {
463
            $lang = trim($matches[1]);
464
            $newtitre = str_replace($matches[0], trim($matches[2]), $this->getParam('titre'));
465
            $this->setParam('titre', $newtitre);
466
            $this->log('°titre');
467
            if (self::WIKI_LANGUAGE !== $lang && empty($this->getParam('langue'))) {
468
                $this->setParam('langue', $lang);
469
                $this->log('+langue='.$lang);
470
            }
471
        }
472
    }
473
474
    private function processDates()
475
    {
476
        // dewikification
477
        $params = ['date', 'année', 'mois', 'jour'];
478
        foreach ($params as $param) {
479
            if (WikiTextUtil::isWikify(' '.$this->getParam($param))) {
480
                $this->setParam($param, WikiTextUtil::unWikify($this->getParam($param)));
1 ignored issue
show
Bug introduced by
It seems like $this->getParam($param) can also be of type null; however, parameter $text of App\Domain\Utils\WikiTextUtil::unWikify() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

480
                $this->setParam($param, WikiTextUtil::unWikify(/** @scrutinizer ignore-type */ $this->getParam($param)));
Loading history...
481
            }
482
        }
483
484
        try {
485
            $this->moveDate2Year();
486
        } catch (Exception $e) {
487
            dump($e);
488
        }
489
    }
490
491
    /**
492
     * todo: move to AbstractWikiTemplate ?
493
     * Correction des parametres rejetés à l'hydratation données.
494
     *
495
     * @throws Exception
496
     */
497
    private function cleanAndPredictErrorParameters()
498
    {
499
        if (empty($this->ouvrage->parametersErrorFromHydrate)) {
500
            return;
501
        }
502
        $allParamsAndAlias = $this->ouvrage->getParamsAndAlias();
503
504
        foreach ($this->ouvrage->parametersErrorFromHydrate as $name => $value) {
505
            if (!is_string($name)) {
506
                // example : 1 => "ouvrage collectif" from |ouvrage collectif|
507
                continue;
508
            }
509
510
            // delete error parameter if no value
511
            if (empty($value)) {
512
                unset($this->ouvrage->parametersErrorFromHydrate[$name]);
513
514
                continue;
515
            }
516
517
            $maxDistance = 1;
518
            if (mb_strlen($name) >= 4) {
519
                $maxDistance = 2;
520
            }
521
            if (mb_strlen($name) >= 8) {
522
                $maxDistance = 3;
523
            }
524
525
            $predName = TextUtil::predictCorrectParam($name, $allParamsAndAlias, $maxDistance);
526
            if ($predName && mb_strlen($name) >= 5) {
527
                if (empty($this->getParam($predName))) {
528
                    $predName = $this->ouvrage->getAliasParam($predName);
529
                    $this->setParam($predName, $value);
530
                    $this->log(sprintf('%s⇒%s ?', $name, $predName));
531
                    $this->notCosmetic = true;
532
                    unset($this->ouvrage->parametersErrorFromHydrate[$name]);
533
                }
534
            }
535
        }
536
    }
537
538
    /**
539
     * TODO : return "" instead of null ?
540
     *
541
     * @param $name
542
     *
543
     * @return string|null
544
     * @throws Exception
545
     */
546
    private function getParam(string $name): ?string
547
    {
548
        return $this->ouvrage->getParam($name);
549
    }
550
551
    private function setParam($name, $value)
552
    {
553
        // todo : overwrite setParam() ?
554
        if (!empty($value) || $this->ouvrage->getParam($name)) {
555
            $this->ouvrage->setParam($name, $value);
556
        }
557
    }
558
559
    private function log(string $string): void
560
    {
561
        if (!empty($string)) {
562
            $this->log[] = trim($string);
563
        }
564
    }
565
566
    /**
567
     * Bool ?
568
     * déwikification du titre : consensus Bistro 27 août 2011
569
     * idem  'titre chapitre'.
570
     *
571
     * @param string $param
572
     *
573
     * @throws Exception
574
     */
575
    private function deWikifyExternalLink(string $param): void
576
    {
577
        if (empty($this->getParam($param))) {
578
            return;
579
        }
580
        if (preg_match('#^\[(http[^ \]]+) ([^]]+)]#i', $this->getParam($param), $matches) > 0) {
581
            $this->setParam($param, str_replace($matches[0], $matches[2], $this->getParam($param)));
582
            $this->log('±'.$param);
583
584
            if (in_array($param, ['titre', 'titre chapitre'])) {
585
                if (empty($this->getParam('lire en ligne'))) {
586
                    $this->setParam('lire en ligne', $matches[1]);
587
                    $this->log('+lire en ligne');
588
589
                    return;
590
                }
591
                $this->log('autre lien externe: '.$matches[1]);
592
            }
593
        }
594
    }
595
596
    private function upperCaseFirstLetter($param)
597
    {
598
        if (empty($this->getParam($param))) {
599
            return;
600
        }
601
        $newValue = TextUtil::mb_ucfirst(trim($this->getParam($param)));
602
        $this->setParam($param, $newValue);
603
    }
604
605
    /**
606
     * Typo internationale 'titre : sous-titre'.
607
     * Fix fantasy typo of subtitle with '. ' or ' - '.
608
     * International Standard Bibliographic Description :
609
     * https://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Le_Bistro/13_janvier_2016#Modif_du_mod%C3%A8le:Ouvrage.
610
     *
611
     * @param $param
612
     *
613
     * @throws Exception
614
     */
615
    private function typoDeuxPoints($param)
616
    {
617
        $origin = $this->getParam($param) ?? '';
618
        if (empty($origin)) {
619
            return;
620
        }
621
        // FIXED bug [[fu:bar]]
622
        if (WikiTextUtil::isWikify($origin)) {
623
            return;
624
        }
625
626
        $origin = TextUtil::replaceNonBreakingSpaces($origin);
627
628
        $strTitle = $origin;
629
630
        // CORRECTING TYPO FANTASY OF SUBTITLE
631
632
        // Replace first '.' by ':' if no ': ' and no numbers around (as PHP 7.3)
633
        // exlude pattern "blabla... blabla"
634
        // TODO: statistics
635
636
        // Replace ' - ' or ' / ' (spaced!) by ' : ' if no ':' and no numbers after (as PHP 7.3 or 1939-1945)
637
        if (!mb_strpos(':', $strTitle) && preg_match('#.{6,} ?[-/] ?[^0-9)]{6,}#', $strTitle) > 0) {
638
            $strTitle = preg_replace('#(.{6,}) [-/] ([^0-9)]{6,})#', '$1 : $2', $strTitle);
639
        }
640
641
        // international typo style " : " (first occurrence)
642
        $strTitle = preg_replace('#[ ]*:[ ]*#', ' : ', $strTitle);
643
644
        if ($strTitle !== $origin) {
645
            $this->setParam($param, $strTitle);
646
            $this->log(sprintf(':%s', $param));
647
        }
648
    }
649
650
    private function valideNumeroChapitre()
651
    {
652
        $value = $this->getParam('numéro chapitre');
653
        if (empty($value)) {
654
            return;
655
        }
656
        // "12" ou "VI", {{II}}, II:3
657
        if (preg_match('#^[0-9IVXL\-.:{}]+$#i', $value) > 0) {
658
            return;
659
        }
660
        // déplace vers "titre chapitre" ?
661
        if (!$this->getParam('titre chapitre')) {
662
            $this->unsetParam('numéro chapitre');
663
            $this->setParam('titre chapitre', $value);
664
        }
665
        $this->log('≠numéro chapitre');
666
    }
667
668
    private function unsetParam($name)
669
    {
670
        $this->ouvrage->unsetParam($name);
671
    }
672
673
    /**
674
     * TODO move+refac
675
     * TODO PlumeTemplate CommentaireBiblioTemplate  ExtraitTemplate
676
     * Probleme {{commentaire biblio}} <> {{commentaire biblio SRL}}
677
     * Generate supplementary templates from obsoletes params.
678
     *
679
     * @throws Exception
680
     */
681
    protected function externalTemplates()
682
    {
683
        // "plume=bla" => {{plume}}
684
        // =oui selon doc, mais testé OK avec "non"
685
        // todo detect duplication ouvrage/plume dans externalTemplate ?
686
        if (!empty($this->getParam('plume'))) {
687
            $plumeValue = $this->getParam('plume');
688
            $this->ouvrage->externalTemplates[] = (object)[
689
                'template' => 'plume',
690
                '1' => $plumeValue,
691
                'raw' => '{{nobr|. {{plume}}}}',
692
            ];
693
            $this->unsetParam('plume');
694
            $this->log('{plume}');
695
        }
696
697
        // "extrait=bla" => {{citation bloc|bla}}
698
        if (!empty($this->getParam('extrait'))) {
699
            $extrait = $this->getParam('extrait');
700
            // todo bug {{citation bloc}} si "=" ou "|" dans texte de citation
701
            // Legacy : use {{début citation}} ... {{fin citation}}
702
            if (preg_match('#[=|]#', $extrait) > 0) {
703
                $this->ouvrage->externalTemplates[] = (object)[
704
                    'template' => 'début citation',
705
                    '1' => '',
706
                    'raw' => '{{début citation}}'.$extrait.'{{fin citation}}',
707
                ];
708
                $this->log('{début citation}');
709
                $this->notCosmetic = true;
710
            } else {
711
                // StdClass
712
                $this->ouvrage->externalTemplates[] = (object)[
713
                    'template' => 'citation bloc',
714
                    '1' => $extrait,
715
                    'raw' => '{{extrait|'.$extrait.'}}',
716
                ];
717
                $this->log('{extrait}');
718
                $this->notCosmetic = true;
719
            }
720
721
            $this->unsetParam('extrait');
722
            $this->notCosmetic = true;
723
        }
724
725
        // "commentaire=bla" => {{Commentaire biblio|1=bla}}
726
        if (!empty($this->getParam('commentaire'))) {
727
            $commentaire = $this->getParam('commentaire');
728
            $this->ouvrage->externalTemplates[] = (object)[
729
                'template' => 'commentaire biblio',
730
                '1' => $commentaire,
731
                'raw' => '{{commentaire biblio|'.$commentaire.'}}',
732
            ];
733
            $this->unsetParam('commentaire');
734
            $this->log('{commentaire}');
735
            $this->notCosmetic = true;
736
        }
737
    }
738
739
    // ----------------------
740
    // ----------------------
741
    // ----------------------
742
743
    /**
744
     * Date->année (nécessaire pour OuvrageComplete).
745
     *
746
     * @throws Exception
747
     */
748
    private function moveDate2Year()
749
    {
750
        $date = $this->getParam('date') ?? false;
751
        if ($date) {
752
            if (preg_match('#^-?[12][0-9][0-9][0-9]$#', $date)) {
753
                $this->setParam('année', $date);
754
                $this->unsetParam('date');
755
                //$this->log('>année');
756
            }
757
        }
758
    }
759
760
    private function predictFormatByPattern()
761
    {
762
        if (($value = $this->getParam('format'))) {
763
            // predict if 'format électronique'
764
            // format electronique lié au champ 'lire en ligne'
765
            // 2015 https://fr.wikipedia.org/wiki/Discussion_mod%C3%A8le:Ouvrage#format,_format_livre,_format_%C3%A9lectronique
766
            //            if (preg_match('#(pdf|epub|html|kindle|audio|\{\{aud|jpg)#i', $value) > 0) {
767
            //
768
            //                $this->setParam('format électronique', $value);
769
            //                $this->unsetParam('format');
770
            //                $this->log('format:électronique?');
771
            //
772
            //                return;
773
            //            }
774
            if (preg_match(
775
                    '#(ill\.|couv\.|in-[0-9]|in-fol|poche|broché|relié|{{unité|{{Dunité|[0-9]{2} ?cm|\|cm}}|vol\.|A4)#i',
776
                    $value
777
                ) > 0
778
            ) {
779
                $this->setParam('format livre', $value);
780
                $this->unsetParam('format');
781
                $this->log('format:livre?');
782
                $this->notCosmetic = true;
783
            }
784
            // Certainement 'format électronique'...
785
        }
786
    }
787
788
    /**
789
     * @return bool
790
     * @throws Exception
791
     */
792
    public function checkMajorEdit(): bool
793
    {
794
        // Correction paramètre
795
        if ($this->ouvrage->parametersErrorFromHydrate !== $this->original->parametersErrorFromHydrate) {
796
            return true;
797
        }
798
        // Complétion langue ?
799
        if (!empty($this->getParam('langue')) && empty($this->original->getParam('langue'))
800
            && self::WIKI_LANGUAGE !== $this->getParam('langue')
801
        ) {
802
            return true;
803
        }
804
        // TODO replace conditions ci-dessous par event flagMajor()
805
        // Retire le param/value 'langue' (pas major si conversion nom langue)
806
        $datOuvrage = $this->ouvrage->toArray();
807
        $datOriginal = $this->original->toArray();
808
        unset($datOriginal['langue'], $datOuvrage['langue']);
809
810
        // Modification données
811
        if ($datOriginal !== $datOuvrage) {
812
            return true;
813
        }
814
815
        return false;
816
    }
817
818
    /**
819
     * @return array
820
     */
821
    public function getLog(): array
822
    {
823
        return $this->log;
824
    }
825
826
    /**
827
     * @return OuvrageTemplate
828
     */
829
    public function getOuvrage(): OuvrageTemplate
830
    {
831
        return $this->ouvrage;
832
    }
833
834
    /**
835
     * todo : vérif lien rouge
836
     * todo 'lien éditeur' affiché 1x par page
837
     * opti : Suppression lien éditeur si c'est l'article de l'éditeur.
838
     *
839
     * @throws Exception
840
     */
841
    private function processEditeur()
842
    {
843
        $editeur = $this->getParam('éditeur');
844
        if (empty($editeur)) {
845
            return;
846
        }
847
848
        // FIX bug "GEO Art ([[Prisma Media]]) ; [[Le Monde]]"
849
        if (preg_match('#\[.*\[.*\[#', $editeur) > 0) {
850
            return;
851
        }
852
        // FIX bug "[[Fu|Bar]] bla" => [[Fu|Bar bla]]
853
        if (preg_match('#(.+\[\[|\]\].+)#', $editeur) > 0) {
854
            return;
855
        }
856
857
        // [[éditeur]]
858
        if (preg_match('#\[\[([^|]+)]]#', $editeur, $matches) > 0) {
859
            $editeurUrl = $matches[1];
860
        }
861
        // [[bla|éditeur]]
862
        if (preg_match('#\[\[([^]|]+)\|.+]]#', $editeur, $matches) > 0) {
863
            $editeurUrl = $matches[1];
864
        }
865
866
        // abréviations communes
867
        // ['éd. de ', 'éd. du ', 'éd.', 'ed.', 'Éd. de ', 'Éd.', 'édit.', 'Édit.', '(éd.)', '(ed.)', 'Ltd.']
868
869
        $editeurStr = WikiTextUtil::unWikify($editeur);
870
        // On garde minuscule sur éditeur, pour nuance Éditeur/éditeur permettant de supprimer "éditeur"
871
        // ex: "éditions Milan" => "Milan"
872
873
        //        $editeurStr = TextUtil::mb_ucfirst($editeurStr);
874
875
        // Déconseillé : 'lien éditeur' (obsolete 2019)
876
        if (!empty($this->getParam('lien éditeur'))) {
877
            if (empty($editeurUrl)) {
878
                $editeurUrl = $this->getParam('lien éditeur');
879
            }
880
            $this->unsetParam('lien éditeur');
881
        }
882
883
        //        if (isset($editeurUrl)) {
884
        //            $editeurUrl = TextUtil::mb_ucfirst($editeurUrl);
885
        //        }
886
        $newEditeur = $editeurStr;
887
        if (isset($editeurUrl) && $editeurUrl !== $editeurStr) {
888
            $newEditeur = '[['.$editeurUrl.'|'.$editeurStr.']]';
889
        }
890
        if (isset($editeurUrl) && $editeurUrl === $editeurStr) {
891
            $newEditeur = '[['.$editeurStr.']]';
892
        }
893
894
        if ($newEditeur !== $editeur) {
895
            $this->setParam('éditeur', $newEditeur);
896
            $this->log('±éditeur');
897
            $this->notCosmetic = true;
898
        }
899
    }
900
}
901