Passed
Push — master ( 5eccc7...1faa52 )
by Dispositif
02:45
created

OuvrageOptimize   F

Complexity

Total Complexity 149

Size/Duplication

Total Lines 897
Duplicated Lines 0 %

Test Coverage

Coverage 82.89%

Importance

Changes 25
Bugs 0 Features 3
Metric Value
eloc 370
dl 0
loc 897
ccs 344
cts 415
cp 0.8289
rs 2
c 25
b 0
f 3
wmc 149

34 Methods

Rating   Name   Duplication   Size   Complexity  
A convertLienAuteurTitre() 0 15 4
A findBookYear() 0 12 5
A __construct() 0 6 1
B processLocation() 0 27 7
A processBnf() 0 8 2
B distinguishAuthors() 0 43 7
A doTasks() 0 25 1
B processLang() 0 19 9
A processAuthors() 0 3 1
A stripIsbn() 0 3 1
D processIsbn() 0 92 20
A unsetParam() 0 3 1
A langInTitle() 0 17 3
A hasParamValue() 0 3 1
A externalTemplates() 0 41 4
A log() 0 4 2
A getOuvrage() 0 3 1
A checkMajorEdit() 0 24 6
A processTitle() 0 19 2
A predictFormatByPattern() 0 23 3
A moveDate2Year() 0 7 3
A getSummaryLog() 0 3 1
A processDates() 0 14 5
A valideNumeroChapitre() 0 16 4
B cleanAndPredictErrorParameters() 0 36 10
A typoDeuxPoints() 0 32 6
A detectColon() 0 8 3
A deWikifyExternalLink() 0 17 5
B googleBookUrl() 0 41 9
A setParam() 0 5 3
B processEditeur() 0 49 10
A getParam() 0 3 1
A upperCaseFirstLetter() 0 7 2
A extractSubTitle() 0 20 6

How to fix   Complexity   

Complex Class

Complex classes like OuvrageOptimize often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use OuvrageOptimize, and based on these observations, apply Extract Interface, too.

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\Publisher\GoogleBooksUtil;
16
use App\Domain\Utils\TextUtil;
17
use App\Domain\Utils\WikiTextUtil;
18
use App\Infrastructure\FileManager;
19
use DomainException;
20
use Exception;
21
use Psr\Log\LoggerInterface;
22
use Psr\Log\NullLogger;
23
use Throwable;
24
25
use function mb_strlen;
26
27
/**
28
 * Legacy.
29
 * TODO move methods to OuvrageClean setters
30
 * TODO AbstractProcess
31
 * TODO observer/event (log, MajorEdition)
32
 * Class OuvrageProcess.
33
 */
