Completed
Push — master ( 02d12e...885401 )
by Dispositif
02:42
created

OuvrageOptimize::processLang()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 11
c 2
b 0
f 0
nc 7
nop 1
dl 0
loc 19
rs 8.0555
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\TextUtil;
16
use App\Domain\Utils\WikiTextUtil;
17
use App\Infrastructure\FileManager;
18
use Exception;
19
use Throwable;
20
21
use function mb_strlen;
22
23
/**
24
 * Legacy.
25
 * TODO move methods to OuvrageClean setters
26
 * TODO AbstractProcess
27
 * TODO observer/event (log, MajorEdition)
28
 * Class OuvrageProcess.
29
 */
30
class OuvrageOptimize
31
{
32
    const CONVERT_GOOGLEBOOK_TEMPLATE = false; // change OuvrageOptimizeTest !!
33
34
    const WIKI_LANGUAGE = 'fr';
35
36
    protected $original;
37
38
    private $wikiPageTitle;
39
40
    private $log = [];
41
42
    public $notCosmetic = false;
43
44
    public $major = false;
45
46
    private $ouvrage;
47
48
    private $currentTask;
49
50
    // todo inject TextUtil + ArticleVersion ou WikiRef
51
    public function __construct(OuvrageTemplate $ouvrage, $wikiPageTitle = null)
52
    {
53
        $this->original = $ouvrage;
54
        $this->ouvrage = clone $ouvrage;
55
        $this->wikiPageTitle = ($wikiPageTitle) ?? null;
56
    }
57
58
    public function doTasks(): self
59
    {
60
        $this->cleanAndPredictErrorParameters();
61
62
        $this->processAuthors();
63
64
        $this->processLang();
65
        $this->processLang('langue originale');
66
67
        $this->processTitle();
68
        $this->processEditeur();
69
        $this->processDates();
70
        $this->externalTemplates();
71
        $this->predictFormatByPattern();
72
73
        $this->processIsbn();
74
        $this->processBnf();
75
76
        $this->processLocation(); // 'lieu'
77
78
        $this->GoogleBookURL('lire en ligne');
79
        $this->GoogleBookURL('présentation en ligne');
80
81
        return $this;
82
    }
83
84
    /**
85
     * Todo: injection dep.
86
     * Todo : "[s. l.]" sans lieu "s.l.n.d." sans lieu ni date.
87
     *
88
     * @throws Exception
89
     */
90
    private function processLocation()
91
    {
92
        $location = $this->getParam('lieu');
93
        if (empty($location)) {
94
            return;
95
        }
96
97
        // typo and unwikify
98
        $memo = $location;
99
        $location = WikiTextUtil::unWikify($location);
100
        $location = TextUtil::mb_ucfirst($location);
101
        if ($memo !== $location) {
102
            $this->setParam('lieu', $location);
103
            $this->log('±lieu');
104
            $this->notCosmetic = true;
105
        }
106
107
        // french translation : "London"->"Londres"
108
        $manager = new FileManager();
109
        $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
110
        if (!empty($row) && !empty($row[1])) {
111
            $this->setParam('lieu', $row[1]);
112
            $this->log('lieu francisé');
113
            $this->notCosmetic = true;
114
        }
115
    }
116
117
    private function processBnf()
118
    {
119
        $bnf = $this->getParam('bnf');
120
        if (!$bnf) {
121
            return;
122
        }
123
        $bnf = str_ireplace('FRBNF', '', $bnf);
124
        $this->setParam('bnf', $bnf);
125
    }
126
127
    //    private function convertRoman(string $param): void
128
    //    {
129
    //        $value = $this->getParam($param);
130
    //        // note : strval() condition because intval('4c') = 4
131
    // opti : $number can also be of type double
132
    //        if ($value && intval($value) > 0 && intval($value) <= 10 && strval(intval($value)) === $value) {
133
    //            $number = abs(intval($value));
134
    //            $roman = NumberUtil::arab2roman($number);
135
    //            //            if ($number > 10) {
136
    //            //                $roman = '{{'.$roman.'}}';
137
    //            //            }
138
    //            $this->setParam($param, $roman);
139
    //            $this->log('romain');
140
    //            $this->notCosmetic = true;
141
    //        }
142
    //    }
143
144
    /**
145
     * @throws Exception
146
     */
147
    private function processAuthors()
148
    {
149
        $this->distinguishAuthors();
150
        //$this->fusionFirstNameAndName(); // desactived : no consensus
151
    }
152
153
    /**
154
     * Detect and correct multiple authors in same parameter.
155
     * Like "auteurs=J. M. Waller, M. Bigger, R. J. Hillocks".
156
     *
157
     * @throws Exception
158
     */
159
    private function distinguishAuthors()
160
    {
161
        // merge params of author 1
162
        $auteur1 = $this->getParam('auteur') ?? '';
163
        $auteur1 .= $this->getParam('auteurs') ?? '';
164
        $auteur1 .= $this->getParam('prénom1') ?? '';
165
        $auteur1 .= ' '.$this->getParam('nom1') ?? '';
166
        $auteur1 = trim($auteur1);
167
        // of authors 2
168
        $auteur2 = $this->getParam('auteur2') ?? '';
169
        $auteur2 .= $this->getParam('prénom2') ?? '';
170
        $auteur2 .= ' '.$this->getParam('nom2') ?? '';
171
        $auteur2 = trim($auteur2);
172
173
        // skip if wikilink in author
174
        if (empty($auteur1) || WikiTextUtil::isWikify($auteur1)) {
175
            return;
176
        }
177
178
        $machine = new PredictAuthors();
179
        $res = $machine->predictAuthorNames($auteur1);
180
181
        if (1 === count($res)) {
182
            // auteurs->auteur?
183
            return;
184
        }
185
        // Many authors... and empty "auteur2"
186
        if (count($res) >= 2 && empty($auteur2)) {
187
            // delete author-params
188
            array_map(
189
                function ($param) {
190
                    $this->unsetParam($param);
191
                },
192
                ['auteur', 'auteurs', 'prénom1', 'nom1']
193
            );
194
            // iterate and edit new values
195
            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...
196
                $this->setParam(sprintf('auteur%s', $i + 1), $res[$i]);
197
            }
198
            $this->log('distinction auteurs');
199
            $this->major = true;
200
            $this->notCosmetic = true;
201
        }
202
    }
