Deployed 2063d78 to 5.4 with MkDocs 1.1.2 and mike 0.5.5
[GitHub/WoltLab/woltlab.github.io.git] / latest / tutorial / series / part_2 / index.html
CommitLineData
0c5338dd
TD
1
2<!doctype html>
3<html lang="en" class="no-js">
4 <head>
5
6 <meta charset="utf-8">
7 <meta name="viewport" content="width=device-width,initial-scale=1">
8
9
10
11
12 <link rel="shortcut icon" href="../../../assets/default.favicon.ico">
fb88dc6e 13 <meta name="generator" content="mkdocs-1.1.2, mkdocs-material-7.0.5">
0c5338dd
TD
14
15
16
17 <title>Part 2 - WoltLab Suite Documentation</title>
18
19
20
fb88dc6e 21 <link rel="stylesheet" href="../../../assets/stylesheets/main.77f3fd56.min.css">
0c5338dd
TD
22
23
24 <link rel="stylesheet" href="../../../assets/stylesheets/palette.7fa14f5b.min.css">
25
26
27
28 <meta name="theme-color" content="#009485">
29
30
31
32
33
fd8430cb
WG
34
35
0c5338dd
TD
36
37
38 <link rel="stylesheet" href="../../../stylesheets/extra.css">
39
40
41
42
43
44 </head>
45
46
47
48
49
50
51
52 <body dir="ltr" data-md-color-scheme="" data-md-color-primary="teal" data-md-color-accent="">
53
54
55
56 <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
57 <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
58 <label class="md-overlay" for="__drawer"></label>
59 <div data-md-component="skip">
60
61
62 <a href="#part-2-event-listeners-and-template-listeners" class="md-skip">
63 Skip to content
64 </a>
65
66 </div>
67 <div data-md-component="announce">
68
69 <aside class="md-announce">
70 <div class="md-announce__inner md-grid md-typeset">
71
72 <a href="https://www.woltlab.com">Back to <strong>woltlab.com</strong></a>
73
74 </div>
75 </aside>
76
77 </div>
78
79
80
81<header class="md-header" data-md-component="header">
82 <nav class="md-header__inner md-grid" aria-label="Header">
83 <a href="../../.." title="WoltLab Suite Documentation" class="md-header__button md-logo" aria-label="WoltLab Suite Documentation">
84
85 <img src="../../../assets/logo.png" alt="logo">
86
87 </a>
88 <label class="md-header__button md-icon" for="__drawer">
89 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/></svg>
90 </label>
91 <div class="md-header__title" data-md-component="header-title">
92 <div class="md-header__ellipsis">
93 <div class="md-header__topic">
94 <span class="md-ellipsis">
95 WoltLab Suite Documentation
96 </span>
97 </div>
98 <div class="md-header__topic" data-md-component="header-topic">
99 <span class="md-ellipsis">
100
101 Part 2
102
103 </span>
104 </div>
105 </div>
106 </div>
107 <div class="md-header__options">
108
109 </div>
110
0c5338dd 111
7124f4cb
WG
112 <div class="md-header__source">
113
114<a href="https://github.com/WoltLab/docs.woltlab.com/" title="Go to repository" class="md-source" data-md-component="source">
115 <div class="md-source__icon md-icon">
116
117 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
118 </div>
119 <div class="md-source__repository">
120 GitHub
121 </div>
122</a>
123 </div>
124
0c5338dd
TD
125 </nav>
126</header>
127
128 <div class="md-container" data-md-component="container">
129
130
131
132
133 <main class="md-main" data-md-component="main">
134 <div class="md-main__inner md-grid">
135
136
137
138 <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
139 <div class="md-sidebar__scrollwrap">
140 <div class="md-sidebar__inner">
141
142
143
144
145
146<nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
147 <label class="md-nav__title" for="__drawer">
148 <a href="../../.." title="WoltLab Suite Documentation" class="md-nav__button md-logo" aria-label="WoltLab Suite Documentation">
149
150 <img src="../../../assets/logo.png" alt="logo">
151
152 </a>
153 WoltLab Suite Documentation
154 </label>
155
7124f4cb
WG
156 <div class="md-nav__source">
157
158<a href="https://github.com/WoltLab/docs.woltlab.com/" title="Go to repository" class="md-source" data-md-component="source">
159 <div class="md-source__icon md-icon">
160
161 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05L244 40.45a28.87 28.87 0 0 0-40.81 0l-40.66 40.63 51.52 51.52c27.06-9.14 52.68 16.77 43.39 43.68l49.66 49.66c34.23-11.8 61.18 31 35.47 56.69-26.49 26.49-70.21-2.87-56-37.34L240.22 199v121.85c25.3 12.54 22.26 41.85 9.08 55a34.34 34.34 0 0 1-48.55 0c-17.57-17.6-11.07-46.91 11.25-56v-123c-20.8-8.51-24.6-30.74-18.64-45L142.57 101 8.45 235.14a28.86 28.86 0 0 0 0 40.81l195.61 195.6a28.86 28.86 0 0 0 40.8 0l194.69-194.69a28.86 28.86 0 0 0 0-40.81z"/></svg>
162 </div>
163 <div class="md-source__repository">
164 GitHub
165 </div>
166</a>
167 </div>
168
0c5338dd
TD
169 <ul class="md-nav__list" data-md-scrollfix>
170
171
172
173
174
175
176
177
178 <li class="md-nav__item">
179 <a href="../../../getting-started/" class="md-nav__link">
180 Getting Started
181 </a>
182 </li>
183
184
185
186
187
188
189
190
191
192
193
194 <li class="md-nav__item md-nav__item--nested">
195
196
197 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" >
198
199 <label class="md-nav__link" for="__nav_2">
200 PHP API
201 <span class="md-nav__icon md-icon"></span>
202 </label>
203 <nav class="md-nav" aria-label="PHP API" data-md-level="1">
204 <label class="md-nav__title" for="__nav_2">
205 <span class="md-nav__icon md-icon"></span>
206 PHP API
207 </label>
208 <ul class="md-nav__list" data-md-scrollfix>
209
210
211
212
213
214 <li class="md-nav__item">
215 <a href="../../../php/pages/" class="md-nav__link">
216 Pages
217 </a>
218 </li>
219
220
221
222
223
224
225
226 <li class="md-nav__item">
227 <a href="../../../php/database-objects/" class="md-nav__link">
228 Database Objects
229 </a>
230 </li>
231
232
233
234
235
236
237
238 <li class="md-nav__item">
239 <a href="../../../php/database-access/" class="md-nav__link">
240 Database Access
241 </a>
242 </li>
243
244
245
246
247
248
249
250 <li class="md-nav__item">
251 <a href="../../../php/exceptions/" class="md-nav__link">
252 Exceptions
253 </a>
254 </li>
255
256
257
258
259
260
261
262
263 <li class="md-nav__item md-nav__item--nested">
264
265
266 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2_5" type="checkbox" id="__nav_2_5" >
267
268 <label class="md-nav__link" for="__nav_2_5">
269 API
270 <span class="md-nav__icon md-icon"></span>
271 </label>
272 <nav class="md-nav" aria-label="API" data-md-level="2">
273 <label class="md-nav__title" for="__nav_2_5">
274 <span class="md-nav__icon md-icon"></span>
275 API
276 </label>
277 <ul class="md-nav__list" data-md-scrollfix>
278
279
280
281
282
283 <li class="md-nav__item">
284 <a href="../../../php/api/caches/" class="md-nav__link">
285 Caches
286 </a>
287 </li>
288
289
290
291
292
293
294
295 <li class="md-nav__item">
296 <a href="../../../php/api/comments/" class="md-nav__link">
297 Comments
298 </a>
299 </li>
300
301
302
303
304
305
306
307 <li class="md-nav__item">
308 <a href="../../../php/api/cronjobs/" class="md-nav__link">
309 Cronjobs
310 </a>
311 </li>
312
313
314
315
316
317
318
319 <li class="md-nav__item">
320 <a href="../../../php/api/events/" class="md-nav__link">
321 Events
322 </a>
323 </li>
324
325
326
327
328
329
330
331
332 <li class="md-nav__item md-nav__item--nested">
333
334
335 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2_5_5" type="checkbox" id="__nav_2_5_5" >
336
337 <label class="md-nav__link" for="__nav_2_5_5">
338 Form Builder
339 <span class="md-nav__icon md-icon"></span>
340 </label>
341 <nav class="md-nav" aria-label="Form Builder" data-md-level="3">
342 <label class="md-nav__title" for="__nav_2_5_5">
343 <span class="md-nav__icon md-icon"></span>
344 Form Builder
345 </label>
346 <ul class="md-nav__list" data-md-scrollfix>
347
348
349
350
351
352 <li class="md-nav__item">
353 <a href="../../../php/api/form_builder/overview/" class="md-nav__link">
354 Overview
355 </a>
356 </li>
357
358
359
360
361
362
363
364 <li class="md-nav__item">
365 <a href="../../../php/api/form_builder/structure/" class="md-nav__link">
366 Structure
367 </a>
368 </li>
369
370
371
372
373
374
375
376 <li class="md-nav__item">
377 <a href="../../../php/api/form_builder/form_fields/" class="md-nav__link">
378 Fields
379 </a>
380 </li>
381
382
383
384
385
386
387
388 <li class="md-nav__item">
389 <a href="../../../php/api/form_builder/validation_data/" class="md-nav__link">
390 Validation and Data
391 </a>
392 </li>
393
394
395
396
397
398
399
400 <li class="md-nav__item">
401 <a href="../../../php/api/form_builder/dependencies/" class="md-nav__link">
402 Dependencies
403 </a>
404 </li>
405
406
407
408 </ul>
409 </nav>
410 </li>
411
412
413
414
415
416
417
418 <li class="md-nav__item">
419 <a href="../../../php/api/package_installation_plugins/" class="md-nav__link">
420 Package Installation Plugins
421 </a>
422 </li>
423
424
425
426
427
428
429
430 <li class="md-nav__item">
431 <a href="../../../php/api/user_activity_points/" class="md-nav__link">
432 User Activity Points
433 </a>
434 </li>
435
436
437
438
439
440
441
442 <li class="md-nav__item">
443 <a href="../../../php/api/user_notifications/" class="md-nav__link">
444 User Notifications
445 </a>
446 </li>
447
448
449
450
451
452
453
454 <li class="md-nav__item">
455 <a href="../../../php/api/sitemaps/" class="md-nav__link">
456 Sitemaps
457 </a>
458 </li>
459
460
461
462 </ul>
463 </nav>
464 </li>
465
466
467
468
469
470
471
472 <li class="md-nav__item">
473 <a href="../../../php/code-style/" class="md-nav__link">
474 Code Style
475 </a>
476 </li>
477
478
479
480
481
482
483
484 <li class="md-nav__item">
485 <a href="../../../php/apps/" class="md-nav__link">
486 Apps
487 </a>
488 </li>
489
490
491
492
493
494
495
496 <li class="md-nav__item">
497 <a href="../../../php/gdpr/" class="md-nav__link">
498 GDPR
499 </a>
500 </li>
501
502
503
504 </ul>
505 </nav>
506 </li>
507
508
509
510
511
512
513
514
515
516
517
518 <li class="md-nav__item md-nav__item--nested">
519
520
521 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_3" type="checkbox" id="__nav_3" >
522
523 <label class="md-nav__link" for="__nav_3">
524 Languages, Templates & CSS
525 <span class="md-nav__icon md-icon"></span>
526 </label>
527 <nav class="md-nav" aria-label="Languages, Templates & CSS" data-md-level="1">
528 <label class="md-nav__title" for="__nav_3">
529 <span class="md-nav__icon md-icon"></span>
530 Languages, Templates & CSS
531 </label>
532 <ul class="md-nav__list" data-md-scrollfix>
533
534
535
536
537
538 <li class="md-nav__item">
539 <a href="../../../view/languages/" class="md-nav__link">
540 Languages
541 </a>
542 </li>
543
544
545
546
547
548
549
550 <li class="md-nav__item">
551 <a href="../../../view/templates/" class="md-nav__link">
552 Templates
553 </a>
554 </li>
555
556
557
558
559
560
561
562 <li class="md-nav__item">
563 <a href="../../../view/css/" class="md-nav__link">
564 CSS
565 </a>
566 </li>
567
568
569
570 </ul>
571 </nav>
572 </li>
573
574
575
576
577
578
579
580
581
582
583
584 <li class="md-nav__item md-nav__item--nested">
585
586
587 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_4" type="checkbox" id="__nav_4" >
588
589 <label class="md-nav__link" for="__nav_4">
590 JavaScript API
591 <span class="md-nav__icon md-icon"></span>
592 </label>
593 <nav class="md-nav" aria-label="JavaScript API" data-md-level="1">
594 <label class="md-nav__title" for="__nav_4">
595 <span class="md-nav__icon md-icon"></span>
596 JavaScript API
597 </label>
598 <ul class="md-nav__list" data-md-scrollfix>
599
600
601
602
603
604 <li class="md-nav__item">
605 <a href="../../../javascript/general-usage/" class="md-nav__link">
606 General Usage
607 </a>
608 </li>
609
610
611
612
613
614
615
616
617 <li class="md-nav__item md-nav__item--nested">
618
619
620 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_4_2" type="checkbox" id="__nav_4_2" >
621
622 <label class="md-nav__link" for="__nav_4_2">
623 New API
624 <span class="md-nav__icon md-icon"></span>
625 </label>
626 <nav class="md-nav" aria-label="New API" data-md-level="2">
627 <label class="md-nav__title" for="__nav_4_2">
628 <span class="md-nav__icon md-icon"></span>
629 New API
630 </label>
631 <ul class="md-nav__list" data-md-scrollfix>
632
633
634
635
636
637 <li class="md-nav__item">
638 <a href="../../../javascript/new-api_writing-a-module/" class="md-nav__link">
639 Writing a module
640 </a>
641 </li>
642
643
644
645
646
647
648
649 <li class="md-nav__item">
650 <a href="../../../javascript/new-api_data-structures/" class="md-nav__link">
651 Data Structures
652 </a>
653 </li>
654
655
656
657
658
659
660
661 <li class="md-nav__item">
662 <a href="../../../javascript/new-api_core/" class="md-nav__link">
663 Core Functions
664 </a>
665 </li>
666
667
668
669
670
671
672
673 <li class="md-nav__item">
674 <a href="../../../javascript/new-api_dom/" class="md-nav__link">
675 DOM
676 </a>
677 </li>
678
679
680
681
682
683
684
685 <li class="md-nav__item">
686 <a href="../../../javascript/new-api_events/" class="md-nav__link">
687 Event Handling
688 </a>
689 </li>
690
691
692
693
694
695
696
697 <li class="md-nav__item">
698 <a href="../../../javascript/new-api_ajax/" class="md-nav__link">
699 Ajax
700 </a>
701 </li>
702
703
704
705
706
707
708
709 <li class="md-nav__item">
710 <a href="../../../javascript/new-api_dialogs/" class="md-nav__link">
711 Dialogs
712 </a>
713 </li>
714
715
716
717
718
719
720
721 <li class="md-nav__item">
722 <a href="../../../javascript/new-api_browser/" class="md-nav__link">
723 Browser and Screen Sizes
724 </a>
725 </li>
726
727
728
729
730
731
732
733 <li class="md-nav__item">
734 <a href="../../../javascript/new-api_ui/" class="md-nav__link">
735 User Interface
736 </a>
737 </li>
738
739
740
741 </ul>
742 </nav>
743 </li>
744
745
746
747
748
749
750
751 <li class="md-nav__item">
752 <a href="../../../javascript/legacy-api/" class="md-nav__link">
753 Legacy API
754 </a>
755 </li>
756
757
758
759
760
761
762
763 <li class="md-nav__item">
764 <a href="../../../javascript/helper-functions/" class="md-nav__link">
765 Helper Functions
766 </a>
767 </li>
768
769
770
771
772
773
774
775 <li class="md-nav__item">
776 <a href="../../../javascript/code-snippets/" class="md-nav__link">
777 Code Snippets
778 </a>
779 </li>
780
781
782
783 </ul>
784 </nav>
785 </li>
786
787
788
789
790
791
792
793
794
795
796
797 <li class="md-nav__item md-nav__item--nested">
798
799
800 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_5" type="checkbox" id="__nav_5" >
801
802 <label class="md-nav__link" for="__nav_5">
803 Package Components
804 <span class="md-nav__icon md-icon"></span>
805 </label>
806 <nav class="md-nav" aria-label="Package Components" data-md-level="1">
807 <label class="md-nav__title" for="__nav_5">
808 <span class="md-nav__icon md-icon"></span>
809 Package Components
810 </label>
811 <ul class="md-nav__list" data-md-scrollfix>
812
813
814
815
816
817 <li class="md-nav__item">
818 <a href="../../../package/package-xml/" class="md-nav__link">
819 package.xml
820 </a>
821 </li>
822
823
824
825
826
827
828
829
830 <li class="md-nav__item md-nav__item--nested">
831
832
833 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_5_2" type="checkbox" id="__nav_5_2" >
834
835 <label class="md-nav__link" for="__nav_5_2">
836 PIPs
837 <span class="md-nav__icon md-icon"></span>
838 </label>
839 <nav class="md-nav" aria-label="PIPs" data-md-level="2">
840 <label class="md-nav__title" for="__nav_5_2">
841 <span class="md-nav__icon md-icon"></span>
842 PIPs
843 </label>
844 <ul class="md-nav__list" data-md-scrollfix>
845
846
847
848
849
850 <li class="md-nav__item">
851 <a href="../../../package/pip/" class="md-nav__link">
852 Overview
853 </a>
854 </li>
855
856
857
858
859
860
861
862 <li class="md-nav__item">
863 <a href="../../../package/pip/acl-option/" class="md-nav__link">
864 aclOption
865 </a>
866 </li>
867
868
869
870
871
872
873
874 <li class="md-nav__item">
875 <a href="../../../package/pip/acp-menu/" class="md-nav__link">
876 acpMenu
877 </a>
878 </li>
879
880
881
882
883
884
885
886 <li class="md-nav__item">
887 <a href="../../../package/pip/acp-search-provider/" class="md-nav__link">
888 acpSearchProvider
889 </a>
890 </li>
891
892
893
894
895
896
897
898 <li class="md-nav__item">
899 <a href="../../../package/pip/acp-template/" class="md-nav__link">
900 acpTemplate
901 </a>
902 </li>
903
904
905
906
907
908
909
910 <li class="md-nav__item">
911 <a href="../../../package/pip/bbcode/" class="md-nav__link">
912 bbcode
913 </a>
914 </li>
915
916
917
918
919
920
921
922 <li class="md-nav__item">
923 <a href="../../../package/pip/box/" class="md-nav__link">
924 box
925 </a>
926 </li>
927
928
929
930
931
932
933
934 <li class="md-nav__item">
935 <a href="../../../package/pip/clipboard-action/" class="md-nav__link">
936 clipboardAction
937 </a>
938 </li>
939
940
941
942
943
944
945
946 <li class="md-nav__item">
947 <a href="../../../package/pip/core-object/" class="md-nav__link">
948 coreObject
949 </a>
950 </li>
951
952
953
954
955
956
957
958 <li class="md-nav__item">
959 <a href="../../../package/pip/cronjob/" class="md-nav__link">
960 cronjob
961 </a>
962 </li>
963
964
965
966
967
968
969
970 <li class="md-nav__item">
971 <a href="../../../package/pip/event-listener/" class="md-nav__link">
972 eventListener
973 </a>
974 </li>
975
976
977
978
979
980
981
982 <li class="md-nav__item">
983 <a href="../../../package/pip/file/" class="md-nav__link">
984 file
985 </a>
986 </li>
987
988
989
990
991
992
993
994 <li class="md-nav__item">
995 <a href="../../../package/pip/language/" class="md-nav__link">
996 language
997 </a>
998 </li>
999
1000
1001
1002
1003
1004
1005
1006 <li class="md-nav__item">
1007 <a href="../../../package/pip/media-provider/" class="md-nav__link">
1008 mediaProvider
1009 </a>
1010 </li>
1011
1012
1013
1014
1015
1016
1017
1018 <li class="md-nav__item">
1019 <a href="../../../package/pip/menu/" class="md-nav__link">
1020 menu
1021 </a>
1022 </li>
1023
1024
1025
1026
1027
1028
1029
1030 <li class="md-nav__item">
1031 <a href="../../../package/pip/menu-item/" class="md-nav__link">
1032 menuItem
1033 </a>
1034 </li>
1035
1036
1037
1038
1039
1040
1041
1042 <li class="md-nav__item">
1043 <a href="../../../package/pip/object-type/" class="md-nav__link">
1044 objectType
1045 </a>
1046 </li>
1047
1048
1049
1050
1051
1052
1053
1054 <li class="md-nav__item">
1055 <a href="../../../package/pip/object-type-definition/" class="md-nav__link">
1056 objectTypeDefinition
1057 </a>
1058 </li>
1059
1060
1061
1062
1063
1064
1065
1066 <li class="md-nav__item">
1067 <a href="../../../package/pip/option/" class="md-nav__link">
1068 option
1069 </a>
1070 </li>
1071
1072
1073
1074
1075
1076
1077
1078 <li class="md-nav__item">
1079 <a href="../../../package/pip/page/" class="md-nav__link">
1080 page
1081 </a>
1082 </li>
1083
1084
1085
1086
1087
1088
1089
1090 <li class="md-nav__item">
1091 <a href="../../../package/pip/pip/" class="md-nav__link">
1092 pip
1093 </a>
1094 </li>
1095
1096
1097
1098
1099
1100
1101
1102 <li class="md-nav__item">
1103 <a href="../../../package/pip/script/" class="md-nav__link">
1104 script
1105 </a>
1106 </li>
1107
1108
1109
1110
1111
1112
1113
1114 <li class="md-nav__item">
1115 <a href="../../../package/pip/smiley/" class="md-nav__link">
1116 smiley
1117 </a>
1118 </li>
1119
1120
1121
1122
1123
1124
1125
1126 <li class="md-nav__item">
1127 <a href="../../../package/pip/sql/" class="md-nav__link">
1128 sql
1129 </a>
1130 </li>
1131
1132
1133
1134
1135
1136
1137
1138 <li class="md-nav__item">
1139 <a href="../../../package/pip/style/" class="md-nav__link">
1140 style
1141 </a>
1142 </li>
1143
1144
1145
1146
1147
1148
1149
1150 <li class="md-nav__item">
1151 <a href="../../../package/pip/template/" class="md-nav__link">
1152 template
1153 </a>
1154 </li>
1155
1156
1157
1158
1159
1160
1161
1162 <li class="md-nav__item">
1163 <a href="../../../package/pip/template-listener/" class="md-nav__link">
1164 templateListener
1165 </a>
1166 </li>
1167
1168
1169
1170
1171
1172
1173
1174 <li class="md-nav__item">
1175 <a href="../../../package/pip/user-group-option/" class="md-nav__link">
1176 userGroupOption
1177 </a>
1178 </li>
1179
1180
1181
1182
1183
1184
1185
1186 <li class="md-nav__item">
1187 <a href="../../../package/pip/user-menu/" class="md-nav__link">
1188 userMenu
1189 </a>
1190 </li>
1191
1192
1193
1194
1195
1196
1197
1198 <li class="md-nav__item">
1199 <a href="../../../package/pip/user-notification-event/" class="md-nav__link">
1200 userNotificationEvent
1201 </a>
1202 </li>
1203
1204
1205
1206
1207
1208
1209
1210 <li class="md-nav__item">
1211 <a href="../../../package/pip/user-option/" class="md-nav__link">
1212 userOption
1213 </a>
1214 </li>
1215
1216
1217
1218
1219
1220
1221
1222 <li class="md-nav__item">
1223 <a href="../../../package/pip/user-profile-menu/" class="md-nav__link">
1224 userProfileMenu
1225 </a>
1226 </li>
1227
1228
1229
1230 </ul>
1231 </nav>
1232 </li>
1233
1234
1235
1236
1237
1238
1239
1240 <li class="md-nav__item">
1241 <a href="../../../package/database-php-api/" class="md-nav__link">
1242 Database PHP API
1243 </a>
1244 </li>
1245
1246
1247
1248 </ul>
1249 </nav>
1250 </li>
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262 <li class="md-nav__item md-nav__item--nested">
1263
1264
1265 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6" type="checkbox" id="__nav_6" >
1266
1267 <label class="md-nav__link" for="__nav_6">
1268 Migration
1269 <span class="md-nav__icon md-icon"></span>
1270 </label>
1271 <nav class="md-nav" aria-label="Migration" data-md-level="1">
1272 <label class="md-nav__title" for="__nav_6">
1273 <span class="md-nav__icon md-icon"></span>
1274 Migration
1275 </label>
1276 <ul class="md-nav__list" data-md-scrollfix>
1277
1278
1279
1280
1281
1282
1283 <li class="md-nav__item md-nav__item--nested">
1284
1285
1286 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_1" type="checkbox" id="__nav_6_1" >
1287
1288 <label class="md-nav__link" for="__nav_6_1">
1289 Migrating from WSC 5.3
1290 <span class="md-nav__icon md-icon"></span>
1291 </label>
1292 <nav class="md-nav" aria-label="Migrating from WSC 5.3" data-md-level="2">
1293 <label class="md-nav__title" for="__nav_6_1">
1294 <span class="md-nav__icon md-icon"></span>
1295 Migrating from WSC 5.3
1296 </label>
1297 <ul class="md-nav__list" data-md-scrollfix>
1298
1299
1300
1301
1302
1303 <li class="md-nav__item">
1304 <a href="../../../migration/wsc53/php/" class="md-nav__link">
1305 PHP API
1306 </a>
1307 </li>
1308
1309
1310
1311
1312
1313
1314
1315 <li class="md-nav__item">
1316 <a href="../../../migration/wsc53/session/" class="md-nav__link">
1317 Session Handling and Authentication
1318 </a>
1319 </li>
1320
1321
1322
1323
1324
1325
1326
1327 <li class="md-nav__item">
1328 <a href="../../../migration/wsc53/javascript/" class="md-nav__link">
1329 JavaScript
1330 </a>
1331 </li>
1332
1333
1334
1335
1336
1337
1338
1339 <li class="md-nav__item">
1340 <a href="../../../migration/wsc53/templates/" class="md-nav__link">
1341 Templates
1342 </a>
1343 </li>
1344
1345
1346
1347
1348
1349
1350
1351 <li class="md-nav__item">
1352 <a href="../../../migration/wsc53/libraries/" class="md-nav__link">
1353 Third Party Libraries
1354 </a>
1355 </li>
1356
1357
1358
1359 </ul>
1360 </nav>
1361 </li>
1362
1363
1364
1365
1366
1367
1368
1369
1370 <li class="md-nav__item md-nav__item--nested">
1371
1372
1373 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_2" type="checkbox" id="__nav_6_2" >
1374
1375 <label class="md-nav__link" for="__nav_6_2">
1376 Migrating from WSC 5.2
1377 <span class="md-nav__icon md-icon"></span>
1378 </label>
1379 <nav class="md-nav" aria-label="Migrating from WSC 5.2" data-md-level="2">
1380 <label class="md-nav__title" for="__nav_6_2">
1381 <span class="md-nav__icon md-icon"></span>
1382 Migrating from WSC 5.2
1383 </label>
1384 <ul class="md-nav__list" data-md-scrollfix>
1385
1386
1387
1388
1389
1390 <li class="md-nav__item">
1391 <a href="../../../migration/wsc52/php/" class="md-nav__link">
1392 PHP API
1393 </a>
1394 </li>
1395
1396
1397
1398
1399
1400
1401
1402 <li class="md-nav__item">
1403 <a href="../../../migration/wsc52/templates/" class="md-nav__link">
1404 Templates and Languages
1405 </a>
1406 </li>
1407
1408
1409
1410
1411
1412
1413
1414 <li class="md-nav__item">
1415 <a href="../../../migration/wsc52/libraries/" class="md-nav__link">
1416 Third Party Libraries
1417 </a>
1418 </li>
1419
1420
1421
1422 </ul>
1423 </nav>
1424 </li>
1425
1426
1427
1428
1429
1430
1431
1432
1433 <li class="md-nav__item md-nav__item--nested">
1434
1435
1436 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_3" type="checkbox" id="__nav_6_3" >
1437
1438 <label class="md-nav__link" for="__nav_6_3">
1439 Migrating from WSC 3.1
1440 <span class="md-nav__icon md-icon"></span>
1441 </label>
1442 <nav class="md-nav" aria-label="Migrating from WSC 3.1" data-md-level="2">
1443 <label class="md-nav__title" for="__nav_6_3">
1444 <span class="md-nav__icon md-icon"></span>
1445 Migrating from WSC 3.1
1446 </label>
1447 <ul class="md-nav__list" data-md-scrollfix>
1448
1449
1450
1451
1452
1453 <li class="md-nav__item">
1454 <a href="../../../migration/wsc31/php/" class="md-nav__link">
1455 PHP API
1456 </a>
1457 </li>
1458
1459
1460
1461 </ul>
1462 </nav>
1463 </li>
1464
1465
1466
1467
1468
1469
1470
1471
1472 <li class="md-nav__item md-nav__item--nested">
1473
1474
1475 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_4" type="checkbox" id="__nav_6_4" >
1476
1477 <label class="md-nav__link" for="__nav_6_4">
1478 Migrating from WSC 3.0
1479 <span class="md-nav__icon md-icon"></span>
1480 </label>
1481 <nav class="md-nav" aria-label="Migrating from WSC 3.0" data-md-level="2">
1482 <label class="md-nav__title" for="__nav_6_4">
1483 <span class="md-nav__icon md-icon"></span>
1484 Migrating from WSC 3.0
1485 </label>
1486 <ul class="md-nav__list" data-md-scrollfix>
1487
1488
1489
1490
1491
1492 <li class="md-nav__item">
1493 <a href="../../../migration/wsc30/php/" class="md-nav__link">
1494 PHP API
1495 </a>
1496 </li>
1497
1498
1499
1500
1501
1502
1503
1504 <li class="md-nav__item">
1505 <a href="../../../migration/wsc30/javascript/" class="md-nav__link">
1506 JavaScript API
1507 </a>
1508 </li>
1509
1510
1511
1512
1513
1514
1515
1516 <li class="md-nav__item">
1517 <a href="../../../migration/wsc30/templates/" class="md-nav__link">
1518 Templates
1519 </a>
1520 </li>
1521
1522
1523
1524
1525
1526
1527
1528 <li class="md-nav__item">
1529 <a href="../../../migration/wsc30/css/" class="md-nav__link">
1530 CSS
1531 </a>
1532 </li>
1533
1534
1535
1536
1537
1538
1539
1540 <li class="md-nav__item">
1541 <a href="../../../migration/wsc30/package/" class="md-nav__link">
1542 Package Components
1543 </a>
1544 </li>
1545
1546
1547
1548 </ul>
1549 </nav>
1550 </li>
1551
1552
1553
1554
1555
1556
1557
1558
1559 <li class="md-nav__item md-nav__item--nested">
1560
1561
1562 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_5" type="checkbox" id="__nav_6_5" >
1563
1564 <label class="md-nav__link" for="__nav_6_5">
1565 Migrating from WCF 2.1
1566 <span class="md-nav__icon md-icon"></span>
1567 </label>
1568 <nav class="md-nav" aria-label="Migrating from WCF 2.1" data-md-level="2">
1569 <label class="md-nav__title" for="__nav_6_5">
1570 <span class="md-nav__icon md-icon"></span>
1571 Migrating from WCF 2.1
1572 </label>
1573 <ul class="md-nav__list" data-md-scrollfix>
1574
1575
1576
1577
1578
1579 <li class="md-nav__item">
1580 <a href="../../../migration/wcf21/php/" class="md-nav__link">
1581 PHP API
1582 </a>
1583 </li>
1584
1585
1586
1587
1588
1589
1590
1591 <li class="md-nav__item">
1592 <a href="../../../migration/wcf21/templates/" class="md-nav__link">
1593 Templates
1594 </a>
1595 </li>
1596
1597
1598
1599
1600
1601
1602
1603 <li class="md-nav__item">
1604 <a href="../../../migration/wcf21/css/" class="md-nav__link">
1605 CSS
1606 </a>
1607 </li>
1608
1609
1610
1611
1612
1613
1614
1615 <li class="md-nav__item">
1616 <a href="../../../migration/wcf21/package/" class="md-nav__link">
1617 Package Components
1618 </a>
1619 </li>
1620
1621
1622
1623 </ul>
1624 </nav>
1625 </li>
1626
1627
1628
1629 </ul>
1630 </nav>
1631 </li>
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645 <li class="md-nav__item md-nav__item--active md-nav__item--nested">
1646
1647
1648 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_7" type="checkbox" id="__nav_7" checked>
1649
1650 <label class="md-nav__link" for="__nav_7">
1651 Tutorials
1652 <span class="md-nav__icon md-icon"></span>
1653 </label>
1654 <nav class="md-nav" aria-label="Tutorials" data-md-level="1">
1655 <label class="md-nav__title" for="__nav_7">
1656 <span class="md-nav__icon md-icon"></span>
1657 Tutorials
1658 </label>
1659 <ul class="md-nav__list" data-md-scrollfix>
1660
1661
1662
1663
1664
1665
1666
1667
1668 <li class="md-nav__item md-nav__item--active md-nav__item--nested">
1669
1670
1671 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_7_1" type="checkbox" id="__nav_7_1" checked>
1672
1673 <label class="md-nav__link" for="__nav_7_1">
1674 Tutorial Series
1675 <span class="md-nav__icon md-icon"></span>
1676 </label>
1677 <nav class="md-nav" aria-label="Tutorial Series" data-md-level="2">
1678 <label class="md-nav__title" for="__nav_7_1">
1679 <span class="md-nav__icon md-icon"></span>
1680 Tutorial Series
1681 </label>
1682 <ul class="md-nav__list" data-md-scrollfix>
1683
1684
1685
1686
1687
1688 <li class="md-nav__item">
1689 <a href="../overview/" class="md-nav__link">
1690 Overview
1691 </a>
1692 </li>
1693
1694
1695
1696
1697
1698
1699
1700 <li class="md-nav__item">
1701 <a href="../part_1/" class="md-nav__link">
1702 Part 1
1703 </a>
1704 </li>
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714 <li class="md-nav__item md-nav__item--active">
1715
1716 <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc">
1717
1718
1719
1720
1721 <label class="md-nav__link md-nav__link--active" for="__toc">
1722 Part 2
1723 <span class="md-nav__icon md-icon"></span>
1724 </label>
1725
1726 <a href="./" class="md-nav__link md-nav__link--active">
1727 Part 2
1728 </a>
1729
1730
1731<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
1732
1733
1734
1735
1736
1737 <label class="md-nav__title" for="__toc">
1738 <span class="md-nav__icon md-icon"></span>
1739 Table of contents
1740 </label>
1741 <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
1742
1743 <li class="md-nav__item">
1744 <a href="#package-functionality" class="md-nav__link">
1745 Package Functionality
1746 </a>
1747
1748</li>
1749
1750 <li class="md-nav__item">
1751 <a href="#used-components" class="md-nav__link">
1752 Used Components
1753 </a>
1754
1755</li>
1756
1757 <li class="md-nav__item">
1758 <a href="#package-structure" class="md-nav__link">
1759 Package Structure
1760 </a>
1761
1762</li>
1763
1764 <li class="md-nav__item">
1765 <a href="#extending-person-model-installsql" class="md-nav__link">
1766 Extending Person Model (install.sql)
1767 </a>
1768
1769</li>
1770
1771 <li class="md-nav__item">
1772 <a href="#setting-birthday-in-acp" class="md-nav__link">
1773 Setting Birthday in ACP
1774 </a>
1775
1776</li>
1777
1778 <li class="md-nav__item">
1779 <a href="#adding-birthday-table-column-in-acp" class="md-nav__link">
1780 Adding Birthday Table Column in ACP
1781 </a>
1782
1783</li>
1784
1785 <li class="md-nav__item">
1786 <a href="#adding-birthday-in-front-end" class="md-nav__link">
1787 Adding Birthday in Front End
1788 </a>
1789
1790</li>
1791
1792 <li class="md-nav__item">
1793 <a href="#templatelistenerxml" class="md-nav__link">
1794 templateListener.xml
1795 </a>
1796
1797</li>
1798
1799 <li class="md-nav__item">
1800 <a href="#eventlistenerxml" class="md-nav__link">
1801 eventListener.xml
1802 </a>
1803
1804</li>
1805
1806 <li class="md-nav__item">
1807 <a href="#packagexml" class="md-nav__link">
1808 package.xml
1809 </a>
1810
1811</li>
1812
1813 </ul>
1814
1815</nav>
1816
1817 </li>
1818
1819
1820
1821
1822
1823
1824
1825 <li class="md-nav__item">
1826 <a href="../part_3/" class="md-nav__link">
1827 Part 3
1828 </a>
1829 </li>
1830
1831
1832
1833 </ul>
1834 </nav>
1835 </li>
1836
1837
1838
1839 </ul>
1840 </nav>
1841 </li>
1842
1843
1844
1845 </ul>
1846</nav>
1847 </div>
1848 </div>
1849 </div>
1850
1851
1852
1853 <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
1854 <div class="md-sidebar__scrollwrap">
1855 <div class="md-sidebar__inner">
1856
1857<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
1858
1859
1860
1861
1862
1863 <label class="md-nav__title" for="__toc">
1864 <span class="md-nav__icon md-icon"></span>
1865 Table of contents
1866 </label>
1867 <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
1868
1869 <li class="md-nav__item">
1870 <a href="#package-functionality" class="md-nav__link">
1871 Package Functionality
1872 </a>
1873
1874</li>
1875
1876 <li class="md-nav__item">
1877 <a href="#used-components" class="md-nav__link">
1878 Used Components
1879 </a>
1880
1881</li>
1882
1883 <li class="md-nav__item">
1884 <a href="#package-structure" class="md-nav__link">
1885 Package Structure
1886 </a>
1887
1888</li>
1889
1890 <li class="md-nav__item">
1891 <a href="#extending-person-model-installsql" class="md-nav__link">
1892 Extending Person Model (install.sql)
1893 </a>
1894
1895</li>
1896
1897 <li class="md-nav__item">
1898 <a href="#setting-birthday-in-acp" class="md-nav__link">
1899 Setting Birthday in ACP
1900 </a>
1901
1902</li>
1903
1904 <li class="md-nav__item">
1905 <a href="#adding-birthday-table-column-in-acp" class="md-nav__link">
1906 Adding Birthday Table Column in ACP
1907 </a>
1908
1909</li>
1910
1911 <li class="md-nav__item">
1912 <a href="#adding-birthday-in-front-end" class="md-nav__link">
1913 Adding Birthday in Front End
1914 </a>
1915
1916</li>
1917
1918 <li class="md-nav__item">
1919 <a href="#templatelistenerxml" class="md-nav__link">
1920 templateListener.xml
1921 </a>
1922
1923</li>
1924
1925 <li class="md-nav__item">
1926 <a href="#eventlistenerxml" class="md-nav__link">
1927 eventListener.xml
1928 </a>
1929
1930</li>
1931
1932 <li class="md-nav__item">
1933 <a href="#packagexml" class="md-nav__link">
1934 package.xml
1935 </a>
1936
1937</li>
1938
1939 </ul>
1940
1941</nav>
1942 </div>
1943 </div>
1944 </div>
1945
1946
1947 <div class="md-content" data-md-component="content">
1948 <article class="md-content__inner md-typeset">
1949
1950
9b8bddda 1951 <a href="https://github.com/WoltLab/docs.woltlab.com/edit/5.4/docs/tutorial/series/part_2.md" title="Edit this page" class="md-content__button md-icon">
7124f4cb
WG
1952 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83 3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75L3 17.25z"/></svg>
1953 </a>
1954
0c5338dd
TD
1955
1956 <h1 id="part-2-event-listeners-and-template-listeners">Part 2: Event Listeners and Template Listeners<a class="headerlink" href="#part-2-event-listeners-and-template-listeners" title="Permanent link">#</a></h1>
1957<p>In the <a href="../part_1/">first part</a> of this tutorial series, we have created the base structure of our people management package.
1958In further parts, we will use the package of the first part as a basis to directly add new features.
1959In order to explain how event listeners and template works, however, we will not directly adding a new feature to the package by altering it in this part, but we will assume that somebody else created the package and that we want to extend it the “correct” way by creating a plugin.</p>
1960<p>The goal of the small plugin that will be created in this part is to add the birthday of the managed people.
1961As in the first part, we will not bother with careful validation of the entered date but just make sure that it is a valid date.</p>
1962<h2 id="package-functionality">Package Functionality<a class="headerlink" href="#package-functionality" title="Permanent link">#</a></h2>
1963<p>The package should provide the following possibilities/functions:</p>
1964<ul>
1965<li>List person’s birthday (if set) in people list in the ACP</li>
1966<li>Sort people list by birthday in the ACP</li>
1967<li>Add or remove birthday when adding or editing person</li>
1968<li>List person’s birthday (if set) in people list in the front end</li>
1969<li>Sort people list by birthday in the front end</li>
1970</ul>
1971<h2 id="used-components">Used Components<a class="headerlink" href="#used-components" title="Permanent link">#</a></h2>
1972<p>We will use the following package installation plugins:</p>
1973<ul>
1974<li><a href="../../../package/pip/acp-template/">acpTemplate package installation plugin</a>,</li>
1975<li><a href="../../../package/pip/event-listener/">eventListener package installation plugin</a>,</li>
1976<li><a href="../../../package/pip/file/">file package installation plugin</a>,</li>
1977<li><a href="../../../package/pip/language/">language package installation plugin</a>,</li>
1978<li><a href="../../../package/pip/sql/">sql package installation plugin</a>,</li>
1979<li><a href="../../../package/pip/template/">template package installation plugin</a>,</li>
1980<li><a href="../../../package/pip/template-listener/">templateListener package installation plugin</a>.</li>
1981</ul>
1982<p>For more information about the event system, please refer to the <a href="../../../php/api/events/">dedicated page on events</a>.</p>
1983<h2 id="package-structure">Package Structure<a class="headerlink" href="#package-structure" title="Permanent link">#</a></h2>
1984<p>The package will have the following file structure:</p>
1985<div class="highlight"><pre><span></span><code>├── acptemplates
1986│ └── __personAddBirthday.tpl
1987├── eventListener.xml
1988├── files
1989│ └── lib
1990│ └── system
1991│ └── event
1992│ └── listener
1993│ ├── BirthdayPersonAddFormListener.class.php
1994│ └── BirthdaySortFieldPersonListPageListener.class.php
1995├── install.sql
1996├── language
1997│ ├── de.xml
1998│ └── en.xml
1999├── package.xml
2000├── templateListener.xml
2001└── templates
2002 ├── __personListBirthday.tpl
2003 └── __personListBirthdaySortField.tpl
2004</code></pre></div>
0c5338dd
TD
2005<h2 id="extending-person-model-installsql">Extending Person Model (<code>install.sql</code>)<a class="headerlink" href="#extending-person-model-installsql" title="Permanent link">#</a></h2>
2006<p>The existing model of a person only contains the person’s first name and their last name (in additional to the id used to identify created people).
2007To add the birthday to the model, we need to create an additional database table column using the <a href="../../../package/pip/sql/">sql package installation plugin</a>:</p>
2008<div class="highlight"><pre><span></span><code><span class="k">ALTER</span> <span class="k">TABLE</span> <span class="n">wcf1_person</span> <span class="k">ADD</span> <span class="n">birthday</span> <span class="nb">DATE</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">;</span>
2009</code></pre></div>
0c5338dd
TD
2010<p>If we have a <a href="../part_1/#person">Person object</a>, this new property can be accessed the same way as the <code>personID</code> property, the <code>firstName</code> property, or the <code>lastName</code> property from the base package: <code>$person-&gt;birthday</code>.</p>
2011<h2 id="setting-birthday-in-acp">Setting Birthday in ACP<a class="headerlink" href="#setting-birthday-in-acp" title="Permanent link">#</a></h2>
2012<p>To set the birthday of a person, we need to extend the <code>personAdd</code> template to add an additional birthday field.
2013This can be achieved using the <code>dataFields</code> template event at whose position we inject the following template code:</p>
2014<div class="highlight"><pre><span></span><code><span class="o">&lt;</span><span class="n">dl</span><span class="err">{</span><span class="k">if</span> <span class="err">$</span><span class="n">errorField</span> <span class="o">==</span> <span class="s1">&#39;birthday&#39;</span><span class="err">}</span> <span class="k">class</span><span class="o">=</span><span class="ss">&quot;formError&quot;</span><span class="err">{</span><span class="o">/</span><span class="k">if</span><span class="err">}</span><span class="o">&gt;</span>
2015 <span class="o">&lt;</span><span class="n">dt</span><span class="o">&gt;&lt;</span><span class="n">label</span> <span class="k">for</span><span class="o">=</span><span class="ss">&quot;birthday&quot;</span><span class="o">&gt;</span><span class="err">{</span><span class="n">lang</span><span class="err">}</span><span class="n">wcf</span><span class="p">.</span><span class="n">person</span><span class="p">.</span><span class="n">birthday</span><span class="err">{</span><span class="o">/</span><span class="n">lang</span><span class="err">}</span><span class="o">&lt;/</span><span class="n">label</span><span class="o">&gt;&lt;/</span><span class="n">dt</span><span class="o">&gt;</span>
2016 <span class="o">&lt;</span><span class="n">dd</span><span class="o">&gt;</span>
2017 <span class="o">&lt;</span><span class="k">input</span> <span class="k">type</span><span class="o">=</span><span class="ss">&quot;date&quot;</span> <span class="n">id</span><span class="o">=</span><span class="ss">&quot;birthday&quot;</span> <span class="n">name</span><span class="o">=</span><span class="ss">&quot;birthday&quot;</span> <span class="n">value</span><span class="o">=</span><span class="ss">&quot;{$birthday}&quot;</span><span class="o">&gt;</span>
2018 <span class="err">{</span><span class="k">if</span> <span class="err">$</span><span class="n">errorField</span> <span class="o">==</span> <span class="s1">&#39;birthday&#39;</span><span class="err">}</span>
2019 <span class="o">&lt;</span><span class="n">small</span> <span class="k">class</span><span class="o">=</span><span class="ss">&quot;innerError&quot;</span><span class="o">&gt;</span>
2020 <span class="err">{</span><span class="k">if</span> <span class="err">$</span><span class="n">errorType</span> <span class="o">==</span> <span class="s1">&#39;noValidSelection&#39;</span><span class="err">}</span>
2021 <span class="err">{</span><span class="n">lang</span><span class="err">}</span><span class="n">wcf</span><span class="p">.</span><span class="k">global</span><span class="p">.</span><span class="n">form</span><span class="p">.</span><span class="n">error</span><span class="p">.</span><span class="n">noValidSelection</span><span class="err">{</span><span class="o">/</span><span class="n">lang</span><span class="err">}</span>
2022 <span class="err">{</span><span class="k">else</span><span class="err">}</span>
2023 <span class="err">{</span><span class="n">lang</span><span class="err">}</span><span class="n">wcf</span><span class="p">.</span><span class="n">acp</span><span class="p">.</span><span class="n">person</span><span class="p">.</span><span class="n">birthday</span><span class="p">.</span><span class="n">error</span><span class="p">.</span><span class="err">{$</span><span class="n">errorType</span><span class="err">}{</span><span class="o">/</span><span class="n">lang</span><span class="err">}</span>
2024 <span class="err">{</span><span class="o">/</span><span class="k">if</span><span class="err">}</span>
2025 <span class="o">&lt;/</span><span class="n">small</span><span class="o">&gt;</span>
2026 <span class="err">{</span><span class="o">/</span><span class="k">if</span><span class="err">}</span>
2027 <span class="o">&lt;/</span><span class="n">dd</span><span class="o">&gt;</span>
2028<span class="o">&lt;/</span><span class="n">dl</span><span class="o">&gt;</span>
2029</code></pre></div>
0c5338dd
TD
2030<p>which we store in a <code>__personAddBirthday.tpl</code> template file.
2031The used language item <code>wcf.person.birthday</code> is actually the only new one for this package:</p>
2032<div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="n">xml</span> <span class="k">version</span><span class="o">=</span><span class="ss">&quot;1.0&quot;</span> <span class="k">encoding</span><span class="o">=</span><span class="ss">&quot;UTF-8&quot;</span><span class="o">?&gt;</span>
2033<span class="o">&lt;</span><span class="k">language</span> <span class="n">xmlns</span><span class="o">=</span><span class="ss">&quot;http://www.woltlab.com&quot;</span> <span class="n">xmlns</span><span class="p">:</span><span class="n">xsi</span><span class="o">=</span><span class="ss">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="n">xsi</span><span class="p">:</span><span class="n">schemaLocation</span><span class="o">=</span><span class="ss">&quot;http://www.woltlab.com http://www.woltlab.com/XSD/tornado/language.xsd&quot;</span> <span class="n">languagecode</span><span class="o">=</span><span class="ss">&quot;de&quot;</span><span class="o">&gt;</span>
2034 <span class="o">&lt;</span><span class="n">category</span> <span class="n">name</span><span class="o">=</span><span class="ss">&quot;wcf.person&quot;</span><span class="o">&gt;</span>
2035 <span class="o">&lt;</span><span class="n">item</span> <span class="n">name</span><span class="o">=</span><span class="ss">&quot;wcf.person.birthday&quot;</span><span class="o">&gt;&lt;!</span><span class="p">[</span><span class="n">CDATA</span><span class="p">[</span><span class="n">Geburtstag</span><span class="p">]]</span><span class="o">&gt;&lt;/</span><span class="n">item</span><span class="o">&gt;</span>
2036 <span class="o">&lt;/</span><span class="n">category</span><span class="o">&gt;</span>
2037<span class="o">&lt;/</span><span class="k">language</span><span class="o">&gt;</span>
2038</code></pre></div>
0c5338dd
TD
2039<div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="n">xml</span> <span class="k">version</span><span class="o">=</span><span class="ss">&quot;1.0&quot;</span> <span class="k">encoding</span><span class="o">=</span><span class="ss">&quot;UTF-8&quot;</span><span class="o">?&gt;</span>
2040<span class="o">&lt;</span><span class="k">language</span> <span class="n">xmlns</span><span class="o">=</span><span class="ss">&quot;http://www.woltlab.com&quot;</span> <span class="n">xmlns</span><span class="p">:</span><span class="n">xsi</span><span class="o">=</span><span class="ss">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="n">xsi</span><span class="p">:</span><span class="n">schemaLocation</span><span class="o">=</span><span class="ss">&quot;http://www.woltlab.com http://www.woltlab.com/XSD/tornado/language.xsd&quot;</span> <span class="n">languagecode</span><span class="o">=</span><span class="ss">&quot;en&quot;</span><span class="o">&gt;</span>
2041 <span class="o">&lt;</span><span class="n">category</span> <span class="n">name</span><span class="o">=</span><span class="ss">&quot;wcf.person&quot;</span><span class="o">&gt;</span>
2042 <span class="o">&lt;</span><span class="n">item</span> <span class="n">name</span><span class="o">=</span><span class="ss">&quot;wcf.person.birthday&quot;</span><span class="o">&gt;&lt;!</span><span class="p">[</span><span class="n">CDATA</span><span class="p">[</span><span class="n">Birthday</span><span class="p">]]</span><span class="o">&gt;&lt;/</span><span class="n">item</span><span class="o">&gt;</span>
2043 <span class="o">&lt;/</span><span class="n">category</span><span class="o">&gt;</span>
2044<span class="o">&lt;/</span><span class="k">language</span><span class="o">&gt;</span>
2045</code></pre></div>
0c5338dd
TD
2046<p>The template listener needs to be registered using the <a href="../../../package/pip/template-listener/">templateListener package installation plugin</a>.
2047The corresponding complete <code>templateListener.xml</code> file is included <a href="#templatelistenerxml">below</a>.</p>
2048<p>The template code alone is not sufficient because the <code>birthday</code> field is, at the moment, neither read, nor processed, nor saved by any PHP code.
2049This can be be achieved, however, by adding event listeners to <code>PersonAddForm</code> and <code>PersonEditForm</code> which allow us to execute further code at specific location of the program.
2050Before we take a look at the event listener code, we need to identify exactly which additional steps we need to undertake:</p>
2051<ol>
2052<li>If a person is edited and the form has not been submitted, the existing birthday of that person needs to be read.</li>
2053<li>If a person is added or edited and the form has been submitted, the new birthday value needs to be read.</li>
2054<li>If a person is added or edited and the form has been submitted, the new birthday value needs to be validated.</li>
2055<li>If a person is added or edited and the new birthday value has been successfully validated, the new birthday value needs to be saved.</li>
2056<li>If a person is added and the new birthday value has been successfully saved, the internally stored birthday needs to be reset so that the birthday field is empty when the form is shown again.</li>
2057<li>The internally stored birthday value needs to be assigned to the template.</li>
2058</ol>
2059<p>The following event listeners achieves these requirements:</p>
2060<div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2061<span class="k">namespace</span> <span class="nx">wcf\system\event\listener</span><span class="p">;</span>
2062<span class="k">use</span> <span class="nx">wcf\acp\form\PersonAddForm</span><span class="p">;</span>
2063<span class="k">use</span> <span class="nx">wcf\acp\form\PersonEditForm</span><span class="p">;</span>
2064<span class="k">use</span> <span class="nx">wcf\form\IForm</span><span class="p">;</span>
2065<span class="k">use</span> <span class="nx">wcf\page\IPage</span><span class="p">;</span>
2066<span class="k">use</span> <span class="nx">wcf\system\exception\UserInputException</span><span class="p">;</span>
2067<span class="k">use</span> <span class="nx">wcf\system\WCF</span><span class="p">;</span>
2068<span class="k">use</span> <span class="nx">wcf\util\StringUtil</span><span class="p">;</span>
2069
2070<span class="sd">/**</span>
2071<span class="sd"> * Handles setting the birthday when adding and editing people.</span>
2072<span class="sd"> *</span>
2073<span class="sd"> * @author Matthias Schmidt</span>
2074<span class="sd"> * @copyright 2001-2020 WoltLab GmbH</span>
2075<span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2076<span class="sd"> * @package WoltLabSuite\Core\System\Event\Listener</span>
2077<span class="sd"> */</span>
2078<span class="k">class</span> <span class="nc">BirthdayPersonAddFormListener</span> <span class="k">extends</span> <span class="nx">AbstractEventListener</span> <span class="p">{</span>
2079 <span class="sd">/**</span>
2080<span class="sd"> * birthday of the created or edited person</span>
2081<span class="sd"> * @var string</span>
2082<span class="sd"> */</span>
2083 <span class="k">protected</span> <span class="nv">$birthday</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2084
2085 <span class="sd">/**</span>
2086<span class="sd"> * @see IPage::assignVariables()</span>
2087<span class="sd"> */</span>
2088 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onAssignVariables</span><span class="p">()</span> <span class="p">{</span>
2089 <span class="nx">WCF</span><span class="o">::</span><span class="na">getTPL</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">assign</span><span class="p">(</span><span class="s1">&#39;birthday&#39;</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">);</span>
2090 <span class="p">}</span>
2091
2092 <span class="sd">/**</span>
2093<span class="sd"> * @see IPage::readData()</span>
2094<span class="sd"> */</span>
2095 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onReadData</span><span class="p">(</span><span class="nx">PersonEditForm</span> <span class="nv">$form</span><span class="p">)</span> <span class="p">{</span>
2096 <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">))</span> <span class="p">{</span>
2097 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">=</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">person</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">;</span>
2098
2099 <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">===</span> <span class="s1">&#39;0000-00-00&#39;</span><span class="p">)</span> <span class="p">{</span>
2100 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2101 <span class="p">}</span>
2102 <span class="p">}</span>
2103 <span class="p">}</span>
2104
2105 <span class="sd">/**</span>
2106<span class="sd"> * @see IForm::readFormParameters()</span>
2107<span class="sd"> */</span>
2108 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onReadFormParameters</span><span class="p">()</span> <span class="p">{</span>
2109 <span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;birthday&#39;</span><span class="p">]))</span> <span class="p">{</span>
2110 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">=</span> <span class="nx">StringUtil</span><span class="o">::</span><span class="na">trim</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;birthday&#39;</span><span class="p">]);</span>
2111 <span class="p">}</span>
2112 <span class="p">}</span>
2113
2114 <span class="sd">/**</span>
2115<span class="sd"> * @see IForm::save()</span>
2116<span class="sd"> */</span>
2117 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onSave</span><span class="p">(</span><span class="nx">PersonAddForm</span> <span class="nv">$form</span><span class="p">)</span> <span class="p">{</span>
2118 <span class="k">if</span> <span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">)</span> <span class="p">{</span>
2119 <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">additionalFields</span><span class="p">[</span><span class="s1">&#39;birthday&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">;</span>
2120 <span class="p">}</span>
2121 <span class="k">else</span> <span class="p">{</span>
2122 <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">additionalFields</span><span class="p">[</span><span class="s1">&#39;birthday&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="s1">&#39;0000-00-00&#39;</span><span class="p">;</span>
2123 <span class="p">}</span>
2124 <span class="p">}</span>
2125
2126 <span class="sd">/**</span>
2127<span class="sd"> * @see IForm::saved()</span>
2128<span class="sd"> */</span>
2129 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onSaved</span><span class="p">()</span> <span class="p">{</span>
2130 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2131 <span class="p">}</span>
2132
2133 <span class="sd">/**</span>
2134<span class="sd"> * @see IForm::validate()</span>
2135<span class="sd"> */</span>
2136 <span class="k">protected</span> <span class="k">function</span> <span class="nf">onValidate</span><span class="p">()</span> <span class="p">{</span>
2137 <span class="k">if</span> <span class="p">(</span><span class="k">empty</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">))</span> <span class="p">{</span>
2138 <span class="k">return</span><span class="p">;</span>
2139 <span class="p">}</span>
2140
2141 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">preg_match</span><span class="p">(</span><span class="s1">&#39;/^(\d{4})-(\d{2})-(\d{2})$/&#39;</span><span class="p">,</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="p">,</span> <span class="nv">$match</span><span class="p">))</span> <span class="p">{</span>
2142 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;birthday&#39;</span><span class="p">,</span> <span class="s1">&#39;noValidSelection&#39;</span><span class="p">);</span>
2143 <span class="p">}</span>
2144
2145 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nb">checkdate</span><span class="p">(</span><span class="nb">intval</span><span class="p">(</span><span class="nv">$match</span><span class="p">[</span><span class="mi">2</span><span class="p">]),</span> <span class="nb">intval</span><span class="p">(</span><span class="nv">$match</span><span class="p">[</span><span class="mi">3</span><span class="p">]),</span> <span class="nb">intval</span><span class="p">(</span><span class="nv">$match</span><span class="p">[</span><span class="mi">1</span><span class="p">])))</span> <span class="p">{</span>
2146 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;birthday&#39;</span><span class="p">,</span> <span class="s1">&#39;noValidSelection&#39;</span><span class="p">);</span>
2147 <span class="p">}</span>
2148 <span class="p">}</span>
2149<span class="p">}</span>
2150</code></pre></div>
0c5338dd
TD
2151<p>Some notes on the code:</p>
2152<ul>
2153<li>We are inheriting from <code>AbstractEventListener</code>, instead of just implementing the <code>IParameterizedEventListener</code> interface.
2154 The <code>execute()</code> method of <code>AbstractEventListener</code> contains a dispatcher that automatically calls methods called <code>on</code> followed by the event name with the first character uppercased, passing the event object and the <code>$parameters</code> array.
2155 This simple pattern results in the event <code>foo</code> being forwarded to the method <code>onFoo($eventObj, $parameters)</code>.</li>
2156<li>The <code>birthday</code> column has a default value of <code>0000-00-00</code>, which we interpret as “birthday not set”.
2157 To show an empty input field in this case, we empty the <code>birthday</code> property after reading such a value in <code>readData()</code>.</li>
2158<li>The validation of the date is, as mentioned before, very basic and just checks the form of the string and uses PHP’s <a href="https://secure.php.net/manual/en/function.checkdate.php">checkdate</a> function to validate the components.</li>
2159<li>The <code>save</code> needs to make sure that the passed date is actually a valid date and set it to <code>0000-00-00</code> if no birthday is given.
2160 To actually save the birthday in the database, we do not directly manipulate the database but can add an additional field to the data array passed to <code>PersonAction::create()</code> via <code>AbstractForm::$additionalFields</code>.
2161 As the <code>save</code> event is the last event fired before the actual save process happens, this is the perfect event to set this array element.</li>
2162</ul>
2163<p>The event listeners are installed using the <code>eventListener.xml</code> file shown <a href="#eventlistenerxml">below</a>.</p>
2164<h2 id="adding-birthday-table-column-in-acp">Adding Birthday Table Column in ACP<a class="headerlink" href="#adding-birthday-table-column-in-acp" title="Permanent link">#</a></h2>
2165<p>To add a birthday column to the person list page in the ACP, we need three parts:</p>
2166<ol>
2167<li>an event listener that makes the <code>birthday</code> database table column a valid sort field,</li>
2168<li>a template listener that adds the birthday column to the table’s head, and</li>
2169<li>a template listener that adds the birthday column to the table’s rows.</li>
2170</ol>
2171<p>The first part is a very simple class:</p>
2172<div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2173<span class="k">namespace</span> <span class="nx">wcf\system\event\listener</span><span class="p">;</span>
2174<span class="k">use</span> <span class="nx">wcf\page\SortablePage</span><span class="p">;</span>
2175
2176<span class="sd">/**</span>
2177<span class="sd"> * Makes people&#39;s birthday a valid sort field in the ACP and the front end.</span>
2178<span class="sd"> * </span>
2179<span class="sd"> * @author Matthias Schmidt</span>
2180<span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2181<span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2182<span class="sd"> * @package WoltLabSuite\Core\System\Event\Listener</span>
2183<span class="sd"> */</span>
2184<span class="k">class</span> <span class="nc">BirthdaySortFieldPersonListPageListener</span> <span class="k">implements</span> <span class="nx">IParameterizedEventListener</span> <span class="p">{</span>
2185 <span class="sd">/**</span>
2186<span class="sd"> * @inheritDoc</span>
2187<span class="sd"> */</span>
2188 <span class="k">public</span> <span class="k">function</span> <span class="nf">execute</span><span class="p">(</span><span class="nv">$eventObj</span><span class="p">,</span> <span class="nv">$className</span><span class="p">,</span> <span class="nv">$eventName</span><span class="p">,</span> <span class="k">array</span> <span class="o">&amp;</span><span class="nv">$parameters</span><span class="p">)</span> <span class="p">{</span>
2189 <span class="sd">/** @var SortablePage $eventObj */</span>
2190
2191 <span class="nv">$eventObj</span><span class="o">-&gt;</span><span class="na">validSortFields</span><span class="p">[]</span> <span class="o">=</span> <span class="s1">&#39;birthday&#39;</span><span class="p">;</span>
2192 <span class="p">}</span>
2193<span class="p">}</span>
2194</code></pre></div>
0c5338dd
TD
2195<div class="admonition info">
2196<p class="admonition-title">We use <code>SortablePage</code> as a type hint instead of <code>wcf\acp\page\PersonListPage</code> because we will be using the same event listener class in the front end to also allow sorting that list by birthday.</p>
2197</div>
2198<p>As the relevant template codes are only one line each, we will simply put them directly in the <code>templateListener.xml</code> file that will be shown <a href="#templatelistenerxml">later on</a>.
2199The code for the table head is similar to the other <code>th</code> elements:</p>
2200<div class="highlight"><pre><span></span><code><span class="x">&lt;th class=&quot;columnDate columnBirthday</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;birthday&#39;</span><span class="cp">}</span><span class="x"> active </span><span class="cp">{</span><span class="o">@</span><span class="nv">$sortOrder</span><span class="cp">}{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&quot;&gt;&lt;a href=&quot;</span><span class="cp">{</span><span class="nf">link</span> <span class="na">controller</span><span class="o">=</span><span class="s1">&#39;PersonList&#39;</span><span class="cp">}</span><span class="x">pageNo=</span><span class="cp">{</span><span class="o">@</span><span class="nv">$pageNo</span><span class="cp">}</span><span class="x">&amp;sortField=birthday&amp;sortOrder=</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;birthday&#39;</span> <span class="o">&amp;&amp;</span> <span class="nv">$sortOrder</span> <span class="o">==</span> <span class="s1">&#39;ASC&#39;</span><span class="cp">}</span><span class="x">DESC</span><span class="cp">{</span><span class="nf">else</span><span class="cp">}</span><span class="x">ASC</span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.birthday</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/th&gt;</span>
2201</code></pre></div>
0c5338dd
TD
2202<p>For the table body’s column, we need to make sure that the birthday is only show if it is actually set:</p>
2203<div class="highlight"><pre><span></span><code><span class="x">&lt;td class=&quot;columnDate columnBirthday&quot;&gt;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$person</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">!==</span> <span class="s1">&#39;0000-00-00&#39;</span><span class="cp">}{</span><span class="o">@</span><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="o">|</span><span class="na">strtotime</span><span class="o">|</span><span class="na">date</span><span class="cp">}{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&lt;/td&gt;</span>
2204</code></pre></div>
0c5338dd
TD
2205<h2 id="adding-birthday-in-front-end">Adding Birthday in Front End<a class="headerlink" href="#adding-birthday-in-front-end" title="Permanent link">#</a></h2>
2206<p>In the front end, we also want to make the list sortable by birthday and show the birthday as part of each person’s “statistics”.</p>
2207<p>To add the birthday as a valid sort field, we use <code>BirthdaySortFieldPersonListPageListener</code> just as in the ACP.
2208In the front end, we will now use a template (<code>__personListBirthdaySortField.tpl</code>) instead of a directly putting the template code in the <code>templateListener.xml</code> file:</p>
2209<div class="highlight"><pre><span></span><code><span class="x">&lt;option value=&quot;birthday&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;birthday&#39;</span><span class="cp">}</span><span class="x"> selected</span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.birthday</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/option&gt;</span>
2210</code></pre></div>
0c5338dd
TD
2211<div class="admonition info">
2212<p class="admonition-title">You might have noticed the two underscores at the beginning of the template file. For templates that are included via template listeners, this is the naming convention we use.</p>
2213</div>
2214<p>Putting the template code into a file has the advantage that in the administrator is able to edit the code directly via a custom template group, even though in this case this might not be very probable.</p>
2215<p>To show the birthday, we use the following template code for the <code>personStatistics</code> template event, which again makes sure that the birthday is only shown if it is actually set:</p>
2216<div class="highlight"><pre><span></span><code><span class="cp">{</span><span class="nf">if</span> <span class="nv">$person</span><span class="o">-&gt;</span><span class="na">birthday</span> <span class="o">!==</span> <span class="s1">&#39;0000-00-00&#39;</span><span class="cp">}</span><span class="x"></span>
2217<span class="x"> &lt;dt&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.birthday</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/dt&gt;</span>
2218<span class="x"> &lt;dd&gt;</span><span class="cp">{</span><span class="o">@</span><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">birthday</span><span class="o">|</span><span class="na">strtotime</span><span class="o">|</span><span class="na">date</span><span class="cp">}</span><span class="x">&lt;/dd&gt;</span>
2219<span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2220</code></pre></div>
0c5338dd
TD
2221<h2 id="templatelistenerxml"><code>templateListener.xml</code><a class="headerlink" href="#templatelistenerxml" title="Permanent link">#</a></h2>
2222<p>The following code shows the <code>templateListener.xml</code> file used to install all mentioned template listeners:</p>
2223<div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
2224<span class="nt">&lt;data</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.woltlab.com&quot;</span> <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="na">xsi:schemaLocation=</span><span class="s">&quot;http://www.woltlab.com http://www.woltlab.com/tornado/XSD/templateListener.xsd&quot;</span><span class="nt">&gt;</span>
2225 <span class="nt">&lt;import&gt;</span>
2226 <span class="c">&lt;!-- admin --&gt;</span>
2227 <span class="nt">&lt;templatelistener</span> <span class="na">name=</span><span class="s">&quot;personListBirthdayColumnHead&quot;</span><span class="nt">&gt;</span>
2228 <span class="nt">&lt;eventname&gt;</span>columnHeads<span class="nt">&lt;/eventname&gt;</span>
2229 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2230 <span class="nt">&lt;templatecode&gt;</span><span class="cp">&lt;![CDATA[&lt;th class=&quot;columnDate columnBirthday{if $sortField == &#39;birthday&#39;} active {@$sortOrder}{/if}&quot;&gt;&lt;a href=&quot;{link controller=&#39;PersonList&#39;}pageNo={@$pageNo}&amp;sortField=birthday&amp;sortOrder={if $sortField == &#39;birthday&#39; &amp;&amp; $sortOrder == &#39;ASC&#39;}DESC{else}ASC{/if}{/link}&quot;&gt;{lang}wcf.person.birthday{/lang}&lt;/a&gt;&lt;/th&gt;]]&gt;</span><span class="nt">&lt;/templatecode&gt;</span>
2231 <span class="nt">&lt;templatename&gt;</span>personList<span class="nt">&lt;/templatename&gt;</span>
2232 <span class="nt">&lt;/templatelistener&gt;</span>
2233 <span class="nt">&lt;templatelistener</span> <span class="na">name=</span><span class="s">&quot;personListBirthdayColumn&quot;</span><span class="nt">&gt;</span>
2234 <span class="nt">&lt;eventname&gt;</span>columns<span class="nt">&lt;/eventname&gt;</span>
2235 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2236 <span class="nt">&lt;templatecode&gt;</span><span class="cp">&lt;![CDATA[&lt;td class=&quot;columnDate columnBirthday&quot;&gt;{if $person-&gt;birthday !== &#39;0000-00-00&#39;}{@$person-&gt;birthday|strtotime|date}{/if}&lt;/td&gt;]]&gt;</span><span class="nt">&lt;/templatecode&gt;</span>
2237 <span class="nt">&lt;templatename&gt;</span>personList<span class="nt">&lt;/templatename&gt;</span>
2238 <span class="nt">&lt;/templatelistener&gt;</span>
2239 <span class="nt">&lt;templatelistener</span> <span class="na">name=</span><span class="s">&quot;personAddBirthday&quot;</span><span class="nt">&gt;</span>
2240 <span class="nt">&lt;eventname&gt;</span>dataFields<span class="nt">&lt;/eventname&gt;</span>
2241 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2242 <span class="nt">&lt;templatecode&gt;</span><span class="cp">&lt;![CDATA[{include file=&#39;__personAddBirthday&#39;}]]&gt;</span><span class="nt">&lt;/templatecode&gt;</span>
2243 <span class="nt">&lt;templatename&gt;</span>personAdd<span class="nt">&lt;/templatename&gt;</span>
2244 <span class="nt">&lt;/templatelistener&gt;</span>
2245 <span class="c">&lt;!-- /admin --&gt;</span>
2246
2247 <span class="c">&lt;!-- user --&gt;</span>
2248 <span class="nt">&lt;templatelistener</span> <span class="na">name=</span><span class="s">&quot;personListBirthday&quot;</span><span class="nt">&gt;</span>
2249 <span class="nt">&lt;eventname&gt;</span>personStatistics<span class="nt">&lt;/eventname&gt;</span>
2250 <span class="nt">&lt;environment&gt;</span>user<span class="nt">&lt;/environment&gt;</span>
2251 <span class="nt">&lt;templatecode&gt;</span><span class="cp">&lt;![CDATA[{include file=&#39;__personListBirthday&#39;}]]&gt;</span><span class="nt">&lt;/templatecode&gt;</span>
2252 <span class="nt">&lt;templatename&gt;</span>personList<span class="nt">&lt;/templatename&gt;</span>
2253 <span class="nt">&lt;/templatelistener&gt;</span>
2254 <span class="nt">&lt;templatelistener</span> <span class="na">name=</span><span class="s">&quot;personListBirthdaySortField&quot;</span><span class="nt">&gt;</span>
2255 <span class="nt">&lt;eventname&gt;</span>sortField<span class="nt">&lt;/eventname&gt;</span>
2256 <span class="nt">&lt;environment&gt;</span>user<span class="nt">&lt;/environment&gt;</span>
2257 <span class="nt">&lt;templatecode&gt;</span><span class="cp">&lt;![CDATA[{include file=&#39;__personListBirthdaySortField&#39;}]]&gt;</span><span class="nt">&lt;/templatecode&gt;</span>
2258 <span class="nt">&lt;templatename&gt;</span>personList<span class="nt">&lt;/templatename&gt;</span>
2259 <span class="nt">&lt;/templatelistener&gt;</span>
2260 <span class="c">&lt;!-- /user --&gt;</span>
2261 <span class="nt">&lt;/import&gt;</span>
2262<span class="nt">&lt;/data&gt;</span>
2263</code></pre></div>
0c5338dd
TD
2264<p>In cases where a template is used, we simply use the <code>include</code> syntax to load the template.</p>
2265<h2 id="eventlistenerxml"><code>eventListener.xml</code><a class="headerlink" href="#eventlistenerxml" title="Permanent link">#</a></h2>
2266<p>There are two event listeners, <code>birthdaySortFieldAdminPersonList</code> and <code>birthdaySortFieldPersonList</code>, that make <code>birthday</code> a valid sort field in the ACP and the front end, respectively, and the rest takes care of setting the birthday.
2267The event listener <code>birthdayPersonAddFormInherited</code> takes care of the events that are relevant for both adding and editing people, thus it listens to the <code>PersonAddForm</code> class but has <code>inherit</code> set to <code>1</code> so that it also listens to the events of the <code>PersonEditForm</code> class.
2268In contrast, reading the existing birthday from a person is only relevant for editing so that the event listener <code>birthdayPersonEditForm</code> only listens to that class.</p>
2269<div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
2270<span class="nt">&lt;data</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.woltlab.com&quot;</span> <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="na">xsi:schemaLocation=</span><span class="s">&quot;http://www.woltlab.com http://www.woltlab.com/XSD/tornado/eventListener.xsd&quot;</span><span class="nt">&gt;</span>
2271 <span class="nt">&lt;import&gt;</span>
2272 <span class="c">&lt;!-- admin --&gt;</span>
2273 <span class="nt">&lt;eventlistener</span> <span class="na">name=</span><span class="s">&quot;birthdaySortFieldAdminPersonList&quot;</span><span class="nt">&gt;</span>
2274 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2275 <span class="nt">&lt;eventclassname&gt;</span>wcf\acp\page\PersonListPage<span class="nt">&lt;/eventclassname&gt;</span>
2276 <span class="nt">&lt;eventname&gt;</span>validateSortField<span class="nt">&lt;/eventname&gt;</span>
2277 <span class="nt">&lt;listenerclassname&gt;</span>wcf\system\event\listener\BirthdaySortFieldPersonListPageListener<span class="nt">&lt;/listenerclassname&gt;</span>
2278 <span class="nt">&lt;/eventlistener&gt;</span>
2279 <span class="nt">&lt;eventlistener</span> <span class="na">name=</span><span class="s">&quot;birthdayPersonAddForm&quot;</span><span class="nt">&gt;</span>
2280 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2281 <span class="nt">&lt;eventclassname&gt;</span>wcf\acp\form\PersonAddForm<span class="nt">&lt;/eventclassname&gt;</span>
2282 <span class="nt">&lt;eventname&gt;</span>saved<span class="nt">&lt;/eventname&gt;</span>
2283 <span class="nt">&lt;listenerclassname&gt;</span>wcf\system\event\listener\BirthdayPersonAddFormListener<span class="nt">&lt;/listenerclassname&gt;</span>
2284 <span class="nt">&lt;/eventlistener&gt;</span>
2285 <span class="nt">&lt;eventlistener</span> <span class="na">name=</span><span class="s">&quot;birthdayPersonAddFormInherited&quot;</span><span class="nt">&gt;</span>
2286 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2287 <span class="nt">&lt;eventclassname&gt;</span>wcf\acp\form\PersonAddForm<span class="nt">&lt;/eventclassname&gt;</span>
2288 <span class="nt">&lt;eventname&gt;</span>assignVariables,readFormParameters,save,validate<span class="nt">&lt;/eventname&gt;</span>
2289 <span class="nt">&lt;listenerclassname&gt;</span>wcf\system\event\listener\BirthdayPersonAddFormListener<span class="nt">&lt;/listenerclassname&gt;</span>
2290 <span class="nt">&lt;inherit&gt;</span>1<span class="nt">&lt;/inherit&gt;</span>
2291 <span class="nt">&lt;/eventlistener&gt;</span>
2292 <span class="nt">&lt;eventlistener</span> <span class="na">name=</span><span class="s">&quot;birthdayPersonEditForm&quot;</span><span class="nt">&gt;</span>
2293 <span class="nt">&lt;environment&gt;</span>admin<span class="nt">&lt;/environment&gt;</span>
2294 <span class="nt">&lt;eventclassname&gt;</span>wcf\acp\form\PersonEditForm<span class="nt">&lt;/eventclassname&gt;</span>
2295 <span class="nt">&lt;eventname&gt;</span>readData<span class="nt">&lt;/eventname&gt;</span>
2296 <span class="nt">&lt;listenerclassname&gt;</span>wcf\system\event\listener\BirthdayPersonAddFormListener<span class="nt">&lt;/listenerclassname&gt;</span>
2297 <span class="nt">&lt;/eventlistener&gt;</span>
2298 <span class="c">&lt;!-- /admin --&gt;</span>
2299
2300 <span class="c">&lt;!-- user --&gt;</span>
2301 <span class="nt">&lt;eventlistener</span> <span class="na">name=</span><span class="s">&quot;birthdaySortFieldPersonList&quot;</span><span class="nt">&gt;</span>
2302 <span class="nt">&lt;environment&gt;</span>user<span class="nt">&lt;/environment&gt;</span>
2303 <span class="nt">&lt;eventclassname&gt;</span>wcf\page\PersonListPage<span class="nt">&lt;/eventclassname&gt;</span>
2304 <span class="nt">&lt;eventname&gt;</span>validateSortField<span class="nt">&lt;/eventname&gt;</span>
2305 <span class="nt">&lt;listenerclassname&gt;</span>wcf\system\event\listener\BirthdaySortFieldPersonListPageListener<span class="nt">&lt;/listenerclassname&gt;</span>
2306 <span class="nt">&lt;/eventlistener&gt;</span>
2307 <span class="c">&lt;!-- /user --&gt;</span>
2308 <span class="nt">&lt;/import&gt;</span>
2309<span class="nt">&lt;/data&gt;</span>
2310</code></pre></div>
0c5338dd
TD
2311<h2 id="packagexml"><code>package.xml</code><a class="headerlink" href="#packagexml" title="Permanent link">#</a></h2>
2312<p>The only relevant difference between the <code>package.xml</code> file of the base page from part 1 and the <code>package.xml</code> file of this package is that this package requires the base package <code>com.woltlab.wcf.people</code> (see <code>&lt;requiredpackages&gt;</code>):</p>
2313<div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
2314<span class="nt">&lt;package</span> <span class="na">name=</span><span class="s">&quot;com.woltlab.wcf.people.birthday&quot;</span> <span class="na">xmlns=</span><span class="s">&quot;http://www.woltlab.com&quot;</span> <span class="na">xmlns:xsi=</span><span class="s">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span> <span class="na">xsi:schemaLocation=</span><span class="s">&quot;http://www.woltlab.com http://www.woltlab.com/XSD/tornado/package.xsd&quot;</span><span class="nt">&gt;</span>
2315 <span class="nt">&lt;packageinformation&gt;</span>
2316 <span class="nt">&lt;packagename&gt;</span>WoltLab Suite Core Tutorial: People (Birthday)<span class="nt">&lt;/packagename&gt;</span>
2317 <span class="nt">&lt;packagedescription&gt;</span>Adds a birthday field to the people management system as part of a tutorial to create packages.<span class="nt">&lt;/packagedescription&gt;</span>
2318 <span class="nt">&lt;version&gt;</span>3.1.0<span class="nt">&lt;/version&gt;</span>
2319 <span class="nt">&lt;date&gt;</span>2018-03-30<span class="nt">&lt;/date&gt;</span>
2320 <span class="nt">&lt;/packageinformation&gt;</span>
2321
2322 <span class="nt">&lt;authorinformation&gt;</span>
2323 <span class="nt">&lt;author&gt;</span>WoltLab GmbH<span class="nt">&lt;/author&gt;</span>
2324 <span class="nt">&lt;authorurl&gt;</span>http://www.woltlab.com<span class="nt">&lt;/authorurl&gt;</span>
2325 <span class="nt">&lt;/authorinformation&gt;</span>
2326
2327 <span class="nt">&lt;requiredpackages&gt;</span>
2328 <span class="nt">&lt;requiredpackage</span> <span class="na">minversion=</span><span class="s">&quot;3.1.0&quot;</span><span class="nt">&gt;</span>com.woltlab.wcf<span class="nt">&lt;/requiredpackage&gt;</span>
2329 <span class="nt">&lt;requiredpackage</span> <span class="na">minversion=</span><span class="s">&quot;3.1.0&quot;</span><span class="nt">&gt;</span>com.woltlab.wcf.people<span class="nt">&lt;/requiredpackage&gt;</span>
2330 <span class="nt">&lt;/requiredpackages&gt;</span>
2331
2332 <span class="nt">&lt;excludedpackages&gt;</span>
2333 <span class="nt">&lt;excludedpackage</span> <span class="na">version=</span><span class="s">&quot;3.2.0 Alpha 1&quot;</span><span class="nt">&gt;</span>com.woltlab.wcf<span class="nt">&lt;/excludedpackage&gt;</span>
2334 <span class="nt">&lt;/excludedpackages&gt;</span>
2335
2336 <span class="nt">&lt;compatibility&gt;</span>
2337 <span class="nt">&lt;api</span> <span class="na">version=</span><span class="s">&quot;2018&quot;</span> <span class="nt">/&gt;</span>
2338 <span class="nt">&lt;/compatibility&gt;</span>
2339
2340 <span class="nt">&lt;instructions</span> <span class="na">type=</span><span class="s">&quot;install&quot;</span><span class="nt">&gt;</span>
2341 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;acpTemplate&quot;</span> <span class="nt">/&gt;</span>
2342 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;file&quot;</span> <span class="nt">/&gt;</span>
2343 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;sql&quot;</span> <span class="nt">/&gt;</span>
2344 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;template&quot;</span> <span class="nt">/&gt;</span>
2345 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;language&quot;</span> <span class="nt">/&gt;</span>
2346
2347 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;eventListener&quot;</span> <span class="nt">/&gt;</span>
2348 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;templateListener&quot;</span> <span class="nt">/&gt;</span>
2349 <span class="nt">&lt;/instructions&gt;</span>
2350<span class="nt">&lt;/package&gt;</span>
2351</code></pre></div>
0c5338dd
TD
2352<hr />
2353<p>This concludes the second part of our tutorial series after which you now have extended the base package using event listeners and template listeners that allow you to enter the birthday of the people.</p>
2354<p>The complete source code of this part can be found on <a href="https://github.com/WoltLab/woltlab.github.io/tree/master/_includes/tutorial/tutorial-series/part-2">GitHub</a>.</p>
2355
4a5c32e1
WG
2356
2357
2358
2359<hr>
2360<div class="md-source-date">
2361 <small>
2362
2363 Last update: 2021-01-15
2364
2365 </small>
2366</div>
2367
2368
0c5338dd
TD
2369
2370
2371
2372
2373
2374
2375 </article>
2376 </div>
2377 </div>
2378 </main>
2379
2380
2381<footer class="md-footer">
2382
2383 <nav class="md-footer__inner md-grid" aria-label="Footer">
2384
2385 <a href="../part_1/" class="md-footer__link md-footer__link--prev" rel="prev">
2386 <div class="md-footer__button md-icon">
2387 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12z"/></svg>
2388 </div>
2389 <div class="md-footer__title">
2390 <div class="md-ellipsis">
2391 <span class="md-footer__direction">
2392 Previous
2393 </span>
2394 Part 1
2395 </div>
2396 </div>
2397 </a>
2398
2399
2400 <a href="../part_3/" class="md-footer__link md-footer__link--next" rel="next">
2401 <div class="md-footer__title">
2402 <div class="md-ellipsis">
2403 <span class="md-footer__direction">
2404 Next
2405 </span>
2406 Part 3
2407 </div>
2408 </div>
2409 <div class="md-footer__button md-icon">
2410 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4z"/></svg>
2411 </div>
2412 </a>
2413
2414 </nav>
2415
2416 <div class="md-footer-meta md-typeset">
2417 <div class="md-footer-meta__inner md-grid">
2418 <div class="md-footer-copyright">
2419
2420 <div class="md-footer-copyright__highlight">
2421 Copyright © 2020 WoltLab GmbH
2422 </div>
2423
2424 Made with
2425 <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
2426 Material for MkDocs
2427 </a>
fb88dc6e 2428
0c5338dd
TD
2429 </div>
2430 <div class="md-footer-copyright">
2431 <a href="https://www.woltlab.com/legal-notice/">Legal Notice</a>
2432 <a href="https://www.woltlab.com/privacy-policy/">Privacy Policy</a>
2433</div>
2434 </div>
2435 </div>
2436</footer>
2437
2438 </div>
2439 <div class="md-dialog" data-md-component="dialog">
2440 <div class="md-dialog__inner md-typeset"></div>
2441 </div>
2442 <script id="__config" type="application/json">{"base": "../../..", "features": [], "translations": {"clipboard.copy": "Copy to clipboard", "clipboard.copied": "Copied to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.placeholder": "Type to start searching", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.term.missing": "Missing"}, "search": "../../../assets/javascripts/workers/search.fb4a9340.min.js", "version": {"provider": "mike"}}</script>
2443
2444
fb88dc6e 2445 <script src="../../../assets/javascripts/bundle.5cf3e710.min.js"></script>
0c5338dd
TD
2446
2447
2448 </body>
2449</html>