Passed
Push — dev ( b8ced8...f7ea32 )
by Dispositif
07:14
created

OuvrageOptimize::convertLienAuteurTitre()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
c 0
b 0
f 0
nc 3
nop 0
dl 0
loc 15
rs 9.9332
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->convertLienAuteurTitre();
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
        // translation : "London"->"Londres" seulement si langue=fr
109
        if($this->hasParamValue('langue')
110
        && $this->getParam('langue') === self::WIKI_LANGUAGE ) {
111
            $manager = new FileManager();
112
            $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
113
            if (!empty($row) && !empty($row[1])) {
114
                $this->setParam('lieu', $row[1]);
115
                $this->log('lieu francisé');
116
                $this->notCosmetic = true;
117
            }
118
        }
119
    }
120
121
    private function processBnf()
122
    {
123
        $bnf = $this->getParam('bnf');
124
        if (!$bnf) {
125
            return;
126
        }
127
        $bnf = str_ireplace('FRBNF', '', $bnf);
128
        $this->setParam('bnf', $bnf);
129
    }
130
131
    /**
132
     * @throws Exception
133
     */
134
    private function processAuthors()
135
    {
136
        $this->distinguishAuthors();
137
        //$this->fusionFirstNameAndName(); // desactived : no consensus
138
    }
139
140
    private function convertLienAuteurTitre(): void
141
    {
142
        $auteurParams = ['auteur1', 'auteur2', 'auteur2', 'titre'];
143
        foreach ($auteurParams as $auteurParam) {
144
            if ($this->hasParamValue($auteurParam)
145
                && $this->hasParamValue('lien '.$auteurParam)
146
            ) {
147
                $this->setParam(
148
                    $auteurParam,
149
                    WikiTextUtil::wikilink(
150
                        $this->getParam($auteurParam),
0 ignored issues
show
Bug introduced by
It seems like $this->getParam($auteurParam) can also be of type null; however, parameter $strLink of App\Domain\Utils\WikiTextUtil::wikilink() 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

150
                        /** @scrutinizer ignore-type */ $this->getParam($auteurParam),
Loading history...
151
                        $this->getParam('lien '.$auteurParam)
152
                    )
153
                );
154
                $this->unsetParam('lien '.$auteurParam);
155
            }
156
        }
157
    }
158
159
    /**
160
     * Detect and correct multiple authors in same parameter.
161
     * Like "auteurs=J. M. Waller, M. Bigger, R. J. Hillocks".
162
     *
163
     * @throws Exception
164
     */
165
    private function distinguishAuthors()
166
    {
167
        // merge params of author 1
168
        $auteur1 = $this->getParam('auteur') ?? '';
169
        $auteur1 .= $this->getParam('auteurs') ?? '';
170
        $auteur1 .= $this->getParam('prénom1') ?? '';
171
        $auteur1 .= ' '.$this->getParam('nom1') ?? '';
172
        $auteur1 = trim($auteur1);
173
        // of authors 2
174
        $auteur2 = $this->getParam('auteur2') ?? '';
175
        $auteur2 .= $this->getParam('prénom2') ?? '';
176
        $auteur2 .= ' '.$this->getParam('nom2') ?? '';
177
        $auteur2 = trim($auteur2);
178
179
        // skip if wikilink in author
180
        if (empty($auteur1) || WikiTextUtil::isWikify($auteur1)) {
181
            return;
182
        }
183
184
        $machine = new PredictAuthors();
185
        $res = $machine->predictAuthorNames($auteur1);
186
187
        if (1 === count($res)) {
188
            // auteurs->auteur?
189
            return;
190
        }
191
        // Many authors... and empty "auteur2"
192
        if (count($res) >= 2 && empty($auteur2)) {
193
            // delete author-params
194
            array_map(
195
                function ($param) {
196
                    $this->unsetParam($param);
197
                },
198
                ['auteur', 'auteurs', 'prénom1', 'nom1']
199
            );
200
            // iterate and edit new values
201
            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...
202
                $this->setParam(sprintf('auteur%s', $i + 1), $res[$i]);
203
            }
204
            $this->log('distinction auteurs');
205
            $this->major = true;
206
            $this->notCosmetic = true;
207
        }
208
    }
209
210
    /**
211
     * todo: move/implement.
212
     *
213
     * @param string|null $param
214
     *
215
     * @throws Exception
216
     */
217
    private function processLang(?string $param = 'langue')
218
    {
219
        $lang = $this->getParam($param) ?? null;
220
221
        if ($lang) {
222
            $lang2 = Language::all2wiki($lang);
223
224
            // strip "langue originale=fr"
225
            if ('langue originale' === $param && self::WIKI_LANGUAGE === $lang2
226
                && (!$this->getParam('langue') || $this->getParam('langue') === $lang2)
227
            ) {
228
                $this->unsetParam('langue originale');
229
                $this->log('-langue originale');
230
            }
231
232
            if ($lang2 && $lang !== $lang2) {
233
                $this->setParam($param, $lang2);
234
                if (self::WIKI_LANGUAGE !== $lang2) {
235
                    $this->log('±'.$param);
236
                }
237
            }
238
        }
239
    }
240
241
    /**
242
     * Validate or correct ISBN.
243
     *
244
     * @throws Exception
245
     */
246
    private function processIsbn()
247
    {
248
        $isbn = $this->getParam('isbn') ?? '';
249
        if (empty($isbn)) {
250
            return;
251
        }
252
253
        // ISBN-13 à partir de 2007
254
        $year = $this->findBookYear();
255
        if ($year !== null && $year < 2007 && 10 === strlen($this->stripIsbn($isbn))) {
256
            // juste mise en forme ISBN-10 pour 'isbn'
257
            try {
258
                $isbnMachine = new IsbnFacade($isbn);
259
                $isbnMachine->validate();
260
                $isbn10pretty = $isbnMachine->format('ISBN-10');
261
                if ($isbn10pretty !== $isbn) {
262
                    $this->setParam('isbn', $isbn10pretty);
263
                    $this->log('ISBN10');
264
                    //                    $this->notCosmetic = true;
265
                }
266
            } catch (\Throwable $e) {
267
                // ISBN not validated
268
                $this->setParam(
269
                    'isbn invalide',
270
                    sprintf('%s %s', $isbn, $e->getMessage() ?? '')
271
                );
272
                $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
273
                $this->notCosmetic = true;
274
            }
275
276
            return;
277
        }
278
279
        try {
280
            $isbnMachine = new IsbnFacade($isbn);
281
            $isbnMachine->validate();
282
            $isbn13 = $isbnMachine->format('ISBN-13');
283
        } catch (Throwable $e) {
284
            // ISBN not validated
285
            // TODO : bot ISBN invalide (queue, message PD...)
286
            $this->setParam(
287
                'isbn invalide',
288
                sprintf('%s %s', $isbn, $e->getMessage() ?? '')
289
            );
290
            $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
291
            $this->notCosmetic = true;
292
293
            // TODO log file ISBNinvalide
294
            return;
295
        }
296
297
        // Si $isbn13 et 'isbn2' correspond à ISBN-13 => suppression
298
        if (isset($isbn13)
299
            && $this->hasParamValue('isbn2')
300
            && $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

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

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

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