Completed
Push — master ( d676b1...ecc147 )
by Dispositif
02:12
created

OuvrageOptimize::convertRoman()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 7
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 13
rs 9.6111
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->processLang();
66
        $this->processLang('langue originale');
67
68
        $this->processTitle();
69
        $this->processEditeur();
70
        $this->processDates();
71
        $this->externalTemplates();
72
        $this->predictFormatByPattern();
73
74
        $this->processIsbn();
75
        $this->processBnf();
76
77
        $this->processLocation(); // 'lieu'
78
79
        $this->GoogleBookURL('lire en ligne');
80
        $this->GoogleBookURL('présentation en ligne');
81
82
        return $this;
83
    }
84
85
    /**
86
     * Todo: injection dep.
87
     * Todo : "[s. l.]" sans lieu "s.l.n.d." sans lieu ni date.
88
     *
89
     * @throws Exception
90
     */
91
    private function processLocation()
92
    {
93
        $location = $this->getParam('lieu');
94
        if (empty($location)) {
95
            return;
96
        }
97
98
        // typo and unwikify
99
        $memo = $location;
100
        $location = WikiTextUtil::unWikify($location);
101
        $location = TextUtil::mb_ucfirst($location);
102
        if ($memo !== $location) {
103
            $this->setParam('lieu', $location);
104
            $this->log('±lieu');
105
            $this->notCosmetic = true;
106
        }
107
108
        // french translation : "London"->"Londres"
109
        $manager = new FileManager();
110
        $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
111
        if (!empty($row) && !empty($row[1])) {
112
            $this->setParam('lieu', $row[1]);
113
            $this->log('lieu francisé');
114
            $this->notCosmetic = true;
115
        }
116
    }
117
118
    private function processBnf()
119
    {
120
        $bnf = $this->getParam('bnf');
121
        if (!$bnf) {
122
            return;
123
        }
124
        $bnf = str_ireplace('FRBNF', '', $bnf);
125
        $this->setParam('bnf', $bnf);
126
    }
127
128
//    private function convertRoman(string $param): void
129
//    {
130
//        $value = $this->getParam($param);
131
//        // note : strval() condition because intval('4c') = 4
132
          // opti : $number can also be of type double
133
//        if ($value && intval($value) > 0 && intval($value) <= 10 && strval(intval($value)) === $value) {
134
//            $number = abs(intval($value));
135
//            $roman = NumberUtil::arab2roman($number);
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
     * @param string|null $param
209
     *
210
     * @throws Exception
211
     */
212
    private function processLang(?string $param = 'langue')
213
    {
214
        $lang = $this->getParam($param) ?? null;
215
216
        if ($lang) {
217
            $lang2 = Language::all2wiki($lang);
218
219
            if ($lang2 && $lang !== $lang2) {
220
                $this->setParam($param, $lang2);
221
                if (self::WIKI_LANGUAGE !== $lang2) {
222
                    $this->log('±'.$param);
223
                }
224
            }
225
        }
226
    }
227
228
    /**
229
     * Validate or correct ISBN.
230
     *
231
     * @throws Exception
232
     */
233
    private function processIsbn()
234
    {
235
        $isbn = $this->getParam('isbn') ?? '';
236
        if (empty($isbn)) {
237
            return;
238
        }
239
240
        // ISBN-13 à partir de 2007
241
        $year = $this->findBookYear();
242
        if ($year !== null && $year < 2007 && 10 === strlen($this->stripIsbn($isbn))) {
243
            // juste mise en forme ISBN-10 pour 'isbn'
244
            try {
245
                $isbnMachine = new IsbnFacade($isbn);
246
                $isbnMachine->validate();
247
                $isbn10pretty = $isbnMachine->format('ISBN-10');
248
                if ($isbn10pretty !== $isbn) {
249
                    $this->setParam('isbn', $isbn10pretty);
250
                    $this->log('ISBN10');
251
                    //                    $this->notCosmetic = true;
252
                }
253
            } catch (\Throwable $e) {
254
                // ISBN not validated
255
                $this->setParam(
256
                    'isbn invalide',
257
                    sprintf('%s %s', $isbn, $e->getMessage() ?? '')
258
                );
259
                $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
260
                $this->notCosmetic = true;
261
            }
262
263
            return;
264
        }
265
266
        try {
267
            $isbnMachine = new IsbnFacade($isbn);
268
            $isbnMachine->validate();
269
            $isbn13 = $isbnMachine->format('ISBN-13');
270
        } catch (Throwable $e) {
271
            // ISBN not validated
272
            // TODO : bot ISBN invalide (queue, message PD...)
273
            $this->setParam(
274
                'isbn invalide',
275
                sprintf('%s %s', $isbn, $e->getMessage() ?? '')
276
            );
277
            $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
278
            $this->notCosmetic = true;
279
280
            // TODO log file ISBNinvalide
281
            return;
282
        }
283
284
        // Si $isbn13 et 'isbn2' correspond à ISBN-13 => suppression
285
        if (isset($isbn13)
286
            && !empty($this->getParam('isbn2'))
287
            && $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

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

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

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