203
204
    /**
205
     * todo: move/implement.
206
     *
207
     * @param string|null $param
208
     *
209
     * @throws Exception
210
     */
211
    private function processLang(?string $param = 'langue')
212
    {
213
        $lang = $this->getParam($param) ?? null;
214
215
        if ($lang) {
216
            $lang2 = Language::all2wiki($lang);
217
218
            // strip "langue originale=fr"
219
            if ('langue originale' === $param && self::WIKI_LANGUAGE === $lang2
220
                && (!$this->getParam('langue') || $this->getParam('langue') === $lang2)
221
            ) {
222
                $this->unsetParam('langue originale');
223
                $this->log('-langue originale');
224
            }
225
226
            if ($lang2 && $lang !== $lang2) {
227
                $this->setParam($param, $lang2);
228
                if (self::WIKI_LANGUAGE !== $lang2) {
229
                    $this->log('±'.$param);
230
                }
231
            }
232
        }
233
    }
234
235
    /**
236
     * Validate or correct ISBN.
237
     *
238
     * @throws Exception
239
     */
240
    private function processIsbn()
241
    {
242
        $isbn = $this->getParam('isbn') ?? '';
243
        if (empty($isbn)) {
244
            return;
245
        }
246
247
        // ISBN-13 à partir de 2007
248
        $year = $this->findBookYear();
249
        if ($year !== null && $year < 2007 && 10 === strlen($this->stripIsbn($isbn))) {
250
            // juste mise en forme ISBN-10 pour 'isbn'
251
            try {
252
                $isbnMachine = new IsbnFacade($isbn);
253
                $isbnMachine->validate();
254
                $isbn10pretty = $isbnMachine->format('ISBN-10');
255
                if ($isbn10pretty !== $isbn) {
256
                    $this->setParam('isbn', $isbn10pretty);
257
                    $this->log('ISBN10');
258
                    //                    $this->notCosmetic = true;
259
                }
260
            } catch (\Throwable $e) {
261
                // ISBN not validated
262
                $this->setParam(
263
                    'isbn invalide',
264
                    sprintf('%s %s', $isbn, $e->getMessage() ?? '')
265
                );
266
                $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
267
                $this->notCosmetic = true;
268
            }
269
270
            return;
271
        }
272
273
        try {
274
            $isbnMachine = new IsbnFacade($isbn);
275
            $isbnMachine->validate();
276
            $isbn13 = $isbnMachine->format('ISBN-13');
277
        } catch (Throwable $e) {
278
            // ISBN not validated
279
            // TODO : bot ISBN invalide (queue, message PD...)
280
            $this->setParam(
281
                'isbn invalide',
282
                sprintf('%s %s', $isbn, $e->getMessage() ?? '')
283
            );
284
            $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
285
            $this->notCosmetic = true;
286
287
            // TODO log file ISBNinvalide
288
            return;
289
        }
290
291
        // Si $isbn13 et 'isbn2' correspond à ISBN-13 => suppression
292
        if (isset($isbn13)
293
            && !empty($this->getParam('isbn2'))
294
            && $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

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

400
        if (!$this->getParam('titre') || WikiTextUtil::isWikify(/** @scrutinizer ignore-type */ $this->getParam('titre'))) {
Loading history...
401
            return;
402
        }
403
404
        if (!$this->detectColon('titre')) {
405
            return;
406
        }
407
        // Que faire si déjà un sous-titre ?
408
        if (!empty($this->getParam('sous-titre'))) {
409
            return;
410
        }
411
412
        // titre>5 and sous-titre>5 and sous-titre<40
413
        if (preg_match('#^(?<titre>[^:]{5,}):(?<st>.{5,40})$#', $this->getParam('titre'), $matches) > 0) {
414
            $this->setParam('titre', trim($matches['titre']));
415
            $this->setParam('sous-titre', trim($matches['st']));
416
            $this->log('>sous-titre');
417
        }
418
    }
419
420
    /**
421
     * Normalize a Google Book links.
422
     * Clean the useless URL parameters or transform into wiki-template.
423
     *
424
     * @param $param
425
     *
426
     * @throws Exception
427
     */
428
    private function googleBookUrl(string $param): void
429
    {
430
        $url = $this->getParam($param);
431
        if (empty($url)
432
            || !GoogleLivresTemplate::isGoogleBookURL($url)
433
        ) {
434
            return;
435
        }
436
437
        if (self::CONVERT_GOOGLEBOOK_TEMPLATE) {
438
            $template = GoogleLivresTemplate::createFromURL($url);
439
            if ($template) {
440
                $this->setParam($param, $template->serialize());
441
                $this->log('{Google}');
442
                $this->notCosmetic = true;
443
444
                return;
445
            }
446
        }
447
448
        $goo = GoogleLivresTemplate::simplifyGoogleUrl($url);
449
        if (!empty($goo) && $goo !== $url) {
450
            $this->setParam($param, $goo);
451
            $this->log('Google');
452
            $this->notCosmetic = true;
453
        }
454
    }
455
456
    /**
457
     * - {{lang|...}} dans titre => langue=... puis titre nettoyé
458
     *  langue=L’utilisation de ce paramètre permet aussi aux synthétiseurs vocaux de reconnaître la langue du titre de
459
     * l’ouvrage.
460
     * 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.
461
     *
462
     * @throws Exception
463
     */
464
    private function langInTitle(): void
465
    {
466
        if (preg_match(
467
                '#^{{ ?(?:lang|langue) ?\| ?([a-z-]{2,5}) ?\| ?(?:texte=)?([^{}=]+)(?:\|dir=rtl)?}}$#i',
468
                $this->getParam('titre'),
469
                $matches
470
            ) > 0
471
        ) {
472
            $lang = trim($matches[1]);
473
            $newtitre = str_replace($matches[0], trim($matches[2]), $this->getParam('titre'));
474
475
            // problème : titre anglais de livre français
476
            // => conversion {{lang}} du titre seulement si langue= défini
477
            // opti : restreindre à ISBN zone 2 fr ?
478
            if ($lang === $this->getParam('langue')) {
479
                $this->setParam('titre', $newtitre);
480
                $this->log('°titre');
481
            }
482
483
            // desactivé à cause de l'exception décrite ci-dessus
484
            // si langue=VIDE : ajout langue= à partir de langue titre
485
            //            if (self::WIKI_LANGUAGE !== $lang && empty($this->getParam('langue'))) {
486
            //                $this->setParam('langue', $lang);
487
            //                $this->log('+langue='.$lang);
488
            //            }
489
        }
490
    }
491
492
    private function processDates()
493
    {
494
        // dewikification
495
        $params = ['date', 'année', 'mois', 'jour'];
496
        foreach ($params as $param) {
497
            if (!empty($this->getParam($param)) && WikiTextUtil::isWikify(' '.$this->getParam($param))) {
498
                $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

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