Passed
Push — master ( 38c1f2...ec662d )
by Dispositif
10:17
created

OuvrageOptimize::isEditionYear()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 3
nc 2
nop 1
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 4
rs 10
c 0
b 0
f 0
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\Publisher\GoogleBooksUtil;
15
use App\Domain\Utils\TextUtil;
16
use App\Domain\Utils\WikiTextUtil;
17
use App\Infrastructure\FileManager;
18
use DomainException;
19
use Exception;
20
21
/**
22
 * Legacy.
23
 * TODO move methods to OuvrageClean setters
24
 * TODO AbstractProcess
25
 * TODO observer/event (log, MajorEdition)
26
 * Class OuvrageProcess.
27
 */
28
class OuvrageOptimize extends AbstractTemplateOptimizer
29
{
30
    use OptimizeISBNTrait;
31
32
    const CONVERT_GOOGLEBOOK_TEMPLATE = false; // change OuvrageOptimizeTest !!
33
34
    const WIKI_LANGUAGE = 'fr';
35
36
    const PUBLISHER_FRWIKI_FILENAME = __DIR__.'/resources/data_editors_wiki.json';
37
38
    public $notCosmetic = false;
39
40
    public $major = false;
41
42
    /**
43
     * @return $this
44
     * @throws Exception
45
     */
46
    public function doTasks(): self
47
    {
48
        $this->cleanAndPredictErrorParameters();
49
50
        $this->processAuthors();
51
52
        $this->processLang();
53
        $this->processLang('langue originale');
54
55
        $this->processTitle();
56
        $this->convertLienAuteurTitre();
57
58 47
        $this->processEditionCitebook();
59
60 47
        $this->processEditeur();
61 47
        $this->processDates();
62 47
        $this->externalTemplates();
63 47
        $this->predictFormatByPattern();
64 47
65
        $this->processIsbn();
66
        $this->processBnf();
67
68
        $this->processLocation(); // 'lieu'
69
70 47
        $this->GoogleBookURL('lire en ligne');
71
        $this->GoogleBookURL('présentation en ligne');
72 47
73
        return $this;
74 47
    }
75
76 47
    /**
77 47
     * Todo: injection dep.
78
     * Todo : "[s. l.]" sans lieu "s.l.n.d." sans lieu ni date.
79 47
     *
80 47
     * @throws Exception
81 47
     */
82 47
    protected function processLocation()
83 47
    {
84 47
        $location = $this->getParam('lieu');
85
        if (empty($location)) {
86 47
            return;
87 47
        }
88
89 47
        // typo and unwikify
90
        $memo = $location;
91 47
        $location = WikiTextUtil::unWikify($location);
92 47
        $location = TextUtil::mb_ucfirst($location);
93
        if ($memo !== $location) {
94 47
            $this->setParam('lieu', $location);
95
            $this->addSummaryLog('±lieu');
96
            $this->notCosmetic = true;
97
        }
98
99
        // translation : "London"->"Londres"
100
        $manager = new FileManager();
101
        $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
102
        if (!empty($row) && !empty($row[1])) {
103 47
            $this->setParam('lieu', $row[1]);
104
            $this->addSummaryLog('lieu francisé');
105 47
            $this->notCosmetic = true;
106 47
        }
107 42
    }
108
109
    protected function processBnf()
110
    {
111 5
        $bnf = $this->getParam('bnf');
112 5
        if (!$bnf) {
113 5
            return;
114 5
        }
115 1
        $bnf = str_ireplace('FRBNF', '', $bnf);
116 1
        $this->setParam('bnf', $bnf);
117 1
    }
118
119
    /**
120
     * @throws Exception
121 5
     */
122 5
    protected function processAuthors()
123
    {
124 1
        $this->distinguishAuthors();
125 1
        //$this->fusionFirstNameAndName(); // desactived : no consensus
126 1
    }
127 1
128 1
    protected function convertLienAuteurTitre(): void
129 1
    {
130
        $auteurParams = ['auteur1', 'auteur2', 'auteur2', 'titre'];
131
        foreach ($auteurParams as $auteurParam) {
132 5
            if ($this->hasParamValue($auteurParam)
133
                && $this->hasParamValue('lien '.$auteurParam)
134 47
            ) {
135
                $this->setParam(
136 47
                    $auteurParam,
137 47
                    WikiTextUtil::wikilink(
138 46
                        $this->getParam($auteurParam),
139
                        $this->getParam('lien '.$auteurParam)
140 1
                    )
141 1
                );
142 1
                $this->unsetParam('lien '.$auteurParam);
143
            }
144
        }
145
    }
146
147 47
    /**
148
     * Detect and correct multiple authors in same parameter.
149 47
     * Like "auteurs=J. M. Waller, M. Bigger, R. J. Hillocks".
150
     *
151 47
     * @throws Exception
152
     */
153 47
    protected function distinguishAuthors()
154
    {
155 47
        // merge params of author 1
156 47
        $auteur1 = $this->getParam('auteur') ?? '';
157 47
        $auteur1 .= $this->getParam('auteurs') ?? '';
158 47
        $auteur1 .= $this->getParam('prénom1') ?? '';
159
        $auteur1 .= ' '.$this->getParam('nom1') ?? '';
160 1
        $auteur1 = trim($auteur1);
161 1
        // of authors 2
162 1
        $auteur2 = $this->getParam('auteur2') ?? '';
163 1
        $auteur2 .= $this->getParam('prénom2') ?? '';
164 1
        $auteur2 .= ' '.$this->getParam('nom2') ?? '';
165
        $auteur2 = trim($auteur2);
166
167 1
        // skip if wikilink in author
168
        if (empty($auteur1) || WikiTextUtil::isWikify($auteur1)) {
169
            return;
170 47
        }
171
172
        $machine = new PredictAuthors();
173
        $res = $machine->predictAuthorNames($auteur1);
174
175
        if (1 === count($res)) {
176
            // auteurs->auteur?
177
            return;
178 47
        }
179
        // Many authors... and empty "auteur2"
180
        if (count($res) >= 2 && empty($auteur2)) {
181 47
            // delete author-params
182 47
            array_map(
183 47
                function ($param) {
184 47
                    $this->unsetParam($param);
185 47
                },
186
                ['auteur', 'auteurs', 'prénom1', 'nom1']
187 47
            );
188 47
            // iterate and edit new values
189 47
            $count = count($res);
190 47
            for ($i = 0; $i < $count; ++$i) {
191
                $this->setParam(sprintf('auteur%s', $i + 1), $res[$i]);
192
            }
193 47
            $this->addSummaryLog('distinction auteurs');
194 42
            $this->major = true;
195
            $this->notCosmetic = true;
196
        }
197 5
    }
198 5
199
    /**
200 5
     * todo: move/implement.
201
     *
202 3
     * @param string|null $param
203
     *
204
     * @throws Exception
205 2
     */
206
    protected function processLang(?string $param = 'langue')
207 1
    {
208
        $lang = $this->getParam($param) ?? null;
0 ignored issues
show
Bug introduced by
It seems like $param can also be of type null; however, parameter $name of App\Domain\AbstractTemplateOptimizer::getParam() 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

208
        $lang = $this->getParam(/** @scrutinizer ignore-type */ $param) ?? null;
Loading history...
209 1
210 1
        if ($lang) {
211 1
            $lang2 = Language::all2wiki($lang);
212
213
            // strip "langue originale=fr"
214 1
            if ('langue originale' === $param && self::WIKI_LANGUAGE === $lang2
215 1
                && (!$this->getParam('langue') || $this->getParam('langue') === $lang2)
216 1
            ) {
217
                $this->unsetParam('langue originale');
218 1
                $this->addSummaryLog('-langue originale');
219 1
            }
220 1
221
            if ($lang2 && $lang !== $lang2) {
222 2
                $this->setParam($param, $lang2);
223
                if (self::WIKI_LANGUAGE !== $lang2) {
224
                    $this->addSummaryLog('±'.$param);
225
                }
226
            }
227
        }
228
    }
229
230
    /**
231 47
     * Find year of book publication.
232
     *
233 47
     * @return int|null
234
     * @throws Exception
235 47
     */
236 6
    protected function findBookYear(): ?int
237
    {
238
        $annee = $this->getParam('année');
239 6
        if (!empty($annee) && is_numeric($annee)) {
240 6
            return intval($annee);
241
        }
242 1
        $date = $this->getParam('date');
243 1
        if ($date && preg_match('#[^0-9]?([12][0-9][0-9][0-9])[^0-9]?#', $date, $matches) > 0) {
244
            return intval($matches[1]);
245
        }
246 6
247 4
        return null;
248 4
    }
249 3
250
    protected function stripIsbn(string $isbn): string
251
    {
252
        return trim(preg_replace('#[^0-9Xx]#', '', $isbn));
253 47
    }
254
255
    protected function processTitle()
256
    {
257
        $oldtitre = $this->getParam('titre');
258
        $this->langInTitle();
259
        $this->deWikifyExternalLink('titre');
260 47
        $this->upperCaseFirstLetter('titre');
261
        $this->typoDeuxPoints('titre');
262 47
263 47
        $this->extractSubTitle();
264 37
265
        // 20-11-2019 : Retiré majuscule à sous-titre
266
267
        if ($this->getParam('titre') !== $oldtitre) {
268 10
            $this->addSummaryLog('±titre');
269 10
        }
270
271
        $this->valideNumeroChapitre();
272 2
        $this->deWikifyExternalLink('titre chapitre');
273 2
        $this->upperCaseFirstLetter('titre chapitre');
274 2
    }
275 2
276 2
    protected function detectColon($param): bool
277 2
    {
278
        // > 0 don't count a starting colon ":bla"
279
        if ($this->hasParamValue($param) && mb_strrpos($this->getParam('titre'), ':') > 0) {
280
            return true;
281
        }
282
283
        return false;
284
    }
285
286
    protected function extractSubTitle(): void
287
    {
288
        // FIXED bug [[fu:bar]]
289
        if (!$this->getParam('titre') || WikiTextUtil::isWikify($this->getParam('titre'))) {
290 2
            return;
291
        }
292
293
        if (!$this->detectColon('titre')) {
294 8
            return;
295 8
        }
296 7
        // Que faire si déjà un sous-titre ?
297 1
        if ($this->hasParamValue('sous-titre')) {
298
            return;
299
        }
300 1
301 1
        // titre>5 and sous-titre>5 and sous-titre<40
302 1
        if (preg_match('#^(?<titre>[^:]{5,}):(?<st>.{5,40})$#', $this->getParam('titre'), $matches) > 0) {
303
            $this->setParam('titre', trim($matches['titre']));
304 1
            $this->setParam('sous-titre', trim($matches['st']));
305 1
            $this->addSummaryLog('>sous-titre');
306
        }
307
    }
308 1
309
    /**
310
     * Normalize a Google Book links.
311
     * Clean the useless URL parameters or transform into wiki-template.
312 7
     *
313 7
     * @param $param
314 7
     *
315
     * @throws Exception
316 1
     */
317
    protected function googleBookUrl(string $param): void
318
    {
319
        $url = $this->getParam($param);
320 7
        if (empty($url)
321 7
            || !GoogleBooksUtil::isGoogleBookURL($url)
322
        ) {
323 4
            return;
324
        }
325
326 4
        if (self::CONVERT_GOOGLEBOOK_TEMPLATE) {
327
            $template = GoogleLivresTemplate::createFromURL($url);
328
            if ($template) {
329
                $this->setParam($param, $template->serialize());
330
                $this->addSummaryLog('{Google}');
331
                $this->notCosmetic = true;
332 4
333 4
                return;
334
            }
335
        }
336 4
337 4
        try {
338 4
            $goo = GoogleBooksUtil::simplifyGoogleUrl($url);
339
        } catch (DomainException $e) {
340
            // ID manquant ou malformé
341
            $errorValue = sprintf(
342
                '%s <!-- ERREUR %s -->',
343 4
                $url,
344
                $e->getMessage()
345
            );
346
            $this->setParam($param, $errorValue);
347
            $this->addSummaryLog('erreur URL');
348
            $this->notCosmetic = true;
349 7
            $this->major = true;
350 7
        }
351 7
352
        if (!empty($goo) && $goo !== $url) {
353
            $this->setParam($param, $goo);
354 7
            // cleaned tracking parameters in Google URL ?
355
            if (GoogleBooksUtil::isTrackingUrl($url)) {
356
                $this->addSummaryLog('tracking');
357
                $this->notCosmetic = true;
358
            }
359
        }
360
    }
361
362 10
    /**
363
     * - {{lang|...}} dans titre => langue=... puis titre nettoyé
364 10
     *  langue=L’utilisation de ce paramètre permet aussi aux synthétiseurs vocaux de reconnaître la langue du titre de
365 10
     * l’ouvrage.
366 1
     * 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.
367
     *
368 9
     * @throws Exception
369 9
     */
370 1
    protected function langInTitle(): void
371
    {
372
        if (preg_match(
373 8
                '#^{{ ?(?:lang|langue) ?\| ?([a-z-]{2,5}) ?\| ?(?:texte=)?([^{}=]+)(?:\|dir=rtl)?}}$#i',
374
                $this->getParam('titre'),
375
                $matches
376 9
            ) > 0
377
        ) {
378 9
            $lang = trim($matches[1]);
379
            $newtitre = str_replace($matches[0], trim($matches[2]), $this->getParam('titre'));
380
381 47
            // problème : titre anglais de livre français
382
            // => conversion {{lang}} du titre seulement si langue= défini
383 47
            // opti : restreindre à ISBN zone 2 fr ?
384 47
            if ($lang === $this->getParam('langue')) {
385 47
                $this->setParam('titre', $newtitre);
386 47
                $this->addSummaryLog('°titre');
387 47
            }
388
389 47
            // desactivé à cause de l'exception décrite ci-dessus
390
            // si langue=VIDE : ajout langue= à partir de langue titre
391
            //            if (self::WIKI_LANGUAGE !== $lang && empty($this->getParam('langue'))) {
392
            //                $this->setParam('langue', $lang);
393 47
            //                $this->log('+langue='.$lang);
394 5
            //            }
395
        }
396
    }
397 47
398 47
    protected function processDates()
399 47
    {
400 47
        // dewikification
401
        $params = ['date', 'année', 'mois', 'jour'];
402 13
        foreach ($params as $param) {
403
            if ($this->hasParamValue($param) && WikiTextUtil::isWikify(' '.$this->getParam($param))) {
404
                $this->setParam($param, WikiTextUtil::unWikify($this->getParam($param)));
405 13
            }
406 3
        }
407
408
        try {
409 10
            $this->moveDate2Year();
410
        } catch (Exception $e) {
411
            $this->log->warning('Exception '.$e->getMessage());
412 47
        }
413
    }
414
415 47
    /**
416 34
     * Bool ?
417
     * déwikification du titre : consensus Bistro 27 août 2011
418
     * idem  'titre chapitre'.
419 13
     *
420 10
     * @param string $param
421
     *
422
     * @throws Exception
423 3
     */
424
    protected function deWikifyExternalLink(string $param): void
425
    {
426
        if (empty($this->getParam($param))) {
427
            return;
428 3
        }
429 3
        if (preg_match('#^\[(http[^ \]]+) ([^]]+)]#i', $this->getParam($param), $matches) > 0) {
430 3
            $this->setParam($param, str_replace($matches[0], $matches[2], $this->getParam($param)));
431 3
            $this->addSummaryLog('±'.$param);
432
433 3
            if (in_array($param, ['titre', 'titre chapitre'])) {
434
                if (empty($this->getParam('lire en ligne'))) {
435
                    $this->setParam('lire en ligne', $matches[1]);
436
                    $this->addSummaryLog('+lire en ligne');
437
438
                    return;
439
                }
440
                $this->addSummaryLog('autre lien externe: '.$matches[1]);
441
            }
442
        }
443 47
    }
444
445 47
    protected function upperCaseFirstLetter($param)
446 47
    {
447 47
        if (!$this->hasParamValue($param)) {
448
            return;
449 47
        }
450
        $newValue = TextUtil::mb_ucfirst(trim($this->getParam($param)));
451
        $this->setParam($param, $newValue);
452 1
    }
453
454
    /**
455
     * Typo internationale 'titre : sous-titre'.
456
     * Fix fantasy typo of subtitle with '. ' or ' - '.
457
     * International Standard Bibliographic Description :
458
     * https://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Le_Bistro/13_janvier_2016#Modif_du_mod%C3%A8le:Ouvrage.
459
     *
460
     * @param $param
461
     *
462
     * @throws Exception
463
     */
464 1
    protected function typoDeuxPoints($param)
465
    {
466
        $origin = $this->getParam($param) ?? '';
467
        if (empty($origin)) {
468
            return;
469
        }
470
        // FIXED bug [[fu:bar]]
471
        if (WikiTextUtil::isWikify($origin)) {
472
            return;
473
        }
474
475
        $origin = TextUtil::replaceNonBreakingSpaces($origin);
476
477
        $strTitle = $origin;
478 1
479 1
        // CORRECTING TYPO FANTASY OF SUBTITLE
480
481 1
        // Replace first '.' by ':' if no ': ' and no numbers around (as PHP 7.3)
482 1
        // exlude pattern "blabla... blabla"
483 1
        // TODO: statistics
484
485
        // Replace ' - ' or ' / ' (spaced!) by ' : ' if no ':' and no numbers after (as PHP 7.3 or 1939-1945)
486 1
        if (!mb_strpos(':', $strTitle) && preg_match('#.{6,} ?[-/] ?[^0-9)]{6,}#', $strTitle) > 0) {
487
            $strTitle = preg_replace('#(.{6,}) [-/] ([^0-9)]{6,})#', '$1 : $2', $strTitle);
488
        }
489
490
        // international typo style " : " (first occurrence)
491
        $strTitle = preg_replace('#[ ]*:[ ]*#', ' : ', $strTitle);
492
493
        if ($strTitle !== $origin) {
494
            $this->setParam($param, $strTitle);
495
            $this->addSummaryLog(sprintf(':%s', $param));
496 47
        }
497
    }
498 47
499 47
    protected function valideNumeroChapitre()
500 47
    {
501 47
        $value = $this->getParam('numéro chapitre');
502 47
        if (empty($value)) {
503
            return;
504
        }
505
        // "12" ou "VI", {{II}}, II:3
506
        if (preg_match('#^[0-9IVXL\-.:{}]+$#i', $value) > 0) {
507
            return;
508
        }
509
        // déplace vers "titre chapitre" ?
510
        if (!$this->getParam('titre chapitre')) {
511
            $this->unsetParam('numéro chapitre');
512
            $this->setParam('titre chapitre', $value);
513
        }
514
        $this->addSummaryLog('≠numéro chapitre');
515
    }
516
517
    /**
518
     * TODO move+refac
519
     * TODO CommentaireBiblioTemplate  ExtraitTemplate
520
     * Probleme {{commentaire biblio}} <> {{commentaire biblio SRL}}
521
     * Generate supplementary templates from obsoletes params.
522 47
     *
523
     * @throws Exception
524 47
     */
525
    protected function externalTemplates()
526
    {
527 47
        // "extrait=bla" => {{citation bloc|bla}}
528 47
        if ($this->hasParamValue('extrait')) {
529 47
            $extrait = $this->getParam('extrait');
530 1
            // todo bug {{citation bloc}} si "=" ou "|" dans texte de citation
531
            // Legacy : use {{début citation}} ... {{fin citation}}
532
            if (preg_match('#[=|]#', $extrait) > 0) {
533
                $this->optiTemplate->externalTemplates[] = (object)[
534
                    'template' => 'début citation',
535 47
                    '1' => '',
536
                    'raw' => '{{Début citation}}'.$extrait.'{{Fin citation}}',
537
                ];
538
                $this->addSummaryLog('{Début citation}');
539 47
                $this->notCosmetic = true;
540
            } else {
541
                // StdClass
542
                $this->optiTemplate->externalTemplates[] = (object)[
543
                    'template' => 'citation bloc',
544
                    '1' => $extrait,
545
                    'raw' => '{{Citation bloc|'.$extrait.'}}',
546
                ];
547 47
                $this->addSummaryLog('{Citation bloc}');
548
                $this->notCosmetic = true;
549 47
            }
550 45
551
            $this->unsetParam('extrait');
552 2
            $this->notCosmetic = true;
553
        }
554 2
555 2
        // "commentaire=bla" => {{Commentaire biblio|1=bla}}
556
        if ($this->hasParamValue('commentaire')) {
557 1
            $commentaire = $this->getParam('commentaire');
558
            $this->optiTemplate->externalTemplates[] = (object)[
559
                'template' => 'commentaire biblio',
560
                '1' => $commentaire,
561 2
                'raw' => '{{Commentaire biblio|'.$commentaire.'}}',
562
            ];
563
            $this->unsetParam('commentaire');
564
            $this->addSummaryLog('{commentaire}');
565
            $this->notCosmetic = true;
566
        }
567 2
    }
568 2
569 2
    // ----------------------
570
    // ----------------------
571 2
    // ----------------------
572
573
    /**
574
     * Date->année (nécessaire pour OuvrageComplete).
575 2
     *
576 2
     * @throws Exception
577 2
     */
578 2
    protected function moveDate2Year()
579 2
    {
580 2
        $date = $this->getParam('date') ?? false;
581 2
        if ($date) {
582 2
            if (preg_match('#^-?[12][0-9][0-9][0-9]$#', $date)) {
583
                $this->setParam('année', $date);
584
                $this->unsetParam('date');
585
                //$this->log('>année');
586 2
            }
587
        }
588
    }
589
590
    protected function predictFormatByPattern()
591
    {
592
        if (($value = $this->getParam('format'))) {
593
            // predict if 'format électronique'
594
            // format electronique lié au champ 'lire en ligne'
595
            // 2015 https://fr.wikipedia.org/wiki/Discussion_mod%C3%A8le:Ouvrage#format,_format_livre,_format_%C3%A9lectronique
596 47
            //            if (preg_match('#(pdf|epub|html|kindle|audio|\{\{aud|jpg)#i', $value) > 0) {
597
            //
598 47
            //                $this->setParam('format électronique', $value);
599
            //                $this->unsetParam('format');
600
            //                $this->log('format:électronique?');
601 47
            //
602
            //                return;
603 47
            //            }
604
            if (preg_match(
605
                    '#(ill\.|couv\.|in-[0-9]|in-fol|poche|broché|relié|{{unité|{{Dunité|[0-9]{2} ?cm|\|cm}}|vol\.|A4)#i',
606 35
                    $value
607
                ) > 0
608
            ) {
609 35
                $this->setParam('format livre', $value);
610 35
                $this->unsetParam('format');
611
                $this->addSummaryLog('format:livre?');
612 35
                $this->notCosmetic = true;
613
            }
614 26
            // Certainement 'format électronique'...
615
        }
616 26
    }
617 26
618
    /**
619 26
     * todo : vérif lien rouge
620
     * todo 'lien éditeur' affiché 1x par page
621
     * opti : Suppression lien éditeur si c'est l'article de l'éditeur.
622
     *
623
     * @throws Exception
624
     */
625
    protected function processEditeur()
626
    {
627
        $editeur = $this->getParam('éditeur');
628
        if (empty($editeur)) {
629
            return;
630 47
        }
631
632 47
        // FIX bug "GEO Art ([[Prisma Media]]) ; [[Le Monde]]"
633 47
        if (preg_match('#\[.*\[.*\[#', $editeur) > 0) {
634
            return;
635 15
        }
636 1
        // FIX bug "[[Fu|Bar]] bla" => [[Fu|Bar bla]]
637 1
        if (preg_match('#(.+\[\[|\]\].+)#', $editeur) > 0) {
638
            return;
639 1
        }
640 1
641 1
        // [[éditeur]]
642 1
        if (preg_match('#\[\[([^|]+)]]#', $editeur, $matches) > 0) {
643
            $editeurUrl = $matches[1];
644 1
        }
645
        // [[bla|éditeur]]
646
        if (preg_match('#\[\[([^]|]+)\|.+]]#', $editeur, $matches) > 0) {
647
            $editeurUrl = $matches[1];
648
        }
649 14
650
        // Todo : traitement/suppression des abréviations communes :
651 47
        // ['éd. de ', 'éd. du ', 'éd.', 'ed.', 'Éd. de ', 'Éd.', 'édit.', 'Édit.', '(éd.)', '(ed.)', 'Ltd.']
652
653 47
        $editeurStr = WikiTextUtil::unWikify($editeur);
654 47
        // On garde minuscule sur éditeur, pour nuance Éditeur/éditeur permettant de supprimer "éditeur"
655
        // ex: "éditions Milan" => "Milan"
656 15
657 15
        // Déconseillé : 'lien éditeur' (obsolete 2019)
658 15
        if ($this->hasParamValue('lien éditeur')) {
659
            if (empty($editeurUrl)) {
660
                $editeurUrl = $this->getParam('lien éditeur');
661
            }
662
            $this->unsetParam('lien éditeur');
663
        }
664
665
        if (empty($editeurUrl)) {
666
            $editeurUrl = $this->predictPublisherWikiTitle($editeurStr);
667
            if (!empty($editeurUrl) && $this->wikiPageTitle !== $editeurUrl) {
668
                $this->addSummaryLog('+lien éditeur');
669
                $this->notCosmetic = true;
670 47
                $this->major = true;
671
            }
672 47
        }
673 47
674 32
675
        $newEditeur = $editeurStr;
676
        if (!empty($editeurUrl)) {
677 15
            $newEditeur = WikiTextUtil::wikilink($editeurStr, $editeurUrl);
678 2
        }
679
680
        if ($newEditeur !== $editeur) {
681 13
            $this->setParam('éditeur', $newEditeur);
682
            $this->addSummaryLog('±éditeur');
683 13
            $this->notCosmetic = true;
684
        }
685
    }
686
687
    /**
688
     * todo move (cf. Article/Lien web optimizing)
689
     *
690
     * @param string $publisherName
691
     *
692 13
     * @return string|null
693 4
     */
694
    public function predictPublisherWikiTitle(string $publisherName): ?string
695
    {
696
        try {
697 13
            $data = json_decode(file_get_contents(self::PUBLISHER_FRWIKI_FILENAME), true);
698
        } catch (\Throwable $e) {
699 13
            $this->log->error('Catch EDITOR_TITLES_FILENAME import '.$e->getMessage());
700 3
        }
701 3
        if (isset($data[$publisherName])) {
702
            return (string)urldecode($data[$publisherName]);
703 13
        }
704
705 47
        return null;
706
    }
707 47
708 47
    /**
709 47
     * {Cite book}:"edition" [ordinal number] => {ouvrage}::"numéro d'édition" (ou "réimpression" [année])
710
     * {Cite book}:origyear => {ouvrage}:"année première édition"
711
     * https://wstat.fr/template/index.php?title=Ouvrage&query=paramvalue&param=edition&limit=5000&searchtext=.&searchre=1
712
     * Pas mal de corrupted sur "éditions"
713
     * https://wstat.fr/template/index.php?title=Ouvrage&query=paramvalue&param=%C3%A9dition&limit=5000&searchtext=.&searchre=1
714
     */
715
    private function processEditionCitebook(): void
716
    {
717
        $this->correctReimpressionByParam("numéro d'édition");
718
        $this->correctReimpressionByParam("éditeur-doublon");
719
        $this->correctReimpressionByParam("éditeur");
720
    }
721
722
    private function correctReimpressionByParam(string $param): void
723 9
    {
724
        $editionNumber = $this->getParam($param);
725 9
        if (!empty($editionNumber) && $this->isEditionYear($editionNumber)) {
726 9
            $this->unsetParam($param);
727
            $this->setParam('réimpression', $editionNumber);
728
            $this->addSummaryLog('+réimpression');
729
            $this->notCosmetic = true;
730
731
            return;
732
        }
733
734
        $editionOrdinal = $this->getEditionOrdinalNumber($editionNumber);
735
        if (!empty($editionNumber) && !$this->isEditionYear($editionNumber) && $editionOrdinal) {
736 47
            $this->unsetParam($param);
737
            $this->setParam("numéro d'édition", $editionOrdinal);
738
            $this->addSummaryLog("±numéro d'édition");
739 47
            $this->notCosmetic = true;
740 1
        }
741
    }
742
743 1
    private function getEditionOrdinalNumber(?string $str): ?string
744
    {
745
        if (!$str) {
746
            return null;
747
        }
748
        // {{5e}}
749
        if (preg_match('#^\{\{([0-9]+)e\}\}$#', $str, $matches)) {
750
            return $matches[1];
751
        }
752
        // "1st ed."
753 1
        if (preg_match(
754 1
            '#^([0-9]+) ?(st|nd|rd|th|e|ème)? ?(ed|ed\.|edition|reprint|published|publication)?$#i',
755 1
            $str,
756 1
            $matches
757
        )
758 1
        ) {
759 1
            return $matches[1];
760
        }
761
762 1
        return null;
763 1
    }
764
765
    private function isEditionYear(string $str): bool
766
    {
767 47
        if (preg_match('#^[0-9]{4}$#', $str) && intval($str) > 1700 && intval($str) < 2025) {
768 1
            return true;
769 1
        }
770 1
771 1
        return false;
772 1
    }
773
}
774