ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
ilXlsFoParserTest.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
21use PHPUnit\Framework\Attributes\DataProvider;
22use PHPUnit\Framework\MockObject\Stub;
23
25{
29 private function verifyFoGeneratedFromXhtml(array $form_data, string $fo): void
30 {
31 $output = $this->createXlsFoParser()->parse($form_data);
32
33 $this->assertSame($this->normalizeXml($fo), $this->normalizeXml($output));
34 }
35
36 private function createXlsFoParser(): ilXlsFoParser
37 {
39 $settings = $this->createStub(ilSetting::class);
40 $settings->method('get')->willReturnArgument(0);
41
43 $language = $this->getStubBuilder(ilLanguage::class)->disableOriginalConstructor()->onlyMethods(
44 ['txt']
45 )->getStub();
46 $language->method('txt')->willReturnArgument(0);
47
48 $page_formats = new ilPageFormats($language);
49
51 $util_helper = $this->createStub(ilCertificateUtilHelper::class);
52
53 $util_helper->method('stripSlashes')
54 ->willReturnArgument(0);
55
56 $xmlChecker = new ilXMLChecker(new ILIAS\Data\Factory());
57 $xslt_process = new ilCertificateXlstProcess();
58
60 $language = $this->createStub(ilLanguage::class);
61
62 $xsl_loader = new ilCertificateXlsFileLoader();
63
64 return new ilXlsFoParser(
66 $page_formats,
67 $xmlChecker,
68 $util_helper,
69 $xslt_process,
70 $language,
71 $xsl_loader
72 );
73 }
74
78 private function paragraphAlignments(string $certificate_text): array
79 {
80 $output = $this->createXlsFoParser()->parse([
81 'certificate_text' => $certificate_text,
82 'margin_body' => [
83 'top' => '0cm',
84 'right' => '2cm',
85 'bottom' => '0cm',
86 'left' => '2cm'
87 ],
88 'pageformat' => 'custom',
89 'pagewidth' => '297mm',
90 'pageheight' => '210mm'
91 ]);
92
93 $document = new DOMDocument();
94 $document->loadXML($output);
95 $xpath = new DOMXPath($document);
96 $xpath->registerNamespace('fo', 'http://www.w3.org/1999/XSL/Format');
97
98 $alignments = [];
99 foreach ($xpath->query('//fo:flow/fo:block/fo:block') as $block) {
100 $alignments[] = $block->getAttribute('text-align');
101 }
102
103 return $alignments;
104 }
105
106 private function normalizeXml(string $xml): string
107 {
108 $xml = str_replace(["\n", "\r", "\t"], '', $xml);
109 $xml = preg_replace("/>(\s+)</", "><", $xml);
110 $xml = preg_replace('# {2,}#', ' ', $xml);
111 $xml = trim($xml);
112
113 return $xml;
114 }
115
116 public function testParseWithNonCustomPageFormatting(): void
117 {
118 $formData = [
119 'certificate_text' => '<xml> Some Context </xml>',
120 'margin_body' => [
121 'top' => '1cm',
122 'right' => '2cm',
123 'bottom' => '3cm',
124 'left' => '4cm'
125 ],
126 'pageformat' => 'a4'
127 ];
128
130 $settings = $this->createStub(ilSetting::class);
131
132 $settings->method('get')
133 ->willReturn('Something');
134
136 $pageFormats = $this->createStub(ilPageFormats::class);
137
138 $pageFormats->method('fetchPageFormats')
139 ->willReturn([
140 'a4' => [
141 'name' => 'A4',
142 'value' => 'a4',
143 'width' => '210mm',
144 'height' => '297mm'
145 ],
146 ]);
147
148 $xmlChecker = new ilXMLChecker(new ILIAS\Data\Factory());
149
151 $utilHelper = $this->createStub(ilCertificateUtilHelper::class);
152
153 $utilHelper->method('stripSlashes')
154 ->willReturnOnConsecutiveCalls(
155 '297mm',
156 '210mm',
157 '1cm',
158 '2cm',
159 '3cm',
160 '4cm'
161 );
162
163 $xlstProcess = $this->getMockBuilder(ilCertificateXlstProcess::class)
164 ->getMock();
165
166 $xlstProcess
167 ->expects($this->once())
168 ->method('process')
169 ->with(
170 [
171 '/_xml' => '<html><body><xml> Some Context </xml></body></html>',
172 '/_xsl' => '<xml>Some XLS Content</xml>'
173 ],
174 [
175 'pageheight' => '297mm',
176 'pagewidth' => '210mm',
177 'backgroundimage' => '[BACKGROUND_IMAGE]',
178 'marginbody' => '1cm 2cm 3cm 4cm'
179 ]
180 )
181 ->willReturn('Something Processed');
182
184 $language = $this->createStub(ilLanguage::class);
185
187 $certificateXlsFileLoader = $this->createStub(ilCertificateXlsFileLoader::class);
188
189 $certificateXlsFileLoader->method('getXlsCertificateContent')
190 ->willReturn('<xml>Some XLS Content</xml>');
191
192 $xlsFoParser = new ilXlsFoParser(
193 $settings,
194 $pageFormats,
195 $xmlChecker,
196 $utilHelper,
197 $xlstProcess,
198 $language,
199 $certificateXlsFileLoader
200 );
201
202 $output = $xlsFoParser->parse($formData);
203
204 $this->assertSame('Something Processed', $output);
205 }
206
207 public function testParseButXmlCheckerFindsAnError(): never
208 {
209 $this->expectException(Exception::class);
210
211 $formData = [
212 'certificate_text' => '<xml> Some Context <xml>',
213 'margin_body' => [
214 'top' => '1cm',
215 'right' => '2cm',
216 'bottom' => '3cm',
217 'left' => '4cm'
218 ],
219 'pageformat' => 'custom'
220 ];
221
223 $settings = $this->createStub(ilSetting::class);
224
225 $settings->method('get')
226 ->willReturn('Something');
227
229 $pageFormats = $this->createStub(ilPageFormats::class);
230
231 $pageFormats->method('fetchPageFormats')
232 ->willReturn([
233 'a4' => [
234 'name' => 'A4',
235 'value' => 'a4',
236 'width' => '210mm',
237 'height' => '297mm'
238 ],
239 ]);
240
241 $xmlChecker = new ilXMLChecker(new ILIAS\Data\Factory());
242
244 $utilHelper = $this->createStub(ilCertificateUtilHelper::class);
245
247 $xlstProcess = $this->createStub(ilCertificateXlstProcess::class);
248
249 $language = $this->getMockBuilder(ilLanguage::class)
250 ->disableOriginalConstructor()
251 ->onlyMethods(['txt'])
252 ->getMock();
253 $language->expects($this->once())->method('txt')->willReturn('certificate_not_well_formed');
254
256 $certificateXlsFileLoader = $this->createStub(ilCertificateXlsFileLoader::class);
257
258 $certificateXlsFileLoader->method('getXlsCertificateContent')
259 ->willReturn('<xml>Some XLS Content</xml>');
260
261 $xlsFoParser = new ilXlsFoParser(
262 $settings,
263 $pageFormats,
264 $xmlChecker,
265 $utilHelper,
266 $xlstProcess,
267 $language,
268 $certificateXlsFileLoader
269 );
270
271 $xlsFoParser->parse($formData);
272
273 $this->fail();
274 }
275
276 public function testParseWithCustomPageFormatting(): void
277 {
278 $formData = [
279 'certificate_text' => '<xml> Some Context </xml>',
280 'margin_body' => [
281 'top' => '1cm',
282 'right' => '2cm',
283 'bottom' => '3cm',
284 'left' => '4cm'
285 ],
286 'pageformat' => 'custom',
287 'pagewidth' => '210mm',
288 'pageheight' => '297mm'
289 ];
290
292 $settings = $this->createStub(ilSetting::class);
293
294 $settings->method('get')
295 ->willReturn('Something');
296
298 $pageFormats = $this->createStub(ilPageFormats::class);
299
300 $pageFormats->method('fetchPageFormats')
301 ->willReturn([
302 'a4' => [
303 'name' => 'A4',
304 'value' => 'a4',
305 'width' => '210mm',
306 'height' => '297mm'
307 ],
308 ]);
309
310 $xmlChecker = new ilXMLChecker(new ILIAS\Data\Factory());
311
313 $utilHelper = $this->createStub(ilCertificateUtilHelper::class);
314
315 $utilHelper->method('stripSlashes')
316 ->willReturnOnConsecutiveCalls(
317 '297mm',
318 '210mm',
319 '1cm',
320 '2cm',
321 '3cm',
322 '4cm'
323 );
324
325 $xlstProcess = $this->getMockBuilder(ilCertificateXlstProcess::class)
326 ->getMock();
327
328 $xlstProcess
329 ->expects($this->once())
330 ->method('process')
331 ->with(
332 [
333 '/_xml' => '<html><body><xml> Some Context </xml></body></html>',
334 '/_xsl' => '<xml>Some XLS Content</xml>'
335 ],
336 [
337 'pageheight' => '297mm',
338 'pagewidth' => '210mm',
339 'backgroundimage' => '[BACKGROUND_IMAGE]',
340 'marginbody' => '1cm 2cm 3cm 4cm'
341 ]
342 )
343 ->willReturn('Something Processed');
344
346 $language = $this->createStub(ilLanguage::class);
347
349 $certificateXlsFileLoader = $this->createStub(ilCertificateXlsFileLoader::class);
350
351 $certificateXlsFileLoader->method('getXlsCertificateContent')
352 ->willReturn('<xml>Some XLS Content</xml>');
353
354 $xlsFoParser = new ilXlsFoParser(
355 $settings,
356 $pageFormats,
357 $xmlChecker,
358 $utilHelper,
359 $xlstProcess,
360 $language,
361 $certificateXlsFileLoader
362 );
363
364 $output = $xlsFoParser->parse($formData);
365
366 $this->assertSame('Something Processed', $output);
367 }
368
369 public function testCommasWillBeConvertedToPointInDecimalSepartor(): void
370 {
371 $formData = [
372 'certificate_text' => '<xml> Some Context </xml>',
373 'margin_body' => [
374 'top' => '1cm',
375 'right' => '2cm',
376 'bottom' => '3cm',
377 'left' => '4cm'
378 ],
379 'pageformat' => 'custom',
380 'pagewidth' => '210mm',
381 'pageheight' => '297mm'
382 ];
383
385 $settings = $this->createStub(ilSetting::class);
386
387 $settings->method('get')
388 ->willReturn('Something');
389
391 $pageFormats = $this->createStub(ilPageFormats::class);
392
393 $pageFormats->method('fetchPageFormats')
394 ->willReturn([
395 'a4' => [
396 'name' => 'A4',
397 'value' => 'a4',
398 'width' => '21,0mm',
399 'height' => '29,7mm'
400 ],
401 ]);
402
403 $xmlChecker = new ilXMLChecker(new ILIAS\Data\Factory());
404
406 $utilHelper = $this->createStub(ilCertificateUtilHelper::class);
407
408 $utilHelper->method('stripSlashes')
409 ->willReturnOnConsecutiveCalls(
410 '29,7mm',
411 '21,0mm',
412 '1cm',
413 '2cm',
414 '3cm',
415 '4cm'
416 );
417
418 $xlstProcess = $this->getMockBuilder(ilCertificateXlstProcess::class)
419 ->getMock();
420
421 $xlstProcess
422 ->expects($this->once())
423 ->method('process')
424 ->with(
425 [
426 '/_xml' => '<html><body><xml> Some Context </xml></body></html>',
427 '/_xsl' => '<xml>Some XLS Content</xml>'
428 ],
429 [
430 'pageheight' => '29.7mm',
431 'pagewidth' => '21.0mm',
432 'backgroundimage' => '[BACKGROUND_IMAGE]',
433 'marginbody' => '1cm 2cm 3cm 4cm'
434 ]
435 )
436 ->willReturn('Something Processed');
437
439 $language = $this->createStub(ilLanguage::class);
440
442 $certificateXlsFileLoader = $this->createStub(ilCertificateXlsFileLoader::class);
443
444 $certificateXlsFileLoader->method('getXlsCertificateContent')
445 ->willReturn('<xml>Some XLS Content</xml>');
446
447 $xlsFoParser = new ilXlsFoParser(
448 $settings,
449 $pageFormats,
450 $xmlChecker,
451 $utilHelper,
452 $xlstProcess,
453 $language,
454 $certificateXlsFileLoader
455 );
456
457 $output = $xlsFoParser->parse($formData);
458
459 $this->assertSame('Something Processed', $output);
460 }
461
462 public static function nonBreakingSpaceIsAddedDataProvider(): Generator
463 {
464 $expected_fo_with_centered_block = <<<EOT
465<?xml version="1.0"?>
466<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="rpc_pdf_font">
467 <fo:layout-master-set>
468 <fo:simple-page-master master-name="ILIAS_certificate" page-height="297mm" page-width="210mm">
469 <fo:region-body margin="1cm 2cm 3cm 4cm"/>
470 <fo:region-before region-name="background-image" extent="0"/>
471 </fo:simple-page-master>
472 </fo:layout-master-set>
473 <fo:page-sequence master-reference="ILIAS_certificate">
474 <fo:static-content flow-name="background-image">
475 <fo:block-container absolute-position="absolute" top="0cm" left="0cm" z-index="0">
476 <fo:block>
477 <fo:external-graphic src="url([BACKGROUND_IMAGE])" content-height="297mm" content-width="210mm"/>
478 </fo:block>
479 </fo:block-container>
480 </fo:static-content>
481 <fo:flow flow-name="xsl-region-body">
482 <fo:block>
483 <fo:block text-align="center">&#160;</fo:block>
484 </fo:block>
485 </fo:flow>
486 </fo:page-sequence>
487</fo:root>
488EOT;
489
490 yield 'Centered Paragraph' => [
491 [
492 'certificate_text' => '<p style="text-align: center;"></p>',
493 'margin_body' => [
494 'top' => '1cm',
495 'right' => '2cm',
496 'bottom' => '3cm',
497 'left' => '4cm'
498 ],
499 'pageformat' => 'custom',
500 'pagewidth' => '210mm',
501 'pageheight' => '297mm'
502 ],
503 $expected_fo_with_centered_block
504 ];
505
506 $expected_fo_with_centered_block = <<<EOT
507<?xml version="1.0"?>
508<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="rpc_pdf_font">
509 <fo:layout-master-set>
510 <fo:simple-page-master master-name="ILIAS_certificate" page-height="297mm" page-width="210mm">
511 <fo:region-body margin="1cm 2cm 3cm 4cm"/>
512 <fo:region-before region-name="background-image" extent="0"/>
513 </fo:simple-page-master>
514 </fo:layout-master-set>
515 <fo:page-sequence master-reference="ILIAS_certificate">
516 <fo:static-content flow-name="background-image">
517 <fo:block-container absolute-position="absolute" top="0cm" left="0cm" z-index="0">
518 <fo:block>
519 <fo:external-graphic src="url([BACKGROUND_IMAGE])" content-height="297mm" content-width="210mm"/>
520 </fo:block>
521 </fo:block-container>
522 </fo:static-content>
523 <fo:flow flow-name="xsl-region-body">
524 <fo:block>
525 <fo:block>&#160;</fo:block>
526 </fo:block>
527 </fo:flow>
528 </fo:page-sequence>
529</fo:root>
530EOT;
531
532 yield 'Empty paragraph marked as emptyrow' => [
533 [
534 'certificate_text' => '<p class="emptyrow"></p>',
535 'margin_body' => [
536 'top' => '1cm',
537 'right' => '2cm',
538 'bottom' => '3cm',
539 'left' => '4cm'
540 ],
541 'pageformat' => 'custom',
542 'pagewidth' => '210mm',
543 'pageheight' => '297mm'
544 ],
545 $expected_fo_with_centered_block
546 ];
547
548 yield 'Empty paragraph' => [
549 [
550 'certificate_text' => '<p></p>',
551 'margin_body' => [
552 'top' => '1cm',
553 'right' => '2cm',
554 'bottom' => '3cm',
555 'left' => '4cm'
556 ],
557 'pageformat' => 'custom',
558 'pagewidth' => '210mm',
559 'pageheight' => '297mm'
560 ],
561 $expected_fo_with_centered_block
562 ];
563 }
564
568 #[DataProvider('nonBreakingSpaceIsAddedDataProvider')]
570 array $form_data,
571 string $fo
572 ): void {
573 $this->verifyFoGeneratedFromXhtml($form_data, $fo);
574 }
575
576 public static function noNonBreakingSpaceIsAddedDataProvider(): Generator
577 {
578 $expected_fo_with_centered_block = <<<EOT
579<?xml version="1.0"?>
580<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="rpc_pdf_font">
581 <fo:layout-master-set>
582 <fo:simple-page-master master-name="ILIAS_certificate" page-height="297mm" page-width="210mm">
583 <fo:region-body margin="1cm 2cm 3cm 4cm"/>
584 <fo:region-before region-name="background-image" extent="0"/>
585 </fo:simple-page-master>
586 </fo:layout-master-set>
587 <fo:page-sequence master-reference="ILIAS_certificate">
588 <fo:static-content flow-name="background-image">
589 <fo:block-container absolute-position="absolute" top="0cm" left="0cm" z-index="0">
590 <fo:block>
591 <fo:external-graphic src="url([BACKGROUND_IMAGE])" content-height="297mm" content-width="210mm"/>
592 </fo:block>
593 </fo:block-container>
594 </fo:static-content>
595 <fo:flow flow-name="xsl-region-body">
596 <fo:block>
597 <fo:block>[USER_FULLNAME]</fo:block>
598 </fo:block>
599 </fo:flow>
600 </fo:page-sequence>
601</fo:root>
602EOT;
603
604 yield 'Paragraph with Text' => [
605 [
606 'certificate_text' => '<p>[USER_FULLNAME]</p>',
607 'margin_body' => [
608 'top' => '1cm',
609 'right' => '2cm',
610 'bottom' => '3cm',
611 'left' => '4cm'
612 ],
613 'pageformat' => 'custom',
614 'pagewidth' => '210mm',
615 'pageheight' => '297mm'
616 ],
617 $expected_fo_with_centered_block
618 ];
619
620 $expected_fo_with_centered_block = <<<EOT
621<?xml version="1.0"?>
622<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-family="rpc_pdf_font">
623 <fo:layout-master-set>
624 <fo:simple-page-master master-name="ILIAS_certificate" page-height="297mm" page-width="210mm">
625 <fo:region-body margin="1cm 2cm 3cm 4cm"/>
626 <fo:region-before region-name="background-image" extent="0"/>
627 </fo:simple-page-master>
628 </fo:layout-master-set>
629 <fo:page-sequence master-reference="ILIAS_certificate">
630 <fo:static-content flow-name="background-image">
631 <fo:block-container absolute-position="absolute" top="0cm" left="0cm" z-index="0">
632 <fo:block>
633 <fo:external-graphic src="url([BACKGROUND_IMAGE])" content-height="297mm" content-width="210mm"/>
634 </fo:block>
635 </fo:block-container>
636 </fo:static-content>
637 <fo:flow flow-name="xsl-region-body">
638 <fo:block>
639 <fo:block><fo:inline font-size="24pt">[USER_FULLNAME]</fo:inline></fo:block>
640 </fo:block>
641 </fo:flow>
642 </fo:page-sequence>
643</fo:root>
644EOT;
645
646 yield 'Paragraph with Nodes' => [
647 [
648 'certificate_text' => '<p><span style="font-size: 24pt;">[USER_FULLNAME]</span></p>',
649 'margin_body' => [
650 'top' => '1cm',
651 'right' => '2cm',
652 'bottom' => '3cm',
653 'left' => '4cm'
654 ],
655 'pageformat' => 'custom',
656 'pagewidth' => '210mm',
657 'pageheight' => '297mm'
658 ],
659 $expected_fo_with_centered_block
660 ];
661 }
662
666 #[DataProvider('noNonBreakingSpaceIsAddedDataProvider')]
668 array $form_data,
669 string $fo
670 ): void {
671 $this->verifyFoGeneratedFromXhtml($form_data, $fo);
672 }
673
675 {
676 $this->assertSame(
677 ['center', 'center', 'left', 'right', 'justify', 'center'],
678 $this->paragraphAlignments(
679 '<p align="left" style="text-align: center;">Confirmation</p>'
680 . '<p align="left" style="padding-left: 10px; text-align: center;">Line</p>'
681 . '<p align="center" style="text-align: left;">Left again</p>'
682 . '<p align="left" style="text-align: right;">Right</p>'
683 . '<p align="left" style="text-align: justify;">Justified</p>'
684 . '<p align="center">Legacy center</p>'
685 )
686 );
687 }
688
690 {
691 $fo = $this->createXlsFoParser()->parse([
692 'certificate_text' => '<p style="text-align: center;">Confirmation</p><p style="text-align: left;">Line</p>',
693 'margin_body' => [
694 'top' => '0cm',
695 'right' => '2cm',
696 'bottom' => '0cm',
697 'left' => '2cm'
698 ],
699 'pageformat' => 'custom',
700 'pagewidth' => '297mm',
701 'pageheight' => '210mm'
702 ]);
703
704 $reloaded = (new ilFormFieldParser(new ilCertificateXlstProcess()))->fetchDefaultFormFields($fo);
705
706 $this->assertStringNotContainsString('align=', $reloaded['certificate_text']);
707 $this->assertStringContainsString('text-align: center', $reloaded['certificate_text']);
708 $this->assertStringContainsString('text-align: left', $reloaded['certificate_text']);
709 $this->assertSame(
710 ['center', 'left'],
711 $this->paragraphAlignments($reloaded['certificate_text'])
712 );
713 }
714
716 {
717 $output = $this->createXlsFoParser()->parse([
718 'certificate_text' =>
719 '<p style="text-align: center;"><span style="font-size: 36pt;">Bestätigung</span></p>'
720 . '<p style="text-align: center;"><span style="font-size: 36pt;"></span></p>'
721 . '<p style="text-align: center;"><span style="font-size: 36pt;"></span></p>'
722 . '<p style="text-align: center;"><strong><span style="font-size: 12pt;">Durch dieses Zertifikat wird bestätigt, dass</span></strong></p>'
723 . '<p style="text-align: center;"><span style="font-size: 12pt;"><strong></strong></span></p>'
724 . '<p style="text-align: center;"><span style="font-size: 12pt;"><strong>Zertifikats-ID: [CERTIFICATE_ID]</strong></span></p>',
725 'margin_body' => [
726 'top' => '0cm',
727 'right' => '2cm',
728 'bottom' => '0cm',
729 'left' => '2cm'
730 ],
731 'pageformat' => 'custom',
732 'pagewidth' => '297mm',
733 'pageheight' => '210mm'
734 ]);
735
736 $document = new DOMDocument();
737 $document->loadXML($output);
738 $xpath = new DOMXPath($document);
739 $xpath->registerNamespace('fo', 'http://www.w3.org/1999/XSL/Format');
740
741 $blocks = [];
742 foreach ($xpath->query('//fo:flow/fo:block/fo:block') as $block) {
743 $blocks[] = $block;
744 }
745
746 $this->assertCount(6, $blocks);
747 foreach ($blocks as $block) {
748 $this->assertNotSame('', $block->textContent, $document->saveXML($block));
749 }
750
751 $this->assertSame('Bestätigung', $blocks[0]->textContent);
752 $this->assertSame("\u{00A0}", $blocks[1]->textContent);
753 $this->assertSame("\u{00A0}", $blocks[2]->textContent);
754 $this->assertSame('Durch dieses Zertifikat wird bestätigt, dass', $blocks[3]->textContent);
755 $this->assertSame("\u{00A0}", $blocks[4]->textContent);
756 $this->assertSame('Zertifikats-ID: [CERTIFICATE_ID]', $blocks[5]->textContent);
757 }
758}
$settings
Definition: index.php:44
testTransformingParagraphsWithNoTextAndNoChildrenResultsInNonBreakingSpaceXslFoBlock(array $form_data, string $fo)
static nonBreakingSpaceIsAddedDataProvider()
testParagraphsWithoutDescendantTextKeepANonBreakingSpace()
verifyFoGeneratedFromXhtml(array $form_data, string $fo)
static noNonBreakingSpaceIsAddedDataProvider()
normalizeXml(string $xml)
testCssTextAlignOverridesStaleAlignAttribute()
paragraphAlignments(string $certificate_text)
testTransformingParagraphsWithTextOrChildrenResultsNotInNonBreakingSpaceXslFoBlock(array $form_data, string $fo)
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.