34
class OuvrageOptimize
35
{
36
    const CONVERT_GOOGLEBOOK_TEMPLATE = false; // change OuvrageOptimizeTest !!
37
38
    const WIKI_LANGUAGE = 'fr';
39
40
    protected $original;
41
42
    private $wikiPageTitle;
43
44
    private $summaryLog = [];
45
46
    public $notCosmetic = false;
47
48
    public $major = false;
49
50
    private $ouvrage;
51
52
    // todo inject TextUtil + ArticleVersion ou WikiRef
53
    /**
54
     * @var LoggerInterface|NullLogger
55
     */
56
    private $log;
57
58 47
    public function __construct(OuvrageTemplate $ouvrage, $wikiPageTitle = null, ?LoggerInterface $log = null)
59
    {
60 47
        $this->original = $ouvrage;
61 47
        $this->ouvrage = clone $ouvrage;
62 47
        $this->wikiPageTitle = ($wikiPageTitle) ?? null;
63 47
        $this->log = $log ?? new NullLogger();
64 47
    }
65
66
    /**
67
     * @return $this
68
     * @throws Exception
69
     */
70 47
    public function doTasks(): self
71
    {
72 47
        $this->cleanAndPredictErrorParameters();
73
74 47
        $this->processAuthors();
75
76 47
        $this->processLang();
77 47
        $this->processLang('langue originale');
78
79 47
        $this->processTitle();
80 47
        $this->convertLienAuteurTitre();
81 47
        $this->processEditeur();
82 47
        $this->processDates();
83 47
        $this->externalTemplates();
84 47
        $this->predictFormatByPattern();
85
86 47
        $this->processIsbn();
87 47
        $this->processBnf();
88
89 47
        $this->processLocation(); // 'lieu'
90
91 47
        $this->GoogleBookURL('lire en ligne');
92 47
        $this->GoogleBookURL('présentation en ligne');
93
94 47
        return $this;
95
    }
96
97
    /**
98
     * Todo: injection dep.
99
     * Todo : "[s. l.]" sans lieu "s.l.n.d." sans lieu ni date.
100
     *
101
     * @throws Exception
102
     */
103 47
    private function processLocation()
104
    {
105 47
        $location = $this->getParam('lieu');
106 47
        if (empty($location)) {
107 42
            return;
108
        }
109
110
        // typo and unwikify
111 5
        $memo = $location;
112 5
        $location = WikiTextUtil::unWikify($location);
113 5
        $location = TextUtil::mb_ucfirst($location);
114 5
        if ($memo !== $location) {
115 1
            $this->setParam('lieu', $location);
116 1
            $this->log('±lieu');
117 1
            $this->notCosmetic = true;
118
        }
119
120
        // translation : "London"->"Londres" seulement si langue=fr
121 5
        if ($this->hasParamValue('langue')
122 5
            && $this->getParam('langue') === self::WIKI_LANGUAGE
123
        ) {
124 1
            $manager = new FileManager();
125 1
            $row = $manager->findCSVline(__DIR__.'/resources/traduction_ville.csv', $location);
126 1
            if (!empty($row) && !empty($row[1])) {
127 1
                $this->setParam('lieu', $row[1]);
128 1
                $this->log('lieu francisé');
129 1
                $this->notCosmetic = true;
130
            }
131
        }
132 5
    }
133
134 47
    private function processBnf()
135
    {
136 47
        $bnf = $this->getParam('bnf');
137 47
        if (!$bnf) {
138 46
            return;
139
        }
140 1
        $bnf = str_ireplace('FRBNF', '', $bnf);
141 1
        $this->setParam('bnf', $bnf);
142 1
    }
143
144
    /**
145
     * @throws Exception
146
     */
147 47
    private function processAuthors()
148
    {
149 47
        $this->distinguishAuthors();
150
        //$this->fusionFirstNameAndName(); // desactived : no consensus
151 47
    }
152
153 47
    private function convertLienAuteurTitre(): void
154
    {
155 47
        $auteurParams = ['auteur1', 'auteur2', 'auteur2', 'titre'];
156 47
        foreach ($auteurParams as $auteurParam) {
157 47
            if ($this->hasParamValue($auteurParam)
158 47
                && $this->hasParamValue('lien '.$auteurParam)
159
            ) {
160 1
                $this->setParam(
161 1
                    $auteurParam,
162 1
                    WikiTextUtil::wikilink(
163 1
                        $this->getParam($auteurParam),
164 1
                        $this->getParam('lien '.$auteurParam)
165
                    )
166
                );
167 1
                $this->unsetParam('lien '.$auteurParam);
168
            }
169
        }
170 47
    }
171
172
    /**
173
     * Detect and correct multiple authors in same parameter.
174
     * Like "auteurs=J. M. Waller, M. Bigger, R. J. Hillocks".
175
     *
176
     * @throws Exception
177
     */
178 47
    private function distinguishAuthors()
179
    {
180
        // merge params of author 1
181 47
        $auteur1 = $this->getParam('auteur') ?? '';
182 47
        $auteur1 .= $this->getParam('auteurs') ?? '';
183 47
        $auteur1 .= $this->getParam('prénom1') ?? '';
184 47
        $auteur1 .= ' '.$this->getParam('nom1') ?? '';
185 47
        $auteur1 = trim($auteur1);
186
        // of authors 2
187 47
        $auteur2 = $this->getParam('auteur2') ?? '';
188 47
        $auteur2 .= $this->getParam('prénom2') ?? '';
189 47
        $auteur2 .= ' '.$this->getParam('nom2') ?? '';
190 47
        $auteur2 = trim($auteur2);
191
192
        // skip if wikilink in author
193 47
        if (empty($auteur1) || WikiTextUtil::isWikify($auteur1)) {
194 42
            return;
195
        }
196
197 5
        $machine = new PredictAuthors();
198 5
        $res = $machine->predictAuthorNames($auteur1);
199
200 5
        if (1 === count($res)) {
201
            // auteurs->auteur?
202 3
            return;
203
        }
204
        // Many authors... and empty "auteur2"
205 2
        if (count($res) >= 2 && empty($auteur2)) {
206
            // delete author-params
207 1
            array_map(
208
                function ($param) {
209 1
                    $this->unsetParam($param);
210 1
                },
211 1
                ['auteur', 'auteurs', 'prénom1', 'nom1']
212
            );
213
            // iterate and edit new values
214 1
            $count = count($res);
215 1
            for ($i = 0; $i < $count; ++$i) {
216 1
                $this->setParam(sprintf('auteur%s', $i + 1), $res[$i]);
217
            }
218 1
            $this->log('distinction auteurs');
219 1
            $this->major = true;
220 1
            $this->notCosmetic = true;
221
        }
222 2
    }
223
224
    /**
225
     * todo: move/implement.
226
     *
227
     * @param string|null $param
228
     *
229
     * @throws Exception
230
     */
231 47
    private function processLang(?string $param = 'langue')
232
    {
233 47
        $lang = $this->getParam($param) ?? null;
234
235 47
        if ($lang) {
236 6
            $lang2 = Language::all2wiki($lang);
237
238
            // strip "langue originale=fr"
239 6
            if ('langue originale' === $param && self::WIKI_LANGUAGE === $lang2
240 6
                && (!$this->getParam('langue') || $this->getParam('langue') === $lang2)
241
            ) {
242 1
                $this->unsetParam('langue originale');
243 1
                $this->log('-langue originale');
244
            }
245
246 6
            if ($lang2 && $lang !== $lang2) {
247 4
                $this->setParam($param, $lang2);
248 4
                if (self::WIKI_LANGUAGE !== $lang2) {
249 3
                    $this->log('±'.$param);
250
                }
251
            }
252
        }
253 47
    }
254
255
    /**
256
     * Validate or correct ISBN.
257
     *
258
     * @throws Exception
259
     */
260 47
    private function processIsbn()
261
    {
262 47
        $isbn = $this->getParam('isbn') ?? '';
263 47
        if (empty($isbn)) {
264 37
            return;
265
        }
266
267
        // ISBN-13 à partir de 2007
268 10
        $year = $this->findBookYear();
269 10
        if ($year !== null && $year < 2007 && 10 === strlen($this->stripIsbn($isbn))) {
270
            // juste mise en forme ISBN-10 pour 'isbn'
271
            try {
272 2
                $isbnMachine = new IsbnFacade($isbn);
273 2
                $isbnMachine->validate();
274 2
                $isbn10pretty = $isbnMachine->format('ISBN-10');
275 2
                if ($isbn10pretty !== $isbn) {
276 2
                    $this->setParam('isbn', $isbn10pretty);
277 2
                    $this->log('ISBN10');
278
                    //                    $this->notCosmetic = true;
279
                }
280
            } catch (Throwable $e) {
281
                // ISBN not validated
282
                $this->setParam(
283
                    'isbn invalide',
284
                    sprintf('%s %s', $isbn, $e->getMessage() ?? '')
285
                );
286
                $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
287
                $this->notCosmetic = true;
288
            }
289
290 2
            return;
291
        }
292
293
        try {
294 8
            $isbnMachine = new IsbnFacade($isbn);
295 8
            $isbnMachine->validate();
296 7
            $isbn13 = $isbnMachine->format('ISBN-13');
297 1
        } catch (Throwable $e) {
298
            // ISBN not validated
299
            // TODO : bot ISBN invalide (queue, message PD...)
300 1
            $this->setParam(
301 1
                'isbn invalide',
302 1
                sprintf('%s %s', $isbn, $e->getMessage() ?? '')
303
            );
304 1
            $this->log(sprintf('ISBN invalide: %s', $e->getMessage()));
305 1
            $this->notCosmetic = true;
306
307
            // TODO log file ISBNinvalide
308 1
            return;
309
        }
310
311
        // Si $isbn13 et 'isbn2' correspond à ISBN-13 => suppression
312 7
        if (isset($isbn13)
313 7
            && $this->hasParamValue('isbn2')
314 7
            && $this->stripIsbn($this->getParam('isbn2')) === $this->stripIsbn($isbn13)
315
        ) {
316 1
            $this->unsetParam('isbn2');
317
        }
318
319
        // ISBN-10 ?
320 7
        $stripIsbn = $this->stripIsbn($isbn);
321 7
        if (10 === mb_strlen($stripIsbn)) {
322
            // ajout des tirets
323 4
            $isbn10pretty = $isbn;
324
325
            try {
326 4
                $isbn10pretty = $isbnMachine->format('ISBN-10');
327
            } catch (Throwable $e) {
328
                unset($e);
329
            }
330
331
            // archivage ISBN-10 dans 'isbn2'
332 4
            if (!$this->getParam('isbn2')) {
333 4
                $this->setParam('isbn2', $isbn10pretty);
334
            }
335
            // sinon dans 'isbn3'
336 4
            if ($this->hasParamValue('isbn2')
337 4
                && $this->stripIsbn($this->getParam('isbn2')) !== $stripIsbn
338 4
                && empty($this->getParam('isbn3'))
339
            ) {
340
                $this->setParam('isbn3', $isbn10pretty);
341
            }
342
            // delete 'isbn10' (en attendant modification modèle)
343 4
            if ($this->hasParamValue('isbn10') && $this->stripIsbn($this->getParam('isbn10')) === $stripIsbn) {
344
                $this->unsetParam('isbn10');
345
            }
346
        }
347
348
        // ISBN correction
349 7
        if ($isbn13 !== $isbn) {
350 7
            $this->setParam('isbn', $isbn13);
351 7
            $this->log('ISBN');
352
            //            $this->notCosmetic = true;
353
        }
354 7
    }
355
356
    /**
357
     * Find year of book publication.
358
     *
359
     * @return int|null
360
     * @throws Exception
361
     */
362 10
    private function findBookYear(): ?int
363
    {
364 10
        $annee = $this->getParam('année');
365 10
        if (!empty($annee) && is_numeric($annee)) {
366 1
            return intval($annee);
367
        }
368 9
        $date = $this->getParam('date');
369 9
        if ($date && preg_match('#[^0-9]?([12][0-9][0-9][0-9])[^0-9]?#', $date, $matches) > 0) {
370 1
            return intval($matches[1]);
371
        }
372
373 8
        return null;
374
    }
375
376 9
    private function stripIsbn(string $isbn): string
377
    {
378 9
        return trim(preg_replace('#[^0-9Xx]#', '', $isbn));
379
    }
380
381 47
    private function processTitle()
382
    {
383 47
        $oldtitre = $this->getParam('titre');
384 47
        $this->langInTitle();
385 47
        $this->deWikifyExternalLink('titre');
386 47
        $this->upperCaseFirstLetter('titre');
387 47
        $this->typoDeuxPoints('titre');
388
389 47
        $this->extractSubTitle();
390
391
        // 20-11-2019 : Retiré majuscule à sous-titre
392
393 47
        if ($this->getParam('titre') !== $oldtitre) {
394 5
            $this->log('±titre');
395
        }
396
397 47
        $this->valideNumeroChapitre();
398 47
        $this->deWikifyExternalLink('titre chapitre');
399 47
        $this->upperCaseFirstLetter('titre chapitre');
400 47
    }
401
402 13
    private function detectColon($param): bool
403
    {
404
        // > 0 don't count a starting colon ":bla"
405 13
        if ($this->hasParamValue($param) && mb_strrpos($this->getParam('titre'), ':') > 0) {
406 3
            return true;
407
        }
408
409 10
        return false;
410
    }
411
412 47
    private function extractSubTitle(): void
413
    {
414
        // FIXED bug [[fu:bar]]
415 47
        if (!$this->getParam('titre') || WikiTextUtil::isWikify($this->getParam('titre'))) {
416 34
            return;
417
        }
418
419 13
        if (!$this->detectColon('titre')) {
420 10
            return;
421
        }
422
        // Que faire si déjà un sous-titre ?
423 3
        if ($this->hasParamValue('sous-titre')) {
424
            return;
425
        }
426
427
        // titre>5 and sous-titre>5 and sous-titre<40
428 3
        if (preg_match('#^(?<titre>[^:]{5,}):(?<st>.{5,40})$#', $this->getParam('titre'), $matches) > 0) {
429 3
            $this->setParam('titre', trim($matches['titre']));
430 3
            $this->setParam('sous-titre', trim($matches['st']));
431 3
            $this->log('>sous-titre');
432
        }
433 3
    }
434
435
    /**
436
     * Normalize a Google Book links.
437
     * Clean the useless URL parameters or transform into wiki-template.
438
     *
439
     * @param $param
440
     *
441
     * @throws Exception
442
     */
443 47
    private function googleBookUrl(string $param): void
444
    {
445 47
        $url = $this->getParam($param);
446 47
        if (empty($url)
447 47
            || !GoogleBooksUtil::isGoogleBookURL($url)
448
        ) {
449 47
            return;
450
        }
451
452 1
        if (self::CONVERT_GOOGLEBOOK_TEMPLATE) {
453
            $template = GoogleLivresTemplate::createFromURL($url);
454
            if ($template) {
455
                $this->setParam($param, $template->serialize());
456
                $this->log('{Google}');
457
                $this->notCosmetic = true;
458
459
                return;
460
            }
461
        }
462
463
        try {
464 1
            $goo = GoogleBooksUtil::simplifyGoogleUrl($url);
465
        } catch (DomainException $e) {
466
            // ID manquant ou malformé
467
            $errorValue = sprintf(
468
                '%s <!-- ERREUR %s -->',
469
                $url,
470
                $e->getMessage()
471
            );
472
            $this->setParam($param, $errorValue);
473
            $this->log('erreur URL');
474
            $this->notCosmetic = true;
475
            $this->major = true;
476
        }
477
478 1
        if (!empty($goo) && $goo !== $url) {
479 1
            $this->setParam($param, $goo);
480
            // cleaned tracking parameters in Google URL ?
481 1
            if (GoogleBooksUtil::isTrackingUrl($url)) {
482 1
                $this->log('tracking');
483 1
                $this->notCosmetic = true;
484
            }
485
        }
486 1
    }
487
488
    /**
489
     * - {{lang|...}} dans titre => langue=... puis titre nettoyé
490
     *  langue=L’utilisation de ce paramètre permet aussi aux synthétiseurs vocaux de reconnaître la langue du titre de
491
     * l’ouvrage.
492
     * 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.
493
     *
494
     * @throws Exception
495
     */
496 47
    private function langInTitle(): void
497
    {
498 47
        if (preg_match(
499 47
                '#^{{ ?(?:lang|langue) ?\| ?([a-z-]{2,5}) ?\| ?(?:texte=)?([^{}=]+)(?:\|dir=rtl)?}}$#i',
500 47
                $this->getParam('titre'),
501 47
                $matches
502 47
            ) > 0
503
        ) {
504
            $lang = trim($matches[1]);
505
            $newtitre = str_replace($matches[0], trim($matches[2]), $this->getParam('titre'));
506
507
            // problème : titre anglais de livre français
508
            // => conversion {{lang}} du titre seulement si langue= défini
509
            // opti : restreindre à ISBN zone 2 fr ?
510
            if ($lang === $this->getParam('langue')) {
511
                $this->setParam('titre', $newtitre);
512
                $this->log('°titre');
513
            }
514
515
            // desactivé à cause de l'exception décrite ci-dessus
516
            // si langue=VIDE : ajout langue= à partir de langue titre
517
            //            if (self::WIKI_LANGUAGE !== $lang && empty($this->getParam('langue'))) {
518
            //                $this->setParam('langue', $lang);
519
            //                $this->log('+langue='.$lang);
520
            //            }
521
        }
522 47
    }
523
524 47
    private function processDates()
525
    {
526
        // dewikification
527 47
        $params = ['date', 'année', 'mois', 'jour'];
528 47
        foreach ($params as $param) {
529 47
            if ($this->hasParamValue($param) && WikiTextUtil::isWikify(' '.$this->getParam($param))) {
530 1
                $this->setParam($param, WikiTextUtil::unWikify($this->getParam($param)));
531
            }
532
        }
533
534
        try {
535 47
            $this->moveDate2Year();
536
        } catch (Exception $e) {
537
            $this->log->warning('Exception '.$e->getMessage());
538
        }
539 47
    }
540
541
    /**
542
     * todo: move to AbstractWikiTemplate ?
543
     * Correction des parametres rejetés à l'hydratation données.
544
     *
545
     * @throws Exception
546
     */
547 47
    private function cleanAndPredictErrorParameters()
548
    {
549 47
        if (empty($this->ouvrage->parametersErrorFromHydrate)) {
550 45
            return;
551
        }
552 2
        $allParamsAndAlias = $this->ouvrage->getParamsAndAlias();
553
554 2
        foreach ($this->ouvrage->parametersErrorFromHydrate as $name => $value) {
555 2
            if (!is_string($name)) {
556
                // example : 1 => "ouvrage collectif" from |ouvrage collectif|
557 1
                continue;
558
            }
559
560
            // delete error parameter if no value
561 2
            if (empty($value)) {
562
                unset($this->ouvrage->parametersErrorFromHydrate[$name]);
563
564
                continue;
565
            }
566
567 2
            $maxDistance = 1;
568 2
            if (mb_strlen($name) >= 4) {
569 2
                $maxDistance = 2;
570
            }
571 2
            if (mb_strlen($name) >= 8) {
572
                $maxDistance = 3;
573
            }
574
575 2
            $predName = TextUtil::predictCorrectParam($name, $allParamsAndAlias, $maxDistance);
576 2
            if ($predName && mb_strlen($name) >= 5) {
577 2
                if (empty($this->getParam($predName))) {
578 2
                    $predName = $this->ouvrage->getAliasParam($predName);
579 2
                    $this->setParam($predName, $value);
580 2
                    $this->log(sprintf('%s⇒%s ?', $name, $predName));
581 2
                    $this->notCosmetic = true;
582 2
                    unset($this->ouvrage->parametersErrorFromHydrate[$name]);
583
                }
584
            }
585
        }
586 2
    }
587
588
    /**
589
     * TODO : return "" instead of null ?
590
     *
591
     * @param $name
592
     *
593
     * @return string|null
594
     * @throws Exception
595
     */
596 47
    private function getParam(string $name): ?string
597
    {
598 47
        return $this->ouvrage->getParam($name);
599
    }
600
601 47
    private function hasParamValue(string $name): bool
602
    {
603 47
        return $this->ouvrage->hasParamValue($name);
604
    }
605
606 35
    private function setParam($name, $value)
607
    {
608
        // todo : overwrite setParam() ?
609 35
        if (!empty($value) || $this->ouvrage->getParam($name)) {
610 35
            $this->ouvrage->setParam($name, $value);
611
        }
612 35
    }
613
614 26
    private function log(string $string): void
615
    {
616 26
        if (!empty($string)) {
617 26
            $this->summaryLog[] = trim($string);
618
        }
619 26
    }
620
621
    /**
622
     * Bool ?
623
     * déwikification du titre : consensus Bistro 27 août 2011
624
     * idem  'titre chapitre'.
625
     *
626
     * @param string $param
627
     *
628
     * @throws Exception
629
     */
630 47
    private function deWikifyExternalLink(string $param): void
631
    {
632 47
        if (empty($this->getParam($param))) {
633 47
            return;
634
        }
635 15
        if (preg_match('#^\[(http[^ \]]+) ([^]]+)]#i', $this->getParam($param), $matches) > 0) {
636 1
            $this->setParam($param, str_replace($matches[0], $matches[2], $this->getParam($param)));
637 1
            $this->log('±'.$param);
638
639 1
            if (in_array($param, ['titre', 'titre chapitre'])) {
640 1
                if (empty($this->getParam('lire en ligne'))) {
641 1
                    $this->setParam('lire en ligne', $matches[1]);
642 1
                    $this->log('+lire en ligne');
643
644 1
                    return;
645
                }
646
                $this->log('autre lien externe: '.$matches[1]);
647
            }
648
        }
649 14
    }
650
651 47
    private function upperCaseFirstLetter($param)
652
    {
653 47
        if (!$this->hasParamValue($param)) {
654 47
            return;
655
        }
656 15
        $newValue = TextUtil::mb_ucfirst(trim($this->getParam($param)));
657 15
        $this->setParam($param, $newValue);
658 15
    }
659
660
    /**
661
     * Typo internationale 'titre : sous-titre'.
662
     * Fix fantasy typo of subtitle with '. ' or ' - '.
663
     * International Standard Bibliographic Description :
664
     * https://fr.wikipedia.org/wiki/Wikip%C3%A9dia:Le_Bistro/13_janvier_2016#Modif_du_mod%C3%A8le:Ouvrage.
665
     *
666
     * @param $param
667
     *
668
     * @throws Exception
669
     */
670 47
    private function typoDeuxPoints($param)
671
    {
672 47
        $origin = $this->getParam($param) ?? '';
673 47
        if (empty($origin)) {
674 32
            return;
675
        }
676
        // FIXED bug [[fu:bar]]
677 15
        if (WikiTextUtil::isWikify($origin)) {
678 2
            return;
679
        }
680
681 13
        $origin = TextUtil::replaceNonBreakingSpaces($origin);
682
683 13
        $strTitle = $origin;
684
685
        // CORRECTING TYPO FANTASY OF SUBTITLE
686
687
        // Replace first '.' by ':' if no ': ' and no numbers around (as PHP 7.3)
688
        // exlude pattern "blabla... blabla"
689
        // TODO: statistics
690
691
        // Replace ' - ' or ' / ' (spaced!) by ' : ' if no ':' and no numbers after (as PHP 7.3 or 1939-1945)
692 13
        if (!mb_strpos(':', $strTitle) && preg_match('#.{6,} ?[-/] ?[^0-9)]{6,}#', $strTitle) > 0) {
693 4
            $strTitle = preg_replace('#(.{6,}) [-/] ([^0-9)]{6,})#', '$1 : $2', $strTitle);
694
        }
695
696
        // international typo style " : " (first occurrence)
697 13
        $strTitle = preg_replace('#[ ]*:[ ]*#', ' : ', $strTitle);
698
699 13
        if ($strTitle !== $origin) {
700 3
            $this->setParam($param, $strTitle);
701 3
            $this->log(sprintf(':%s', $param));
702
        }
703 13
    }
704
705 47
    private function valideNumeroChapitre()
706
    {
707 47
        $value = $this->getParam('numéro chapitre');
708 47
        if (empty($value)) {
709 47
            return;
710
        }
711
        // "12" ou "VI", {{II}}, II:3
712
        if (preg_match('#^[0-9IVXL\-.:{}]+$#i', $value) > 0) {
713
            return;
714
        }
715
        // déplace vers "titre chapitre" ?
716
        if (!$this->getParam('titre chapitre')) {
717
            $this->unsetParam('numéro chapitre');
718
            $this->setParam('titre chapitre', $value);
719
        }
720
        $this->log('≠numéro chapitre');
721
    }
722
723 9
    private function unsetParam($name)
724
    {
725 9
        $this->ouvrage->unsetParam($name);
726 9
    }
727
728
    /**
729
     * TODO move+refac
730
     * TODO CommentaireBiblioTemplate  ExtraitTemplate
731
     * Probleme {{commentaire biblio}} <> {{commentaire biblio SRL}}
732
     * Generate supplementary templates from obsoletes params.
733
     *
734
     * @throws Exception
735
     */
736 47
    protected function externalTemplates()
737
    {
738
        // "extrait=bla" => {{citation bloc|bla}}
739 47
        if ($this->hasParamValue('extrait')) {
740 1
            $extrait = $this->getParam('extrait');
741
            // todo bug {{citation bloc}} si "=" ou "|" dans texte de citation
742
            // Legacy : use {{début citation}} ... {{fin citation}}
743 1
            if (preg_match('#[=|]#', $extrait) > 0) {
744
                $this->ouvrage->externalTemplates[] = (object)[
745
                    'template' => 'début citation',
746
                    '1' => '',
747
                    'raw' => '{{Début citation}}'.$extrait.'{{Fin citation}}',
748
                ];
749
                $this->log('{Début citation}');
750
                $this->notCosmetic = true;
751
            } else {
752
                // StdClass
753 1
                $this->ouvrage->externalTemplates[] = (object)[
754 1
                    'template' => 'citation bloc',
755 1
                    '1' => $extrait,
756 1
                    'raw' => '{{Citation bloc|'.$extrait.'}}',
757
                ];
758 1
                $this->log('{Citation bloc}');
759 1
                $this->notCosmetic = true;
760
            }
761
762 1
            $this->unsetParam('extrait');
763 1
            $this->notCosmetic = true;
764
        }
765
766
        // "commentaire=bla" => {{Commentaire biblio|1=bla}}
767 47
        if ($this->hasParamValue('commentaire')) {
768 1
            $commentaire = $this->getParam('commentaire');
769 1
            $this->ouvrage->externalTemplates[] = (object)[
770 1
                'template' => 'commentaire biblio',
771 1
                '1' => $commentaire,
772 1
                'raw' => '{{Commentaire biblio|'.$commentaire.'}}',
773
            ];
774 1
            $this->unsetParam('commentaire');
775 1
            $this->log('{commentaire}');
776 1
            $this->notCosmetic = true;
777
        }
778 47
    }
779
780
    // ----------------------
781
    // ----------------------
782
    // ----------------------
783
784
    /**
785
     * Date->année (nécessaire pour OuvrageComplete).
786
     *
787
     * @throws Exception
788
     */
789 47
    private function moveDate2Year()
790
    {
791 47
        $date = $this->getParam('date') ?? false;
792 47
        if ($date) {
793 3
            if (preg_match('#^-?[12][0-9][0-9][0-9]$#', $date)) {
794 1
                $this->setParam('année', $date);
795 1
                $this->unsetParam('date');
796
                //$this->log('>année');
797
            }
798
        }
799 47
    }
800
801 47
    private function predictFormatByPattern()
802
    {
803 47
        if (($value = $this->getParam('format'))) {
804
            // predict if 'format électronique'
805
            // format electronique lié au champ 'lire en ligne'
806
            // 2015 https://fr.wikipedia.org/wiki/Discussion_mod%C3%A8le:Ouvrage#format,_format_livre,_format_%C3%A9lectronique
807
            //            if (preg_match('#(pdf|epub|html|kindle|audio|\{\{aud|jpg)#i', $value) > 0) {
808
            //
809
            //                $this->setParam('format électronique', $value);
810
            //                $this->unsetParam('format');
811
            //                $this->log('format:électronique?');
812
            //
813
            //                return;
814
            //            }
815
            if (preg_match(
816
                    '#(ill\.|couv\.|in-[0-9]|in-fol|poche|broché|relié|{{unité|{{Dunité|[0-9]{2} ?cm|\|cm}}|vol\.|A4)#i',
817
                    $value
818
                ) > 0
819
            ) {
820
                $this->setParam('format livre', $value);
821
                $this->unsetParam('format');
822
                $this->log('format:livre?');
823
                $this->notCosmetic = true;
824
            }
825
            // Certainement 'format électronique'...
826
        }
827 47
    }
828
829
    /**
830
     * @return bool
831
     * @throws Exception
832
     */
833
    public function checkMajorEdit(): bool
834
    {
835
        // Correction paramètre
836
        if ($this->ouvrage->parametersErrorFromHydrate !== $this->original->parametersErrorFromHydrate) {
837
            return true;
838
        }
839
        // Complétion langue ?
840
        if ($this->hasParamValue('langue') && !$this->original->hasParamValue('langue')
841
            && self::WIKI_LANGUAGE !== $this->getParam('langue')
842
        ) {
843
            return true;
844
        }
845
        // TODO replace conditions ci-dessous par event flagMajor()
846
        // Retire le param/value 'langue' (pas major si conversion nom langue)
847
        $datOuvrage = $this->ouvrage->toArray();
848
        $datOriginal = $this->original->toArray();
849
        unset($datOriginal['langue'], $datOuvrage['langue']);
850
851
        // Modification données
852
        if ($datOriginal !== $datOuvrage) {
853
            return true;
854
        }
855
856
        return false;
857
    }
858
859
    /**
860
     * @return array
861
     */
862 1
    public function getSummaryLog(): array
863
    {
864 1
        return $this->summaryLog;
865
    }
866
867
    /**
868
     * @return OuvrageTemplate
869
     */
870 47
    public function getOuvrage(): OuvrageTemplate
871
    {
872 47
        return $this->ouvrage;
873
    }
874
875
    /**
876
     * todo : vérif lien rouge
877
     * todo 'lien éditeur' affiché 1x par page
878
     * opti : Suppression lien éditeur si c'est l'article de l'éditeur.
879
     *
880
     * @throws Exception
881
     */
882 47
    private function processEditeur()
883
    {
884 47
        $editeur = $this->getParam('éditeur');
885 47
        if (empty($editeur)) {
886 42
            return;
887
        }
888
889
        // FIX bug "GEO Art ([[Prisma Media]]) ; [[Le Monde]]"
890 5
        if (preg_match('#\[.*\[.*\[#', $editeur) > 0) {
891 1
            return;
892
        }
893
        // FIX bug "[[Fu|Bar]] bla" => [[Fu|Bar bla]]
894 4
        if (preg_match('#(.+\[\[|\]\].+)#', $editeur) > 0) {
895 1
            return;
896
        }
897
898
        // [[éditeur]]
899 3
        if (preg_match('#\[\[([^|]+)]]#', $editeur, $matches) > 0) {
900 1
            $editeurUrl = $matches[1];
901
        }
902
        // [[bla|éditeur]]
903 3
        if (preg_match('#\[\[([^]|]+)\|.+]]#', $editeur, $matches) > 0) {
904
            $editeurUrl = $matches[1];
905
        }
906
907
        // Todo : traitement/suppression des abréviations communes :
908
        // ['éd. de ', 'éd. du ', 'éd.', 'ed.', 'Éd. de ', 'Éd.', 'édit.', 'Édit.', '(éd.)', '(ed.)', 'Ltd.']
909
910 3
        $editeurStr = WikiTextUtil::unWikify($editeur);
911
        // On garde minuscule sur éditeur, pour nuance Éditeur/éditeur permettant de supprimer "éditeur"
912
        // ex: "éditions Milan" => "Milan"
913
914
        // Déconseillé : 'lien éditeur' (obsolete 2019)
915 3
        if ($this->hasParamValue('lien éditeur')) {
916 2
            if (empty($editeurUrl)) {
917 2
                $editeurUrl = $this->getParam('lien éditeur');
918
            }
919 2
            $this->unsetParam('lien éditeur');
920
        }
921
922 3
        $newEditeur = $editeurStr;
923 3
        if (!empty($editeurUrl)) {
924 3
            $newEditeur = WikiTextUtil::wikilink($editeurStr, $editeurUrl);
925
        }
926
927 3
        if ($newEditeur !== $editeur) {
928 2
            $this->setParam('éditeur', $newEditeur);
929 2
            $this->log('±éditeur');
930 2
            $this->notCosmetic = true;
931
        }
932 3
    }
933
}
934