Deployed 901748f to 5.4 with MkDocs 1.1.2 and mike 0.5.5
[GitHub/WoltLab/woltlab.github.io.git] / latest / tutorial / series / part_1 / index.html
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="icon" href="../../../assets/default.favicon.ico">
13 <meta name="generator" content="mkdocs-1.1.2, mkdocs-material-7.1.0">
14
15
16
17 <title>Part 1 - WoltLab Suite Documentation</title>
18
19
20
21 <link rel="stylesheet" href="../../../assets/stylesheets/main.33e2939f.min.css">
22
23
24 <link rel="stylesheet" href="../../../assets/stylesheets/palette.ef6f36e2.min.css">
25
26
27
28 <meta name="theme-color" content="#009485">
29
30
31
32
33
34
35
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 <script>function __prefix(e){return new URL("../../..",location).pathname+"."+e}function __get(e,t=localStorage){return JSON.parse(t.getItem(__prefix(e)))}</script>
56
57 <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
58 <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
59 <label class="md-overlay" for="__drawer"></label>
60 <div data-md-component="skip">
61
62
63 <a href="#tutorial-series-part-1-base-structure" class="md-skip">
64 Skip to content
65 </a>
66
67 </div>
68 <div data-md-component="announce">
69
70 <aside class="md-announce">
71 <div class="md-announce__inner md-grid md-typeset">
72
73 <a href="https://www.woltlab.com">Back to <strong>woltlab.com</strong></a>
74
75 </div>
76 </aside>
77
78 </div>
79
80 <header class="md-header" data-md-component="header">
81 <nav class="md-header__inner md-grid" aria-label="Header">
82 <a href="../../.." title="WoltLab Suite Documentation" class="md-header__button md-logo" aria-label="WoltLab Suite Documentation" data-md-component="logo">
83
84 <img src="../../../assets/logo.png" alt="logo">
85
86 </a>
87 <label class="md-header__button md-icon" for="__drawer">
88 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/></svg>
89 </label>
90 <div class="md-header__title" data-md-component="header-title">
91 <div class="md-header__ellipsis">
92 <div class="md-header__topic">
93 <span class="md-ellipsis">
94 WoltLab Suite Documentation
95 </span>
96 </div>
97 <div class="md-header__topic" data-md-component="header-topic">
98 <span class="md-ellipsis">
99
100 Part 1
101
102 </span>
103 </div>
104 </div>
105 </div>
106
107
108
109 <label class="md-header__button md-icon" for="__search">
110 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
111 </label>
112
113 <div class="md-search" data-md-component="search" role="dialog">
114 <label class="md-search__overlay" for="__search"></label>
115 <div class="md-search__inner" role="search">
116 <form class="md-search__form" name="search">
117 <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" data-md-state="active" required>
118 <label class="md-search__icon md-icon" for="__search">
119 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5z"/></svg>
120 <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>
121 </label>
122 <button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1">
123 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/></svg>
124 </button>
125 </form>
126 <div class="md-search__output">
127 <div class="md-search__scrollwrap" data-md-scrollfix>
128 <div class="md-search-result" data-md-component="search-result">
129 <div class="md-search-result__meta">
130 Initializing search
131 </div>
132 <ol class="md-search-result__list"></ol>
133 </div>
134 </div>
135 </div>
136 </div>
137 </div>
138
139
140 <div class="md-header__source">
141
142 <a href="https://github.com/WoltLab/docs.woltlab.com/" title="Go to repository" class="md-source" data-md-component="source">
143 <div class="md-source__icon md-icon">
144
145 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05 244 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>
146 </div>
147 <div class="md-source__repository">
148 GitHub
149 </div>
150 </a>
151 </div>
152
153 </nav>
154 </header>
155
156 <div class="md-container" data-md-component="container">
157
158
159
160
161 <main class="md-main" data-md-component="main">
162 <div class="md-main__inner md-grid">
163
164
165
166 <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
167 <div class="md-sidebar__scrollwrap">
168 <div class="md-sidebar__inner">
169
170
171
172 <nav class="md-nav md-nav--primary" aria-label="Navigation" data-md-level="0">
173 <label class="md-nav__title" for="__drawer">
174 <a href="../../.." title="WoltLab Suite Documentation" class="md-nav__button md-logo" aria-label="WoltLab Suite Documentation" data-md-component="logo">
175
176 <img src="../../../assets/logo.png" alt="logo">
177
178 </a>
179 WoltLab Suite Documentation
180 </label>
181
182 <div class="md-nav__source">
183
184 <a href="https://github.com/WoltLab/docs.woltlab.com/" title="Go to repository" class="md-source" data-md-component="source">
185 <div class="md-source__icon md-icon">
186
187 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M439.55 236.05 244 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>
188 </div>
189 <div class="md-source__repository">
190 GitHub
191 </div>
192 </a>
193 </div>
194
195 <ul class="md-nav__list" data-md-scrollfix>
196
197
198
199
200
201
202
203
204 <li class="md-nav__item">
205 <a href="../../../getting-started/" class="md-nav__link">
206 Getting Started
207 </a>
208 </li>
209
210
211
212
213
214
215
216
217
218
219
220 <li class="md-nav__item md-nav__item--nested">
221
222
223 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" >
224
225 <label class="md-nav__link" for="__nav_2">
226 PHP API
227 <span class="md-nav__icon md-icon"></span>
228 </label>
229 <nav class="md-nav" aria-label="PHP API" data-md-level="1">
230 <label class="md-nav__title" for="__nav_2">
231 <span class="md-nav__icon md-icon"></span>
232 PHP API
233 </label>
234 <ul class="md-nav__list" data-md-scrollfix>
235
236
237
238
239
240 <li class="md-nav__item">
241 <a href="../../../php/pages/" class="md-nav__link">
242 Pages
243 </a>
244 </li>
245
246
247
248
249
250
251
252 <li class="md-nav__item">
253 <a href="../../../php/database-objects/" class="md-nav__link">
254 Database Objects
255 </a>
256 </li>
257
258
259
260
261
262
263
264 <li class="md-nav__item">
265 <a href="../../../php/database-access/" class="md-nav__link">
266 Database Access
267 </a>
268 </li>
269
270
271
272
273
274
275
276 <li class="md-nav__item">
277 <a href="../../../php/exceptions/" class="md-nav__link">
278 Exceptions
279 </a>
280 </li>
281
282
283
284
285
286
287
288
289 <li class="md-nav__item md-nav__item--nested">
290
291
292 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2_5" type="checkbox" id="__nav_2_5" >
293
294 <label class="md-nav__link" for="__nav_2_5">
295 API
296 <span class="md-nav__icon md-icon"></span>
297 </label>
298 <nav class="md-nav" aria-label="API" data-md-level="2">
299 <label class="md-nav__title" for="__nav_2_5">
300 <span class="md-nav__icon md-icon"></span>
301 API
302 </label>
303 <ul class="md-nav__list" data-md-scrollfix>
304
305
306
307
308
309 <li class="md-nav__item">
310 <a href="../../../php/api/caches/" class="md-nav__link">
311 Caches
312 </a>
313 </li>
314
315
316
317
318
319
320
321 <li class="md-nav__item">
322 <a href="../../../php/api/comments/" class="md-nav__link">
323 Comments
324 </a>
325 </li>
326
327
328
329
330
331
332
333 <li class="md-nav__item">
334 <a href="../../../php/api/cronjobs/" class="md-nav__link">
335 Cronjobs
336 </a>
337 </li>
338
339
340
341
342
343
344
345 <li class="md-nav__item">
346 <a href="../../../php/api/events/" class="md-nav__link">
347 Events
348 </a>
349 </li>
350
351
352
353
354
355
356
357
358 <li class="md-nav__item md-nav__item--nested">
359
360
361 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2_5_5" type="checkbox" id="__nav_2_5_5" >
362
363 <label class="md-nav__link" for="__nav_2_5_5">
364 Form Builder
365 <span class="md-nav__icon md-icon"></span>
366 </label>
367 <nav class="md-nav" aria-label="Form Builder" data-md-level="3">
368 <label class="md-nav__title" for="__nav_2_5_5">
369 <span class="md-nav__icon md-icon"></span>
370 Form Builder
371 </label>
372 <ul class="md-nav__list" data-md-scrollfix>
373
374
375
376
377
378 <li class="md-nav__item">
379 <a href="../../../php/api/form_builder/overview/" class="md-nav__link">
380 Overview
381 </a>
382 </li>
383
384
385
386
387
388
389
390 <li class="md-nav__item">
391 <a href="../../../php/api/form_builder/structure/" class="md-nav__link">
392 Structure
393 </a>
394 </li>
395
396
397
398
399
400
401
402 <li class="md-nav__item">
403 <a href="../../../php/api/form_builder/form_fields/" class="md-nav__link">
404 Fields
405 </a>
406 </li>
407
408
409
410
411
412
413
414 <li class="md-nav__item">
415 <a href="../../../php/api/form_builder/validation_data/" class="md-nav__link">
416 Validation and Data
417 </a>
418 </li>
419
420
421
422
423
424
425
426 <li class="md-nav__item">
427 <a href="../../../php/api/form_builder/dependencies/" class="md-nav__link">
428 Dependencies
429 </a>
430 </li>
431
432
433
434 </ul>
435 </nav>
436 </li>
437
438
439
440
441
442
443
444 <li class="md-nav__item">
445 <a href="../../../php/api/package_installation_plugins/" class="md-nav__link">
446 Package Installation Plugins
447 </a>
448 </li>
449
450
451
452
453
454
455
456 <li class="md-nav__item">
457 <a href="../../../php/api/user_activity_points/" class="md-nav__link">
458 User Activity Points
459 </a>
460 </li>
461
462
463
464
465
466
467
468 <li class="md-nav__item">
469 <a href="../../../php/api/user_notifications/" class="md-nav__link">
470 User Notifications
471 </a>
472 </li>
473
474
475
476
477
478
479
480 <li class="md-nav__item">
481 <a href="../../../php/api/sitemaps/" class="md-nav__link">
482 Sitemaps
483 </a>
484 </li>
485
486
487
488 </ul>
489 </nav>
490 </li>
491
492
493
494
495
496
497
498 <li class="md-nav__item">
499 <a href="../../../php/code-style/" class="md-nav__link">
500 Code Style
501 </a>
502 </li>
503
504
505
506
507
508
509
510 <li class="md-nav__item">
511 <a href="../../../php/apps/" class="md-nav__link">
512 Apps
513 </a>
514 </li>
515
516
517
518
519
520
521
522 <li class="md-nav__item">
523 <a href="../../../php/gdpr/" class="md-nav__link">
524 GDPR
525 </a>
526 </li>
527
528
529
530 </ul>
531 </nav>
532 </li>
533
534
535
536
537
538
539
540
541
542
543
544 <li class="md-nav__item md-nav__item--nested">
545
546
547 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_3" type="checkbox" id="__nav_3" >
548
549 <label class="md-nav__link" for="__nav_3">
550 Languages, Templates & CSS
551 <span class="md-nav__icon md-icon"></span>
552 </label>
553 <nav class="md-nav" aria-label="Languages, Templates & CSS" data-md-level="1">
554 <label class="md-nav__title" for="__nav_3">
555 <span class="md-nav__icon md-icon"></span>
556 Languages, Templates & CSS
557 </label>
558 <ul class="md-nav__list" data-md-scrollfix>
559
560
561
562
563
564 <li class="md-nav__item">
565 <a href="../../../view/languages/" class="md-nav__link">
566 Languages
567 </a>
568 </li>
569
570
571
572
573
574
575
576 <li class="md-nav__item">
577 <a href="../../../view/templates/" class="md-nav__link">
578 Templates
579 </a>
580 </li>
581
582
583
584
585
586
587
588 <li class="md-nav__item">
589 <a href="../../../view/css/" class="md-nav__link">
590 CSS
591 </a>
592 </li>
593
594
595
596 </ul>
597 </nav>
598 </li>
599
600
601
602
603
604
605
606
607
608
609
610 <li class="md-nav__item md-nav__item--nested">
611
612
613 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_4" type="checkbox" id="__nav_4" >
614
615 <label class="md-nav__link" for="__nav_4">
616 TypeScript and JavaScript API
617 <span class="md-nav__icon md-icon"></span>
618 </label>
619 <nav class="md-nav" aria-label="TypeScript and JavaScript API" data-md-level="1">
620 <label class="md-nav__title" for="__nav_4">
621 <span class="md-nav__icon md-icon"></span>
622 TypeScript and JavaScript API
623 </label>
624 <ul class="md-nav__list" data-md-scrollfix>
625
626
627
628
629
630 <li class="md-nav__item">
631 <a href="../../../javascript/general-usage/" class="md-nav__link">
632 General Usage
633 </a>
634 </li>
635
636
637
638
639
640
641
642 <li class="md-nav__item">
643 <a href="../../../javascript/typescript/" class="md-nav__link">
644 TypeScript
645 </a>
646 </li>
647
648
649
650
651
652
653
654
655 <li class="md-nav__item md-nav__item--nested">
656
657
658 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_4_3" type="checkbox" id="__nav_4_3" >
659
660 <label class="md-nav__link" for="__nav_4_3">
661 New API
662 <span class="md-nav__icon md-icon"></span>
663 </label>
664 <nav class="md-nav" aria-label="New API" data-md-level="2">
665 <label class="md-nav__title" for="__nav_4_3">
666 <span class="md-nav__icon md-icon"></span>
667 New API
668 </label>
669 <ul class="md-nav__list" data-md-scrollfix>
670
671
672
673
674
675 <li class="md-nav__item">
676 <a href="../../../javascript/new-api_writing-a-module/" class="md-nav__link">
677 Writing a module
678 </a>
679 </li>
680
681
682
683
684
685
686
687 <li class="md-nav__item">
688 <a href="../../../javascript/new-api_data-structures/" class="md-nav__link">
689 Data Structures
690 </a>
691 </li>
692
693
694
695
696
697
698
699 <li class="md-nav__item">
700 <a href="../../../javascript/new-api_core/" class="md-nav__link">
701 Core Functions
702 </a>
703 </li>
704
705
706
707
708
709
710
711 <li class="md-nav__item">
712 <a href="../../../javascript/new-api_dom/" class="md-nav__link">
713 DOM
714 </a>
715 </li>
716
717
718
719
720
721
722
723 <li class="md-nav__item">
724 <a href="../../../javascript/new-api_events/" class="md-nav__link">
725 Event Handling
726 </a>
727 </li>
728
729
730
731
732
733
734
735 <li class="md-nav__item">
736 <a href="../../../javascript/new-api_ajax/" class="md-nav__link">
737 Ajax
738 </a>
739 </li>
740
741
742
743
744
745
746
747 <li class="md-nav__item">
748 <a href="../../../javascript/new-api_dialogs/" class="md-nav__link">
749 Dialogs
750 </a>
751 </li>
752
753
754
755
756
757
758
759 <li class="md-nav__item">
760 <a href="../../../javascript/new-api_browser/" class="md-nav__link">
761 Browser and Screen Sizes
762 </a>
763 </li>
764
765
766
767
768
769
770
771 <li class="md-nav__item">
772 <a href="../../../javascript/new-api_ui/" class="md-nav__link">
773 User Interface
774 </a>
775 </li>
776
777
778
779 </ul>
780 </nav>
781 </li>
782
783
784
785
786
787
788
789 <li class="md-nav__item">
790 <a href="../../../javascript/legacy-api/" class="md-nav__link">
791 Legacy API
792 </a>
793 </li>
794
795
796
797
798
799
800
801 <li class="md-nav__item">
802 <a href="../../../javascript/helper-functions/" class="md-nav__link">
803 Helper Functions
804 </a>
805 </li>
806
807
808
809
810
811
812
813 <li class="md-nav__item">
814 <a href="../../../javascript/code-snippets/" class="md-nav__link">
815 Code Snippets
816 </a>
817 </li>
818
819
820
821 </ul>
822 </nav>
823 </li>
824
825
826
827
828
829
830
831
832
833
834
835 <li class="md-nav__item md-nav__item--nested">
836
837
838 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_5" type="checkbox" id="__nav_5" >
839
840 <label class="md-nav__link" for="__nav_5">
841 Package Components
842 <span class="md-nav__icon md-icon"></span>
843 </label>
844 <nav class="md-nav" aria-label="Package Components" data-md-level="1">
845 <label class="md-nav__title" for="__nav_5">
846 <span class="md-nav__icon md-icon"></span>
847 Package Components
848 </label>
849 <ul class="md-nav__list" data-md-scrollfix>
850
851
852
853
854
855 <li class="md-nav__item">
856 <a href="../../../package/package-xml/" class="md-nav__link">
857 package.xml
858 </a>
859 </li>
860
861
862
863
864
865
866
867
868 <li class="md-nav__item md-nav__item--nested">
869
870
871 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_5_2" type="checkbox" id="__nav_5_2" >
872
873 <label class="md-nav__link" for="__nav_5_2">
874 PIPs
875 <span class="md-nav__icon md-icon"></span>
876 </label>
877 <nav class="md-nav" aria-label="PIPs" data-md-level="2">
878 <label class="md-nav__title" for="__nav_5_2">
879 <span class="md-nav__icon md-icon"></span>
880 PIPs
881 </label>
882 <ul class="md-nav__list" data-md-scrollfix>
883
884
885
886
887
888 <li class="md-nav__item">
889 <a href="../../../package/pip/" class="md-nav__link">
890 Overview
891 </a>
892 </li>
893
894
895
896
897
898
899
900 <li class="md-nav__item">
901 <a href="../../../package/pip/acl-option/" class="md-nav__link">
902 aclOption
903 </a>
904 </li>
905
906
907
908
909
910
911
912 <li class="md-nav__item">
913 <a href="../../../package/pip/acp-menu/" class="md-nav__link">
914 acpMenu
915 </a>
916 </li>
917
918
919
920
921
922
923
924 <li class="md-nav__item">
925 <a href="../../../package/pip/acp-search-provider/" class="md-nav__link">
926 acpSearchProvider
927 </a>
928 </li>
929
930
931
932
933
934
935
936 <li class="md-nav__item">
937 <a href="../../../package/pip/acp-template/" class="md-nav__link">
938 acpTemplate
939 </a>
940 </li>
941
942
943
944
945
946
947
948 <li class="md-nav__item">
949 <a href="../../../package/pip/bbcode/" class="md-nav__link">
950 bbcode
951 </a>
952 </li>
953
954
955
956
957
958
959
960 <li class="md-nav__item">
961 <a href="../../../package/pip/box/" class="md-nav__link">
962 box
963 </a>
964 </li>
965
966
967
968
969
970
971
972 <li class="md-nav__item">
973 <a href="../../../package/pip/clipboard-action/" class="md-nav__link">
974 clipboardAction
975 </a>
976 </li>
977
978
979
980
981
982
983
984 <li class="md-nav__item">
985 <a href="../../../package/pip/core-object/" class="md-nav__link">
986 coreObject
987 </a>
988 </li>
989
990
991
992
993
994
995
996 <li class="md-nav__item">
997 <a href="../../../package/pip/cronjob/" class="md-nav__link">
998 cronjob
999 </a>
1000 </li>
1001
1002
1003
1004
1005
1006
1007
1008 <li class="md-nav__item">
1009 <a href="../../../package/pip/event-listener/" class="md-nav__link">
1010 eventListener
1011 </a>
1012 </li>
1013
1014
1015
1016
1017
1018
1019
1020 <li class="md-nav__item">
1021 <a href="../../../package/pip/file/" class="md-nav__link">
1022 file
1023 </a>
1024 </li>
1025
1026
1027
1028
1029
1030
1031
1032 <li class="md-nav__item">
1033 <a href="../../../package/pip/language/" class="md-nav__link">
1034 language
1035 </a>
1036 </li>
1037
1038
1039
1040
1041
1042
1043
1044 <li class="md-nav__item">
1045 <a href="../../../package/pip/media-provider/" class="md-nav__link">
1046 mediaProvider
1047 </a>
1048 </li>
1049
1050
1051
1052
1053
1054
1055
1056 <li class="md-nav__item">
1057 <a href="../../../package/pip/menu/" class="md-nav__link">
1058 menu
1059 </a>
1060 </li>
1061
1062
1063
1064
1065
1066
1067
1068 <li class="md-nav__item">
1069 <a href="../../../package/pip/menu-item/" class="md-nav__link">
1070 menuItem
1071 </a>
1072 </li>
1073
1074
1075
1076
1077
1078
1079
1080 <li class="md-nav__item">
1081 <a href="../../../package/pip/object-type/" class="md-nav__link">
1082 objectType
1083 </a>
1084 </li>
1085
1086
1087
1088
1089
1090
1091
1092 <li class="md-nav__item">
1093 <a href="../../../package/pip/object-type-definition/" class="md-nav__link">
1094 objectTypeDefinition
1095 </a>
1096 </li>
1097
1098
1099
1100
1101
1102
1103
1104 <li class="md-nav__item">
1105 <a href="../../../package/pip/option/" class="md-nav__link">
1106 option
1107 </a>
1108 </li>
1109
1110
1111
1112
1113
1114
1115
1116 <li class="md-nav__item">
1117 <a href="../../../package/pip/page/" class="md-nav__link">
1118 page
1119 </a>
1120 </li>
1121
1122
1123
1124
1125
1126
1127
1128 <li class="md-nav__item">
1129 <a href="../../../package/pip/pip/" class="md-nav__link">
1130 pip
1131 </a>
1132 </li>
1133
1134
1135
1136
1137
1138
1139
1140 <li class="md-nav__item">
1141 <a href="../../../package/pip/script/" class="md-nav__link">
1142 script
1143 </a>
1144 </li>
1145
1146
1147
1148
1149
1150
1151
1152 <li class="md-nav__item">
1153 <a href="../../../package/pip/smiley/" class="md-nav__link">
1154 smiley
1155 </a>
1156 </li>
1157
1158
1159
1160
1161
1162
1163
1164 <li class="md-nav__item">
1165 <a href="../../../package/pip/sql/" class="md-nav__link">
1166 sql
1167 </a>
1168 </li>
1169
1170
1171
1172
1173
1174
1175
1176 <li class="md-nav__item">
1177 <a href="../../../package/pip/style/" class="md-nav__link">
1178 style
1179 </a>
1180 </li>
1181
1182
1183
1184
1185
1186
1187
1188 <li class="md-nav__item">
1189 <a href="../../../package/pip/template/" class="md-nav__link">
1190 template
1191 </a>
1192 </li>
1193
1194
1195
1196
1197
1198
1199
1200 <li class="md-nav__item">
1201 <a href="../../../package/pip/template-listener/" class="md-nav__link">
1202 templateListener
1203 </a>
1204 </li>
1205
1206
1207
1208
1209
1210
1211
1212 <li class="md-nav__item">
1213 <a href="../../../package/pip/user-group-option/" class="md-nav__link">
1214 userGroupOption
1215 </a>
1216 </li>
1217
1218
1219
1220
1221
1222
1223
1224 <li class="md-nav__item">
1225 <a href="../../../package/pip/user-menu/" class="md-nav__link">
1226 userMenu
1227 </a>
1228 </li>
1229
1230
1231
1232
1233
1234
1235
1236 <li class="md-nav__item">
1237 <a href="../../../package/pip/user-notification-event/" class="md-nav__link">
1238 userNotificationEvent
1239 </a>
1240 </li>
1241
1242
1243
1244
1245
1246
1247
1248 <li class="md-nav__item">
1249 <a href="../../../package/pip/user-option/" class="md-nav__link">
1250 userOption
1251 </a>
1252 </li>
1253
1254
1255
1256
1257
1258
1259
1260 <li class="md-nav__item">
1261 <a href="../../../package/pip/user-profile-menu/" class="md-nav__link">
1262 userProfileMenu
1263 </a>
1264 </li>
1265
1266
1267
1268 </ul>
1269 </nav>
1270 </li>
1271
1272
1273
1274
1275
1276
1277
1278 <li class="md-nav__item">
1279 <a href="../../../package/database-php-api/" class="md-nav__link">
1280 Database PHP API
1281 </a>
1282 </li>
1283
1284
1285
1286 </ul>
1287 </nav>
1288 </li>
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300 <li class="md-nav__item md-nav__item--nested">
1301
1302
1303 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6" type="checkbox" id="__nav_6" >
1304
1305 <label class="md-nav__link" for="__nav_6">
1306 Migration
1307 <span class="md-nav__icon md-icon"></span>
1308 </label>
1309 <nav class="md-nav" aria-label="Migration" data-md-level="1">
1310 <label class="md-nav__title" for="__nav_6">
1311 <span class="md-nav__icon md-icon"></span>
1312 Migration
1313 </label>
1314 <ul class="md-nav__list" data-md-scrollfix>
1315
1316
1317
1318
1319
1320
1321 <li class="md-nav__item md-nav__item--nested">
1322
1323
1324 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_1" type="checkbox" id="__nav_6_1" >
1325
1326 <label class="md-nav__link" for="__nav_6_1">
1327 Migrating from WSC 5.3
1328 <span class="md-nav__icon md-icon"></span>
1329 </label>
1330 <nav class="md-nav" aria-label="Migrating from WSC 5.3" data-md-level="2">
1331 <label class="md-nav__title" for="__nav_6_1">
1332 <span class="md-nav__icon md-icon"></span>
1333 Migrating from WSC 5.3
1334 </label>
1335 <ul class="md-nav__list" data-md-scrollfix>
1336
1337
1338
1339
1340
1341 <li class="md-nav__item">
1342 <a href="../../../migration/wsc53/php/" class="md-nav__link">
1343 PHP API
1344 </a>
1345 </li>
1346
1347
1348
1349
1350
1351
1352
1353 <li class="md-nav__item">
1354 <a href="../../../migration/wsc53/session/" class="md-nav__link">
1355 Session Handling and Authentication
1356 </a>
1357 </li>
1358
1359
1360
1361
1362
1363
1364
1365 <li class="md-nav__item">
1366 <a href="../../../migration/wsc53/javascript/" class="md-nav__link">
1367 TypeScript and JavaScript
1368 </a>
1369 </li>
1370
1371
1372
1373
1374
1375
1376
1377 <li class="md-nav__item">
1378 <a href="../../../migration/wsc53/templates/" class="md-nav__link">
1379 Templates
1380 </a>
1381 </li>
1382
1383
1384
1385
1386
1387
1388
1389 <li class="md-nav__item">
1390 <a href="../../../migration/wsc53/libraries/" class="md-nav__link">
1391 Third Party Libraries
1392 </a>
1393 </li>
1394
1395
1396
1397 </ul>
1398 </nav>
1399 </li>
1400
1401
1402
1403
1404
1405
1406
1407
1408 <li class="md-nav__item md-nav__item--nested">
1409
1410
1411 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_2" type="checkbox" id="__nav_6_2" >
1412
1413 <label class="md-nav__link" for="__nav_6_2">
1414 Migrating from WSC 5.2
1415 <span class="md-nav__icon md-icon"></span>
1416 </label>
1417 <nav class="md-nav" aria-label="Migrating from WSC 5.2" data-md-level="2">
1418 <label class="md-nav__title" for="__nav_6_2">
1419 <span class="md-nav__icon md-icon"></span>
1420 Migrating from WSC 5.2
1421 </label>
1422 <ul class="md-nav__list" data-md-scrollfix>
1423
1424
1425
1426
1427
1428 <li class="md-nav__item">
1429 <a href="../../../migration/wsc52/php/" class="md-nav__link">
1430 PHP API
1431 </a>
1432 </li>
1433
1434
1435
1436
1437
1438
1439
1440 <li class="md-nav__item">
1441 <a href="../../../migration/wsc52/templates/" class="md-nav__link">
1442 Templates and Languages
1443 </a>
1444 </li>
1445
1446
1447
1448
1449
1450
1451
1452 <li class="md-nav__item">
1453 <a href="../../../migration/wsc52/libraries/" class="md-nav__link">
1454 Third Party Libraries
1455 </a>
1456 </li>
1457
1458
1459
1460 </ul>
1461 </nav>
1462 </li>
1463
1464
1465
1466
1467
1468
1469
1470
1471 <li class="md-nav__item md-nav__item--nested">
1472
1473
1474 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_3" type="checkbox" id="__nav_6_3" >
1475
1476 <label class="md-nav__link" for="__nav_6_3">
1477 Migrating from WSC 3.1
1478 <span class="md-nav__icon md-icon"></span>
1479 </label>
1480 <nav class="md-nav" aria-label="Migrating from WSC 3.1" data-md-level="2">
1481 <label class="md-nav__title" for="__nav_6_3">
1482 <span class="md-nav__icon md-icon"></span>
1483 Migrating from WSC 3.1
1484 </label>
1485 <ul class="md-nav__list" data-md-scrollfix>
1486
1487
1488
1489
1490
1491 <li class="md-nav__item">
1492 <a href="../../../migration/wsc31/php/" class="md-nav__link">
1493 PHP API
1494 </a>
1495 </li>
1496
1497
1498
1499 </ul>
1500 </nav>
1501 </li>
1502
1503
1504
1505
1506
1507
1508
1509
1510 <li class="md-nav__item md-nav__item--nested">
1511
1512
1513 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_4" type="checkbox" id="__nav_6_4" >
1514
1515 <label class="md-nav__link" for="__nav_6_4">
1516 Migrating from WSC 3.0
1517 <span class="md-nav__icon md-icon"></span>
1518 </label>
1519 <nav class="md-nav" aria-label="Migrating from WSC 3.0" data-md-level="2">
1520 <label class="md-nav__title" for="__nav_6_4">
1521 <span class="md-nav__icon md-icon"></span>
1522 Migrating from WSC 3.0
1523 </label>
1524 <ul class="md-nav__list" data-md-scrollfix>
1525
1526
1527
1528
1529
1530 <li class="md-nav__item">
1531 <a href="../../../migration/wsc30/php/" class="md-nav__link">
1532 PHP API
1533 </a>
1534 </li>
1535
1536
1537
1538
1539
1540
1541
1542 <li class="md-nav__item">
1543 <a href="../../../migration/wsc30/javascript/" class="md-nav__link">
1544 JavaScript API
1545 </a>
1546 </li>
1547
1548
1549
1550
1551
1552
1553
1554 <li class="md-nav__item">
1555 <a href="../../../migration/wsc30/templates/" class="md-nav__link">
1556 Templates
1557 </a>
1558 </li>
1559
1560
1561
1562
1563
1564
1565
1566 <li class="md-nav__item">
1567 <a href="../../../migration/wsc30/css/" class="md-nav__link">
1568 CSS
1569 </a>
1570 </li>
1571
1572
1573
1574
1575
1576
1577
1578 <li class="md-nav__item">
1579 <a href="../../../migration/wsc30/package/" class="md-nav__link">
1580 Package Components
1581 </a>
1582 </li>
1583
1584
1585
1586 </ul>
1587 </nav>
1588 </li>
1589
1590
1591
1592
1593
1594
1595
1596
1597 <li class="md-nav__item md-nav__item--nested">
1598
1599
1600 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_6_5" type="checkbox" id="__nav_6_5" >
1601
1602 <label class="md-nav__link" for="__nav_6_5">
1603 Migrating from WCF 2.1
1604 <span class="md-nav__icon md-icon"></span>
1605 </label>
1606 <nav class="md-nav" aria-label="Migrating from WCF 2.1" data-md-level="2">
1607 <label class="md-nav__title" for="__nav_6_5">
1608 <span class="md-nav__icon md-icon"></span>
1609 Migrating from WCF 2.1
1610 </label>
1611 <ul class="md-nav__list" data-md-scrollfix>
1612
1613
1614
1615
1616
1617 <li class="md-nav__item">
1618 <a href="../../../migration/wcf21/php/" class="md-nav__link">
1619 PHP API
1620 </a>
1621 </li>
1622
1623
1624
1625
1626
1627
1628
1629 <li class="md-nav__item">
1630 <a href="../../../migration/wcf21/templates/" class="md-nav__link">
1631 Templates
1632 </a>
1633 </li>
1634
1635
1636
1637
1638
1639
1640
1641 <li class="md-nav__item">
1642 <a href="../../../migration/wcf21/css/" class="md-nav__link">
1643 CSS
1644 </a>
1645 </li>
1646
1647
1648
1649
1650
1651
1652
1653 <li class="md-nav__item">
1654 <a href="../../../migration/wcf21/package/" class="md-nav__link">
1655 Package Components
1656 </a>
1657 </li>
1658
1659
1660
1661 </ul>
1662 </nav>
1663 </li>
1664
1665
1666
1667 </ul>
1668 </nav>
1669 </li>
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683 <li class="md-nav__item md-nav__item--active md-nav__item--nested">
1684
1685
1686 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_7" type="checkbox" id="__nav_7" checked>
1687
1688 <label class="md-nav__link" for="__nav_7">
1689 Tutorials
1690 <span class="md-nav__icon md-icon"></span>
1691 </label>
1692 <nav class="md-nav" aria-label="Tutorials" data-md-level="1">
1693 <label class="md-nav__title" for="__nav_7">
1694 <span class="md-nav__icon md-icon"></span>
1695 Tutorials
1696 </label>
1697 <ul class="md-nav__list" data-md-scrollfix>
1698
1699
1700
1701
1702
1703
1704
1705
1706 <li class="md-nav__item md-nav__item--active md-nav__item--nested">
1707
1708
1709 <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_7_1" type="checkbox" id="__nav_7_1" checked>
1710
1711 <label class="md-nav__link" for="__nav_7_1">
1712 Tutorial Series
1713 <span class="md-nav__icon md-icon"></span>
1714 </label>
1715 <nav class="md-nav" aria-label="Tutorial Series" data-md-level="2">
1716 <label class="md-nav__title" for="__nav_7_1">
1717 <span class="md-nav__icon md-icon"></span>
1718 Tutorial Series
1719 </label>
1720 <ul class="md-nav__list" data-md-scrollfix>
1721
1722
1723
1724
1725
1726 <li class="md-nav__item">
1727 <a href="../overview/" class="md-nav__link">
1728 Overview
1729 </a>
1730 </li>
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740 <li class="md-nav__item md-nav__item--active">
1741
1742 <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc">
1743
1744
1745
1746
1747 <label class="md-nav__link md-nav__link--active" for="__toc">
1748 Part 1
1749 <span class="md-nav__icon md-icon"></span>
1750 </label>
1751
1752 <a href="./" class="md-nav__link md-nav__link--active">
1753 Part 1
1754 </a>
1755
1756
1757 <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
1758
1759
1760
1761
1762
1763 <label class="md-nav__title" for="__toc">
1764 <span class="md-nav__icon md-icon"></span>
1765 Table of contents
1766 </label>
1767 <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
1768
1769 <li class="md-nav__item">
1770 <a href="#package-functionality" class="md-nav__link">
1771 Package Functionality
1772 </a>
1773
1774 </li>
1775
1776 <li class="md-nav__item">
1777 <a href="#used-components" class="md-nav__link">
1778 Used Components
1779 </a>
1780
1781 </li>
1782
1783 <li class="md-nav__item">
1784 <a href="#package-structure" class="md-nav__link">
1785 Package Structure
1786 </a>
1787
1788 </li>
1789
1790 <li class="md-nav__item">
1791 <a href="#person-modeling" class="md-nav__link">
1792 Person Modeling
1793 </a>
1794
1795 <nav class="md-nav" aria-label="Person Modeling">
1796 <ul class="md-nav__list">
1797
1798 <li class="md-nav__item">
1799 <a href="#database-table" class="md-nav__link">
1800 Database Table
1801 </a>
1802
1803 </li>
1804
1805 <li class="md-nav__item">
1806 <a href="#database-object" class="md-nav__link">
1807 Database Object
1808 </a>
1809
1810 <nav class="md-nav" aria-label="Database Object">
1811 <ul class="md-nav__list">
1812
1813 <li class="md-nav__item">
1814 <a href="#person" class="md-nav__link">
1815 Person
1816 </a>
1817
1818 </li>
1819
1820 <li class="md-nav__item">
1821 <a href="#personaction" class="md-nav__link">
1822 PersonAction
1823 </a>
1824
1825 </li>
1826
1827 <li class="md-nav__item">
1828 <a href="#personeditor" class="md-nav__link">
1829 PersonEditor
1830 </a>
1831
1832 </li>
1833
1834 <li class="md-nav__item">
1835 <a href="#personlist" class="md-nav__link">
1836 PersonList
1837 </a>
1838
1839 </li>
1840
1841 </ul>
1842 </nav>
1843
1844 </li>
1845
1846 </ul>
1847 </nav>
1848
1849 </li>
1850
1851 <li class="md-nav__item">
1852 <a href="#acp" class="md-nav__link">
1853 ACP
1854 </a>
1855
1856 <nav class="md-nav" aria-label="ACP">
1857 <ul class="md-nav__list">
1858
1859 <li class="md-nav__item">
1860 <a href="#acp-menu" class="md-nav__link">
1861 ACP Menu
1862 </a>
1863
1864 </li>
1865
1866 <li class="md-nav__item">
1867 <a href="#people-list" class="md-nav__link">
1868 People List
1869 </a>
1870
1871 <nav class="md-nav" aria-label="People List">
1872 <ul class="md-nav__list">
1873
1874 <li class="md-nav__item">
1875 <a href="#personlistpage" class="md-nav__link">
1876 PersonListPage
1877 </a>
1878
1879 </li>
1880
1881 <li class="md-nav__item">
1882 <a href="#personlisttpl" class="md-nav__link">
1883 personList.tpl
1884 </a>
1885
1886 </li>
1887
1888 </ul>
1889 </nav>
1890
1891 </li>
1892
1893 <li class="md-nav__item">
1894 <a href="#person-add-form" class="md-nav__link">
1895 Person Add Form
1896 </a>
1897
1898 <nav class="md-nav" aria-label="Person Add Form">
1899 <ul class="md-nav__list">
1900
1901 <li class="md-nav__item">
1902 <a href="#personaddform" class="md-nav__link">
1903 PersonAddForm
1904 </a>
1905
1906 </li>
1907
1908 <li class="md-nav__item">
1909 <a href="#personaddtpl" class="md-nav__link">
1910 personAdd.tpl
1911 </a>
1912
1913 </li>
1914
1915 </ul>
1916 </nav>
1917
1918 </li>
1919
1920 <li class="md-nav__item">
1921 <a href="#person-edit-form" class="md-nav__link">
1922 Person Edit Form
1923 </a>
1924
1925 <nav class="md-nav" aria-label="Person Edit Form">
1926 <ul class="md-nav__list">
1927
1928 <li class="md-nav__item">
1929 <a href="#personeditform" class="md-nav__link">
1930 PersonEditForm
1931 </a>
1932
1933 </li>
1934
1935 </ul>
1936 </nav>
1937
1938 </li>
1939
1940 </ul>
1941 </nav>
1942
1943 </li>
1944
1945 <li class="md-nav__item">
1946 <a href="#frontend" class="md-nav__link">
1947 Frontend
1948 </a>
1949
1950 <nav class="md-nav" aria-label="Frontend">
1951 <ul class="md-nav__list">
1952
1953 <li class="md-nav__item">
1954 <a href="#pagexml" class="md-nav__link">
1955 page.xml
1956 </a>
1957
1958 </li>
1959
1960 <li class="md-nav__item">
1961 <a href="#menuitemxml" class="md-nav__link">
1962 menuItem.xml
1963 </a>
1964
1965 </li>
1966
1967 <li class="md-nav__item">
1968 <a href="#people-list_1" class="md-nav__link">
1969 People List
1970 </a>
1971
1972 <nav class="md-nav" aria-label="People List">
1973 <ul class="md-nav__list">
1974
1975 <li class="md-nav__item">
1976 <a href="#personlistpage_1" class="md-nav__link">
1977 PersonListPage
1978 </a>
1979
1980 </li>
1981
1982 <li class="md-nav__item">
1983 <a href="#personlisttpl_1" class="md-nav__link">
1984 personList.tpl
1985 </a>
1986
1987 </li>
1988
1989 </ul>
1990 </nav>
1991
1992 </li>
1993
1994 </ul>
1995 </nav>
1996
1997 </li>
1998
1999 <li class="md-nav__item">
2000 <a href="#usergroupoptionxml" class="md-nav__link">
2001 userGroupOption.xml
2002 </a>
2003
2004 </li>
2005
2006 <li class="md-nav__item">
2007 <a href="#packagexml" class="md-nav__link">
2008 package.xml
2009 </a>
2010
2011 </li>
2012
2013 </ul>
2014
2015 </nav>
2016
2017 </li>
2018
2019
2020
2021
2022
2023
2024
2025 <li class="md-nav__item">
2026 <a href="../part_2/" class="md-nav__link">
2027 Part 2
2028 </a>
2029 </li>
2030
2031
2032
2033
2034
2035
2036
2037 <li class="md-nav__item">
2038 <a href="../part_3/" class="md-nav__link">
2039 Part 3
2040 </a>
2041 </li>
2042
2043
2044
2045 </ul>
2046 </nav>
2047 </li>
2048
2049
2050
2051 </ul>
2052 </nav>
2053 </li>
2054
2055
2056
2057 </ul>
2058 </nav>
2059 </div>
2060 </div>
2061 </div>
2062
2063
2064
2065 <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
2066 <div class="md-sidebar__scrollwrap">
2067 <div class="md-sidebar__inner">
2068
2069 <nav class="md-nav md-nav--secondary" aria-label="Table of contents">
2070
2071
2072
2073
2074
2075 <label class="md-nav__title" for="__toc">
2076 <span class="md-nav__icon md-icon"></span>
2077 Table of contents
2078 </label>
2079 <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
2080
2081 <li class="md-nav__item">
2082 <a href="#package-functionality" class="md-nav__link">
2083 Package Functionality
2084 </a>
2085
2086 </li>
2087
2088 <li class="md-nav__item">
2089 <a href="#used-components" class="md-nav__link">
2090 Used Components
2091 </a>
2092
2093 </li>
2094
2095 <li class="md-nav__item">
2096 <a href="#package-structure" class="md-nav__link">
2097 Package Structure
2098 </a>
2099
2100 </li>
2101
2102 <li class="md-nav__item">
2103 <a href="#person-modeling" class="md-nav__link">
2104 Person Modeling
2105 </a>
2106
2107 <nav class="md-nav" aria-label="Person Modeling">
2108 <ul class="md-nav__list">
2109
2110 <li class="md-nav__item">
2111 <a href="#database-table" class="md-nav__link">
2112 Database Table
2113 </a>
2114
2115 </li>
2116
2117 <li class="md-nav__item">
2118 <a href="#database-object" class="md-nav__link">
2119 Database Object
2120 </a>
2121
2122 <nav class="md-nav" aria-label="Database Object">
2123 <ul class="md-nav__list">
2124
2125 <li class="md-nav__item">
2126 <a href="#person" class="md-nav__link">
2127 Person
2128 </a>
2129
2130 </li>
2131
2132 <li class="md-nav__item">
2133 <a href="#personaction" class="md-nav__link">
2134 PersonAction
2135 </a>
2136
2137 </li>
2138
2139 <li class="md-nav__item">
2140 <a href="#personeditor" class="md-nav__link">
2141 PersonEditor
2142 </a>
2143
2144 </li>
2145
2146 <li class="md-nav__item">
2147 <a href="#personlist" class="md-nav__link">
2148 PersonList
2149 </a>
2150
2151 </li>
2152
2153 </ul>
2154 </nav>
2155
2156 </li>
2157
2158 </ul>
2159 </nav>
2160
2161 </li>
2162
2163 <li class="md-nav__item">
2164 <a href="#acp" class="md-nav__link">
2165 ACP
2166 </a>
2167
2168 <nav class="md-nav" aria-label="ACP">
2169 <ul class="md-nav__list">
2170
2171 <li class="md-nav__item">
2172 <a href="#acp-menu" class="md-nav__link">
2173 ACP Menu
2174 </a>
2175
2176 </li>
2177
2178 <li class="md-nav__item">
2179 <a href="#people-list" class="md-nav__link">
2180 People List
2181 </a>
2182
2183 <nav class="md-nav" aria-label="People List">
2184 <ul class="md-nav__list">
2185
2186 <li class="md-nav__item">
2187 <a href="#personlistpage" class="md-nav__link">
2188 PersonListPage
2189 </a>
2190
2191 </li>
2192
2193 <li class="md-nav__item">
2194 <a href="#personlisttpl" class="md-nav__link">
2195 personList.tpl
2196 </a>
2197
2198 </li>
2199
2200 </ul>
2201 </nav>
2202
2203 </li>
2204
2205 <li class="md-nav__item">
2206 <a href="#person-add-form" class="md-nav__link">
2207 Person Add Form
2208 </a>
2209
2210 <nav class="md-nav" aria-label="Person Add Form">
2211 <ul class="md-nav__list">
2212
2213 <li class="md-nav__item">
2214 <a href="#personaddform" class="md-nav__link">
2215 PersonAddForm
2216 </a>
2217
2218 </li>
2219
2220 <li class="md-nav__item">
2221 <a href="#personaddtpl" class="md-nav__link">
2222 personAdd.tpl
2223 </a>
2224
2225 </li>
2226
2227 </ul>
2228 </nav>
2229
2230 </li>
2231
2232 <li class="md-nav__item">
2233 <a href="#person-edit-form" class="md-nav__link">
2234 Person Edit Form
2235 </a>
2236
2237 <nav class="md-nav" aria-label="Person Edit Form">
2238 <ul class="md-nav__list">
2239
2240 <li class="md-nav__item">
2241 <a href="#personeditform" class="md-nav__link">
2242 PersonEditForm
2243 </a>
2244
2245 </li>
2246
2247 </ul>
2248 </nav>
2249
2250 </li>
2251
2252 </ul>
2253 </nav>
2254
2255 </li>
2256
2257 <li class="md-nav__item">
2258 <a href="#frontend" class="md-nav__link">
2259 Frontend
2260 </a>
2261
2262 <nav class="md-nav" aria-label="Frontend">
2263 <ul class="md-nav__list">
2264
2265 <li class="md-nav__item">
2266 <a href="#pagexml" class="md-nav__link">
2267 page.xml
2268 </a>
2269
2270 </li>
2271
2272 <li class="md-nav__item">
2273 <a href="#menuitemxml" class="md-nav__link">
2274 menuItem.xml
2275 </a>
2276
2277 </li>
2278
2279 <li class="md-nav__item">
2280 <a href="#people-list_1" class="md-nav__link">
2281 People List
2282 </a>
2283
2284 <nav class="md-nav" aria-label="People List">
2285 <ul class="md-nav__list">
2286
2287 <li class="md-nav__item">
2288 <a href="#personlistpage_1" class="md-nav__link">
2289 PersonListPage
2290 </a>
2291
2292 </li>
2293
2294 <li class="md-nav__item">
2295 <a href="#personlisttpl_1" class="md-nav__link">
2296 personList.tpl
2297 </a>
2298
2299 </li>
2300
2301 </ul>
2302 </nav>
2303
2304 </li>
2305
2306 </ul>
2307 </nav>
2308
2309 </li>
2310
2311 <li class="md-nav__item">
2312 <a href="#usergroupoptionxml" class="md-nav__link">
2313 userGroupOption.xml
2314 </a>
2315
2316 </li>
2317
2318 <li class="md-nav__item">
2319 <a href="#packagexml" class="md-nav__link">
2320 package.xml
2321 </a>
2322
2323 </li>
2324
2325 </ul>
2326
2327 </nav>
2328 </div>
2329 </div>
2330 </div>
2331
2332
2333 <div class="md-content" data-md-component="content">
2334 <article class="md-content__inner md-typeset">
2335
2336
2337 <a href="https://github.com/WoltLab/docs.woltlab.com/edit/5.4/docs/tutorial/series/part_1.md" title="Edit this page" class="md-content__button md-icon">
2338 <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>
2339 </a>
2340
2341
2342 <h1 id="tutorial-series-part-1-base-structure">Tutorial Series Part 1: Base Structure<a class="headerlink" href="#tutorial-series-part-1-base-structure" title="Permanent link">#</a></h1>
2343 <p>In the first part of this tutorial series, we will lay out what the basic version of package should be able to do and how to implement these functions.</p>
2344 <h2 id="package-functionality">Package Functionality<a class="headerlink" href="#package-functionality" title="Permanent link">#</a></h2>
2345 <p>The package should provide the following possibilities/functions:</p>
2346 <ul>
2347 <li>Sortable list of all people in the ACP</li>
2348 <li>Ability to add, edit and delete people in the ACP</li>
2349 <li>Restrict the ability to add, edit and delete people (in short: manage people) in the ACP</li>
2350 <li>Sortable list of all people in the front end</li>
2351 </ul>
2352 <h2 id="used-components">Used Components<a class="headerlink" href="#used-components" title="Permanent link">#</a></h2>
2353 <p>We will use the following package installation plugins:</p>
2354 <ul>
2355 <li><a href="../../../package/pip/acp-template/">acpTemplate package installation plugin</a>,</li>
2356 <li><a href="../../../package/pip/acp-menu/">acpMenu package installation plugin</a>,</li>
2357 <li><a href="../../../package/pip/file/">file package installation plugin</a>,</li>
2358 <li><a href="../../../package/pip/language/">language package installation plugin</a>,</li>
2359 <li><a href="../../../package/pip/menu-item/">menuItem package installation plugin</a>,</li>
2360 <li><a href="../../../package/pip/page/">page package installation plugin</a>,</li>
2361 <li><a href="../../../package/pip/sql/">sql package installation plugin</a>,</li>
2362 <li><a href="../../../package/pip/template/">template package installation plugin</a>,</li>
2363 <li><a href="../../../package/pip/user-group-option/">userGroupOption package installation plugin</a>,</li>
2364 </ul>
2365 <p>use <a href="../../../php/database-objects/">database objects</a>, create <a href="../../../php/pages/">pages</a> and use <a href="../../../view/templates/">templates</a>.</p>
2366 <h2 id="package-structure">Package Structure<a class="headerlink" href="#package-structure" title="Permanent link">#</a></h2>
2367 <p>The package will have the following file structure:</p>
2368 <div class="highlight"><pre><span></span><code>├── acpMenu.xml
2369 ├── acptemplates
2370 │ ├── personAdd.tpl
2371 │ └── personList.tpl
2372 ├── files
2373 │ └── lib
2374 │ ├── acp
2375 │ │ ├── form
2376 │ │ │ ├── PersonAddForm.class.php
2377 │ │ │ └── PersonEditForm.class.php
2378 │ │ └── page
2379 │ │ └── PersonListPage.class.php
2380 │ ├── data
2381 │ │ └── person
2382 │ │ ├── PersonAction.class.php
2383 │ │ ├── Person.class.php
2384 │ │ ├── PersonEditor.class.php
2385 │ │ └── PersonList.class.php
2386 │ └── page
2387 │ └── PersonListPage.class.php
2388 ├── install.sql
2389 ├── language
2390 │ ├── de.xml
2391 │ └── en.xml
2392 ├── menuItem.xml
2393 ├── package.xml
2394 ├── page.xml
2395 ├── templates
2396 │ └── personList.tpl
2397 └── userGroupOption.xml
2398 </code></pre></div>
2399 <h2 id="person-modeling">Person Modeling<a class="headerlink" href="#person-modeling" title="Permanent link">#</a></h2>
2400 <h3 id="database-table">Database Table<a class="headerlink" href="#database-table" title="Permanent link">#</a></h3>
2401 <p>As the first step, we have to model the people we want to manage with this package.
2402 As this is only an introductory tutorial, we will keep things simple and only consider the first and last name of a person.
2403 Thus, the database table we will store the people in only contains three columns:</p>
2404 <ol>
2405 <li><code>personID</code> is the unique numeric identifier of each person created,</li>
2406 <li><code>firstName</code> contains the first name of the person,</li>
2407 <li><code>lastName</code> contains the last name of the person.</li>
2408 </ol>
2409 <p>The first file for our package is the <code>install.sql</code> file used to create such a database table during package installation:</p>
2410 <div class="highlight"><pre><span></span><code><span class="k">DROP</span> <span class="k">TABLE</span> <span class="k">IF</span> <span class="k">EXISTS</span> <span class="n">wcf1_person</span><span class="p">;</span>
2411 <span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">wcf1_person</span> <span class="p">(</span>
2412 <span class="n">personID</span> <span class="nb">INT</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="n">AUTO_INCREMENT</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
2413 <span class="n">firstName</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
2414 <span class="n">lastName</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NOT</span> <span class="k">NULL</span>
2415 <span class="p">);</span>
2416 </code></pre></div>
2417 <h3 id="database-object">Database Object<a class="headerlink" href="#database-object" title="Permanent link">#</a></h3>
2418 <h4 id="person"><code>Person</code><a class="headerlink" href="#person" title="Permanent link">#</a></h4>
2419 <p>In our PHP code, each person will be represented by an object of the following class:</p>
2420 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2421 <span class="k">namespace</span> <span class="nx">wcf\data\person</span><span class="p">;</span>
2422 <span class="k">use</span> <span class="nx">wcf\data\DatabaseObject</span><span class="p">;</span>
2423 <span class="k">use</span> <span class="nx">wcf\system\request\IRouteController</span><span class="p">;</span>
2424
2425 <span class="sd">/**</span>
2426 <span class="sd"> * Represents a person.</span>
2427 <span class="sd"> * </span>
2428 <span class="sd"> * @author Matthias Schmidt</span>
2429 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2430 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2431 <span class="sd"> * @package WoltLabSuite\Core\Data\Person</span>
2432 <span class="sd"> * </span>
2433 <span class="sd"> * @property-read integer $personID unique id of the person</span>
2434 <span class="sd"> * @property-read string $firstName first name of the person</span>
2435 <span class="sd"> * @property-read string $lastName last name of the person</span>
2436 <span class="sd"> */</span>
2437 <span class="k">class</span> <span class="nc">Person</span> <span class="k">extends</span> <span class="nx">DatabaseObject</span> <span class="k">implements</span> <span class="nx">IRouteController</span> <span class="p">{</span>
2438 <span class="sd">/**</span>
2439 <span class="sd"> * Returns the first and last name of the person if a person object is treated as a string.</span>
2440 <span class="sd"> * </span>
2441 <span class="sd"> * @return string</span>
2442 <span class="sd"> */</span>
2443 <span class="k">public</span> <span class="k">function</span> <span class="fm">__toString</span><span class="p">()</span> <span class="p">{</span>
2444 <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">getTitle</span><span class="p">();</span>
2445 <span class="p">}</span>
2446
2447 <span class="sd">/**</span>
2448 <span class="sd"> * @inheritDoc</span>
2449 <span class="sd"> */</span>
2450 <span class="k">public</span> <span class="k">function</span> <span class="nf">getTitle</span><span class="p">()</span> <span class="p">{</span>
2451 <span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span> <span class="o">.</span> <span class="s1">&#39; &#39;</span> <span class="o">.</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span><span class="p">;</span>
2452 <span class="p">}</span>
2453 <span class="p">}</span>
2454 </code></pre></div>
2455 <p>The important thing here is that <code>Person</code> extends <code>DatabaseObject</code>.
2456 Additionally, we implement the <code>IRouteController</code> interface, which allows us to use <code>Person</code> objects to create links, and we implement PHP's magic <a href="https://secure.php.net/manual/en/language.oop5.magic.php#object.tostring">__toString()</a> method for convenience.</p>
2457 <p>For every database object, you need to implement three additional classes:
2458 an action class, an editor class and a list class.</p>
2459 <h4 id="personaction"><code>PersonAction</code><a class="headerlink" href="#personaction" title="Permanent link">#</a></h4>
2460 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2461 <span class="k">namespace</span> <span class="nx">wcf\data\person</span><span class="p">;</span>
2462 <span class="k">use</span> <span class="nx">wcf\data\AbstractDatabaseObjectAction</span><span class="p">;</span>
2463
2464 <span class="sd">/**</span>
2465 <span class="sd"> * Executes person-related actions.</span>
2466 <span class="sd"> * </span>
2467 <span class="sd"> * @author Matthias Schmidt</span>
2468 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2469 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2470 <span class="sd"> * @package WoltLabSuite\Core\Data\Person</span>
2471 <span class="sd"> *</span>
2472 <span class="sd"> * @method Person create()</span>
2473 <span class="sd"> * @method PersonEditor[] getObjects()</span>
2474 <span class="sd"> * @method PersonEditor getSingleObject()</span>
2475 <span class="sd"> */</span>
2476 <span class="k">class</span> <span class="nc">PersonAction</span> <span class="k">extends</span> <span class="nx">AbstractDatabaseObjectAction</span> <span class="p">{</span>
2477 <span class="sd">/**</span>
2478 <span class="sd"> * @inheritDoc</span>
2479 <span class="sd"> */</span>
2480 <span class="k">protected</span> <span class="nv">$permissionsDelete</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;admin.content.canManagePeople&#39;</span><span class="p">];</span>
2481
2482 <span class="sd">/**</span>
2483 <span class="sd"> * @inheritDoc</span>
2484 <span class="sd"> */</span>
2485 <span class="k">protected</span> <span class="nv">$requireACP</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;delete&#39;</span><span class="p">];</span>
2486 <span class="p">}</span>
2487 </code></pre></div>
2488 <p>This implementation of <code>AbstractDatabaseObjectAction</code> is very basic and only sets the <code>$permissionsDelete</code> and <code>$requireACP</code> properties.
2489 This is done so that later on, when implementing the people list for the ACP, we can delete people simply via AJAX.
2490 <code>$permissionsDelete</code> has to be set to the permission needed in order to delete a person.
2491 We will later use the <a href="../../../package/pip/user-group-option/">userGroupOption package installation plugin</a> to create the <code>admin.content.canManagePeople</code> permission.
2492 <code>$requireACP</code> restricts deletion of people to the ACP.</p>
2493 <h4 id="personeditor"><code>PersonEditor</code><a class="headerlink" href="#personeditor" title="Permanent link">#</a></h4>
2494 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2495 <span class="k">namespace</span> <span class="nx">wcf\data\person</span><span class="p">;</span>
2496 <span class="k">use</span> <span class="nx">wcf\data\DatabaseObjectEditor</span><span class="p">;</span>
2497
2498 <span class="sd">/**</span>
2499 <span class="sd"> * Provides functions to edit people.</span>
2500 <span class="sd"> * </span>
2501 <span class="sd"> * @author Matthias Schmidt</span>
2502 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2503 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2504 <span class="sd"> * @package WoltLabSuite\Core\Data\Person</span>
2505 <span class="sd"> * </span>
2506 <span class="sd"> * @method static Person create(array $parameters = [])</span>
2507 <span class="sd"> * @method Person getDecoratedObject()</span>
2508 <span class="sd"> * @mixin Person</span>
2509 <span class="sd"> */</span>
2510 <span class="k">class</span> <span class="nc">PersonEditor</span> <span class="k">extends</span> <span class="nx">DatabaseObjectEditor</span> <span class="p">{</span>
2511 <span class="sd">/**</span>
2512 <span class="sd"> * @inheritDoc</span>
2513 <span class="sd"> */</span>
2514 <span class="k">protected</span> <span class="k">static</span> <span class="nv">$baseClass</span> <span class="o">=</span> <span class="nx">Person</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
2515 <span class="p">}</span>
2516 </code></pre></div>
2517 <p>This implementation of <code>DatabaseObjectEditor</code> fulfills the minimum requirement for a database object editor:
2518 setting the static <code>$baseClass</code> property to the database object class name.</p>
2519 <h4 id="personlist"><code>PersonList</code><a class="headerlink" href="#personlist" title="Permanent link">#</a></h4>
2520 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2521 <span class="k">namespace</span> <span class="nx">wcf\data\person</span><span class="p">;</span>
2522 <span class="k">use</span> <span class="nx">wcf\data\DatabaseObjectList</span><span class="p">;</span>
2523
2524 <span class="sd">/**</span>
2525 <span class="sd"> * Represents a list of people.</span>
2526 <span class="sd"> * </span>
2527 <span class="sd"> * @author Matthias Schmidt</span>
2528 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2529 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2530 <span class="sd"> * @package WoltLabSuite\Core\Data\Person</span>
2531 <span class="sd"> * </span>
2532 <span class="sd"> * @method Person current()</span>
2533 <span class="sd"> * @method Person[] getObjects()</span>
2534 <span class="sd"> * @method Person|null search($objectID)</span>
2535 <span class="sd"> * @property Person[] $objects</span>
2536 <span class="sd"> */</span>
2537 <span class="k">class</span> <span class="nc">PersonList</span> <span class="k">extends</span> <span class="nx">DatabaseObjectList</span> <span class="p">{}</span>
2538 </code></pre></div>
2539 <p>Due to the default implementation of <code>DatabaseObjectList</code>, our <code>PersonList</code> class just needs to extend it and everything else is either automatically set by the code of <code>DatabaseObjectList</code> or, in the case of properties and methods, provided by that class.</p>
2540 <h2 id="acp">ACP<a class="headerlink" href="#acp" title="Permanent link">#</a></h2>
2541 <p>Next, we will take care of the controllers and views for the ACP.
2542 In total, we need three each:</p>
2543 <ol>
2544 <li>page to list people,</li>
2545 <li>form to add people, and</li>
2546 <li>form to edit people.</li>
2547 </ol>
2548 <p>Before we create the controllers and views, let us first create the menu items for the pages in the ACP menu.</p>
2549 <h3 id="acp-menu">ACP Menu<a class="headerlink" href="#acp-menu" title="Permanent link">#</a></h3>
2550 <p>We need to create three menu items:</p>
2551 <ol>
2552 <li>a “parent” menu item on the second level of the ACP menu item tree,</li>
2553 <li>a third level menu item for the people list page, and</li>
2554 <li>a fourth level menu item for the form to add new people.</li>
2555 </ol>
2556 <div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
2557 <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/acpMenu.xsd&quot;</span><span class="nt">&gt;</span>
2558 <span class="nt">&lt;import&gt;</span>
2559 <span class="nt">&lt;acpmenuitem</span> <span class="na">name=</span><span class="s">&quot;wcf.acp.menu.link.person&quot;</span><span class="nt">&gt;</span>
2560 <span class="nt">&lt;parent&gt;</span>wcf.acp.menu.link.content<span class="nt">&lt;/parent&gt;</span>
2561 <span class="nt">&lt;/acpmenuitem&gt;</span>
2562 <span class="nt">&lt;acpmenuitem</span> <span class="na">name=</span><span class="s">&quot;wcf.acp.menu.link.person.list&quot;</span><span class="nt">&gt;</span>
2563 <span class="nt">&lt;controller&gt;</span>wcf\acp\page\PersonListPage<span class="nt">&lt;/controller&gt;</span>
2564 <span class="nt">&lt;parent&gt;</span>wcf.acp.menu.link.person<span class="nt">&lt;/parent&gt;</span>
2565 <span class="nt">&lt;permissions&gt;</span>admin.content.canManagePeople<span class="nt">&lt;/permissions&gt;</span>
2566 <span class="nt">&lt;/acpmenuitem&gt;</span>
2567 <span class="nt">&lt;acpmenuitem</span> <span class="na">name=</span><span class="s">&quot;wcf.acp.menu.link.person.add&quot;</span><span class="nt">&gt;</span>
2568 <span class="nt">&lt;controller&gt;</span>wcf\acp\form\PersonAddForm<span class="nt">&lt;/controller&gt;</span>
2569 <span class="nt">&lt;parent&gt;</span>wcf.acp.menu.link.person.list<span class="nt">&lt;/parent&gt;</span>
2570 <span class="nt">&lt;permissions&gt;</span>admin.content.canManagePeople<span class="nt">&lt;/permissions&gt;</span>
2571 <span class="nt">&lt;icon&gt;</span>fa-plus<span class="nt">&lt;/icon&gt;</span>
2572 <span class="nt">&lt;/acpmenuitem&gt;</span>
2573 <span class="nt">&lt;/import&gt;</span>
2574 <span class="nt">&lt;/data&gt;</span>
2575 </code></pre></div>
2576 <p>We choose <code>wcf.acp.menu.link.content</code> as the parent menu item for the first menu item <code>wcf.acp.menu.link.person</code> because the people we are managing is just one form of content.
2577 The fourth level menu item <code>wcf.acp.menu.link.person.add</code> will only be shown as an icon and thus needs an additional element <code>icon</code> which takes a FontAwesome icon class as value.</p>
2578 <h3 id="people-list">People List<a class="headerlink" href="#people-list" title="Permanent link">#</a></h3>
2579 <p>To list the people in the ACP, we need a <code>PersonListPage</code> class and a <code>personList</code> template.</p>
2580 <h4 id="personlistpage"><code>PersonListPage</code><a class="headerlink" href="#personlistpage" title="Permanent link">#</a></h4>
2581 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2582 <span class="k">namespace</span> <span class="nx">wcf\acp\page</span><span class="p">;</span>
2583 <span class="k">use</span> <span class="nx">wcf\data\person\PersonList</span><span class="p">;</span>
2584 <span class="k">use</span> <span class="nx">wcf\page\SortablePage</span><span class="p">;</span>
2585
2586 <span class="sd">/**</span>
2587 <span class="sd"> * Shows the list of people.</span>
2588 <span class="sd"> * </span>
2589 <span class="sd"> * @author Matthias Schmidt</span>
2590 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2591 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2592 <span class="sd"> * @package WoltLabSuite\Core\Acp\Page</span>
2593 <span class="sd"> */</span>
2594 <span class="k">class</span> <span class="nc">PersonListPage</span> <span class="k">extends</span> <span class="nx">SortablePage</span> <span class="p">{</span>
2595 <span class="sd">/**</span>
2596 <span class="sd"> * @inheritDoc</span>
2597 <span class="sd"> */</span>
2598 <span class="k">public</span> <span class="nv">$activeMenuItem</span> <span class="o">=</span> <span class="s1">&#39;wcf.acp.menu.link.person.list&#39;</span><span class="p">;</span>
2599
2600 <span class="sd">/**</span>
2601 <span class="sd"> * @inheritDoc</span>
2602 <span class="sd"> */</span>
2603 <span class="k">public</span> <span class="nv">$neededPermissions</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;admin.content.canManagePeople&#39;</span><span class="p">];</span>
2604
2605 <span class="sd">/**</span>
2606 <span class="sd"> * @inheritDoc</span>
2607 <span class="sd"> */</span>
2608 <span class="k">public</span> <span class="nv">$objectListClassName</span> <span class="o">=</span> <span class="nx">PersonList</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
2609
2610 <span class="sd">/**</span>
2611 <span class="sd"> * @inheritDoc</span>
2612 <span class="sd"> */</span>
2613 <span class="k">public</span> <span class="nv">$validSortFields</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;personID&#39;</span><span class="p">,</span> <span class="s1">&#39;firstName&#39;</span><span class="p">,</span> <span class="s1">&#39;lastName&#39;</span><span class="p">];</span>
2614 <span class="p">}</span>
2615 </code></pre></div>
2616 <p>As WoltLab Suite Core already provides a powerful default implementation of a sortable page, our work here is minimal:</p>
2617 <ol>
2618 <li>We need to set the active ACP menu item via the <code>$activeMenuItem</code>.</li>
2619 <li><code>$neededPermissions</code> contains a list of permissions of which the user needs to have at least one in order to see the person list.
2620 We use the same permission for both the menu item and the page.</li>
2621 <li>The database object list class whose name is provided via <code>$objectListClassName</code> and that handles fetching the people from database is the <code>PersonList</code> class, which we have already created.</li>
2622 <li>To validate the sort field passed with the request, we set <code>$validSortFields</code> to the available database table columns.</li>
2623 </ol>
2624 <h4 id="personlisttpl"><code>personList.tpl</code><a class="headerlink" href="#personlisttpl" title="Permanent link">#</a></h4>
2625 <div class="highlight"><pre><span></span><code><span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;header&#39;</span> <span class="na">pageTitle</span><span class="o">=</span><span class="s1">&#39;wcf.acp.person.list&#39;</span><span class="cp">}</span><span class="x"></span>
2626
2627 <span class="x">&lt;header class=&quot;contentHeader&quot;&gt;</span>
2628 <span class="x"> &lt;div class=&quot;contentHeaderTitle&quot;&gt;</span>
2629 <span class="x"> &lt;h1 class=&quot;contentTitle&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.person.list</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/h1&gt;</span>
2630 <span class="x"> &lt;/div&gt;</span>
2631
2632 <span class="x"> &lt;nav class=&quot;contentHeaderNavigation&quot;&gt;</span>
2633 <span class="x"> &lt;ul&gt;</span>
2634 <span class="x"> &lt;li&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;PersonAdd&#39;</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot; class=&quot;button&quot;&gt;&lt;span class=&quot;icon icon16 fa-plus&quot;&gt;&lt;/span&gt; &lt;span&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.menu.link.person.add</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;</span>
2635
2636 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;contentHeaderNavigation&#39;</span><span class="cp">}</span><span class="x"></span>
2637 <span class="x"> &lt;/ul&gt;</span>
2638 <span class="x"> &lt;/nav&gt;</span>
2639 <span class="x">&lt;/header&gt;</span>
2640
2641 <span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
2642 <span class="x"> &lt;div class=&quot;paginationTop&quot;&gt;</span>
2643 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="nf">pages</span> <span class="na">print</span><span class="o">=</span><span class="kc">true</span> <span class="na">assign</span><span class="o">=</span><span class="na">pagesLinks</span> <span class="na">controller</span><span class="o">=</span><span class="s2">&quot;PersonList&quot;</span> <span class="na">link</span><span class="o">=</span><span class="s2">&quot;pageNo=%d&amp;sortField=$sortField&amp;sortOrder=$sortOrder&quot;</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
2644 <span class="x"> &lt;/div&gt;</span>
2645 <span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
2646
2647 <span class="cp">{</span><span class="nf">if</span> <span class="nv">$objects</span><span class="o">|</span><span class="na">count</span><span class="cp">}</span><span class="x"></span>
2648 <span class="x"> &lt;div class=&quot;section tabularBox&quot; id=&quot;personTableContainer&quot;&gt;</span>
2649 <span class="x"> &lt;table class=&quot;table&quot;&gt;</span>
2650 <span class="x"> &lt;thead&gt;</span>
2651 <span class="x"> &lt;tr&gt;</span>
2652 <span class="x"> &lt;th class=&quot;columnID columnPersonID</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;personID&#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; colspan=&quot;2&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=personID&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;personID&#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.global.objectID</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/th&gt;</span>
2653 <span class="x"> &lt;th class=&quot;columnTitle columnFirstName</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;firstName&#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=firstName&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;firstName&#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.firstName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/th&gt;</span>
2654 <span class="x"> &lt;th class=&quot;columnTitle columnLastName</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;lastName&#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=lastName&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;lastName&#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.lastName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/th&gt;</span>
2655
2656 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;columnHeads&#39;</span><span class="cp">}</span><span class="x"></span>
2657 <span class="x"> &lt;/tr&gt;</span>
2658 <span class="x"> &lt;/thead&gt;</span>
2659
2660 <span class="x"> &lt;tbody class=&quot;jsReloadPageWhenEmpty&quot;&gt;</span>
2661 <span class="x"> </span><span class="cp">{</span><span class="nf">foreach</span> <span class="na">from</span><span class="o">=</span><span class="nv">$objects</span> <span class="na">item</span><span class="o">=</span><span class="na">person</span><span class="cp">}</span><span class="x"></span>
2662 <span class="x"> &lt;tr class=&quot;jsPersonRow&quot;&gt;</span>
2663 <span class="x"> &lt;td class=&quot;columnIcon&quot;&gt;</span>
2664 <span class="x"> &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;PersonEdit&#39;</span> <span class="na">object</span><span class="o">=</span><span class="nv">$person</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot; title=&quot;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.button.edit</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&quot; class=&quot;jsTooltip&quot;&gt;&lt;span class=&quot;icon icon16 fa-pencil&quot;&gt;&lt;/span&gt;&lt;/a&gt;</span>
2665 <span class="x"> &lt;span class=&quot;icon icon16 fa-times jsDeleteButton jsTooltip pointer&quot; title=&quot;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.button.delete</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&quot; data-object-id=&quot;</span><span class="cp">{</span><span class="o">@</span><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">personID</span><span class="cp">}</span><span class="x">&quot; data-confirm-message-html=&quot;</span><span class="cp">{</span><span class="nf">lang</span> <span class="na">__encode</span><span class="o">=</span><span class="kc">true</span><span class="cp">}</span><span class="x">wcf.acp.person.delete.confirmMessage</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&quot;&gt;&lt;/span&gt;</span>
2666
2667 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;rowButtons&#39;</span><span class="cp">}</span><span class="x"></span>
2668 <span class="x"> &lt;/td&gt;</span>
2669 <span class="x"> &lt;td class=&quot;columnID&quot;&gt;</span><span class="cp">{</span><span class="err">#</span><span class="nv">$person</span><span class="o">-&gt;</span><span class="na">personID</span><span class="cp">}</span><span class="x">&lt;/td&gt;</span>
2670 <span class="x"> &lt;td class=&quot;columnTitle columnFirstName&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;PersonEdit&#39;</span> <span class="na">object</span><span class="o">=</span><span class="nv">$person</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="nv">$person</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/td&gt;</span>
2671 <span class="x"> &lt;td class=&quot;columnTitle columnLastName&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;PersonEdit&#39;</span> <span class="na">object</span><span class="o">=</span><span class="nv">$person</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="nv">$person</span><span class="o">-&gt;</span><span class="na">lastName</span><span class="cp">}</span><span class="x">&lt;/a&gt;&lt;/td&gt;</span>
2672
2673 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;columns&#39;</span><span class="cp">}</span><span class="x"></span>
2674 <span class="x"> &lt;/tr&gt;</span>
2675 <span class="x"> </span><span class="cp">{</span><span class="nf">/foreach</span><span class="cp">}</span><span class="x"></span>
2676 <span class="x"> &lt;/tbody&gt;</span>
2677 <span class="x"> &lt;/table&gt;</span>
2678 <span class="x"> &lt;/div&gt;</span>
2679
2680 <span class="x"> &lt;footer class=&quot;contentFooter&quot;&gt;</span>
2681 <span class="x"> </span><span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
2682 <span class="x"> &lt;div class=&quot;paginationBottom&quot;&gt;</span>
2683 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="o">@</span><span class="nv">$pagesLinks</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
2684 <span class="x"> &lt;/div&gt;</span>
2685 <span class="x"> </span><span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
2686
2687 <span class="x"> &lt;nav class=&quot;contentFooterNavigation&quot;&gt;</span>
2688 <span class="x"> &lt;ul&gt;</span>
2689 <span class="x"> &lt;li&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;PersonAdd&#39;</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot; class=&quot;button&quot;&gt;&lt;span class=&quot;icon icon16 fa-plus&quot;&gt;&lt;/span&gt; &lt;span&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.menu.link.person.add</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;</span>
2690
2691 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;contentFooterNavigation&#39;</span><span class="cp">}</span><span class="x"></span>
2692 <span class="x"> &lt;/ul&gt;</span>
2693 <span class="x"> &lt;/nav&gt;</span>
2694 <span class="x"> &lt;/footer&gt;</span>
2695 <span class="cp">{</span><span class="nf">else</span><span class="cp">}</span><span class="x"></span>
2696 <span class="x"> &lt;p class=&quot;info&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.noItems</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/p&gt;</span>
2697 <span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2698
2699 <span class="x">&lt;script data-relocate=&quot;true&quot;&gt;</span>
2700 <span class="x"> $(function() </span><span class="cp">{</span>
2701 <span class="na">new</span> <span class="na">WCF</span><span class="o">.</span><span class="na">Action</span><span class="o">.</span><span class="na">Delete</span><span class="o">(</span><span class="s1">&#39;wcf\\data\\person\\PersonAction&#39;</span><span class="o">,</span> <span class="s1">&#39;.jsPersonRow&#39;</span><span class="o">);</span>
2702 <span class="cp">}</span><span class="x">);</span>
2703 <span class="x">&lt;/script&gt;</span>
2704
2705 <span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;footer&#39;</span><span class="cp">}</span><span class="x"></span>
2706 </code></pre></div>
2707 <p>We will go piece by piece through the template code:</p>
2708 <ol>
2709 <li>We include the <code>header</code> template and set the page title <code>wcf.acp.person.list</code>.
2710 You have to include this template for every page!</li>
2711 <li>We set the content header and additional provide a button to create a new person in the content header navigation.</li>
2712 <li>As not all people are listed on the same page if many people have been created, we need a pagination for which we use the <code>pages</code> template plugin.
2713 The <code>{hascontent}{content}{/content}{/hascontent}</code> construct ensures the <code>.paginationTop</code> element is only shown if the <code>pages</code> template plugin has a return value, thus if a pagination is necessary.</li>
2714 <li>Now comes the main part of the page, the list of the people, which will only be displayed if any people exist.
2715 Otherwise, an info box is displayed using the generic <code>wcf.global.noItems</code> language item.
2716 The <code>$objects</code> template variable is automatically assigned by <code>wcf\page\MultipleLinkPage</code> and contains the <code>PersonList</code> object used to read the people from database.</li>
2717 </ol>
2718 <p>The table itself consists of a <code>thead</code> and a <code>tbody</code> element and is extendable with more columns using the template events <code>columnHeads</code> and <code>columns</code>.
2719 In general, every table should provide these events.
2720 The default structure of a table is used here so that the first column of the content rows contains icons to edit and to delete the row (and provides another standard event <code>rowButtons</code>) and that the second column contains the ID of the person.
2721 The table can be sorted by clicking on the head of each column.
2722 The used variables <code>$sortField</code> and <code>$sortOrder</code> are automatically assigned to the template by <code>SortablePage</code>.
2723 1. The <code>.contentFooter</code> element is only shown if people exist as it basically repeats the <code>.contentHeaderNavigation</code> and <code>.paginationTop</code> element.
2724 1. The JavaScript code here fulfills two duties:
2725 Handling clicks on the delete icons and forwarding the requests via AJAX to the <code>PersonAction</code> class, and setting up some code that triggers if all people shown on the current page are deleted via JavaScript to either reload the page or show the <code>wcf.global.noItems</code> info box.
2726 1. Lastly, the <code>footer</code> template is included that terminates the page.
2727 You also have to include this template for every page!</p>
2728 <p>Now, we have finished the page to manage the people so that we can move on to the forms with which we actually create and edit the people.</p>
2729 <h3 id="person-add-form">Person Add Form<a class="headerlink" href="#person-add-form" title="Permanent link">#</a></h3>
2730 <p>Like the person list, the form to add new people requires a controller class and a template.</p>
2731 <h4 id="personaddform"><code>PersonAddForm</code><a class="headerlink" href="#personaddform" title="Permanent link">#</a></h4>
2732 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2733 <span class="k">namespace</span> <span class="nx">wcf\acp\form</span><span class="p">;</span>
2734 <span class="k">use</span> <span class="nx">wcf\data\person\PersonAction</span><span class="p">;</span>
2735 <span class="k">use</span> <span class="nx">wcf\form\AbstractForm</span><span class="p">;</span>
2736 <span class="k">use</span> <span class="nx">wcf\system\exception\UserInputException</span><span class="p">;</span>
2737 <span class="k">use</span> <span class="nx">wcf\system\request\LinkHandler</span><span class="p">;</span>
2738 <span class="k">use</span> <span class="nx">wcf\system\WCF</span><span class="p">;</span>
2739 <span class="k">use</span> <span class="nx">wcf\util\StringUtil</span><span class="p">;</span>
2740
2741 <span class="sd">/**</span>
2742 <span class="sd"> * Shows the form to create a new person.</span>
2743 <span class="sd"> * </span>
2744 <span class="sd"> * @author Matthias Schmidt</span>
2745 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2746 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2747 <span class="sd"> * @package WoltLabSuite\Core\Acp\Form</span>
2748 <span class="sd"> */</span>
2749 <span class="k">class</span> <span class="nc">PersonAddForm</span> <span class="k">extends</span> <span class="nx">AbstractForm</span> <span class="p">{</span>
2750 <span class="sd">/**</span>
2751 <span class="sd"> * @inheritDoc</span>
2752 <span class="sd"> */</span>
2753 <span class="k">public</span> <span class="nv">$activeMenuItem</span> <span class="o">=</span> <span class="s1">&#39;wcf.acp.menu.link.person.add&#39;</span><span class="p">;</span>
2754
2755 <span class="sd">/**</span>
2756 <span class="sd"> * first name of the person</span>
2757 <span class="sd"> * @var string</span>
2758 <span class="sd"> */</span>
2759 <span class="k">public</span> <span class="nv">$firstName</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2760
2761 <span class="sd">/**</span>
2762 <span class="sd"> * last name of the person</span>
2763 <span class="sd"> * @var string</span>
2764 <span class="sd"> */</span>
2765 <span class="k">public</span> <span class="nv">$lastName</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2766
2767 <span class="sd">/**</span>
2768 <span class="sd"> * @inheritDoc</span>
2769 <span class="sd"> */</span>
2770 <span class="k">public</span> <span class="nv">$neededPermissions</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;admin.content.canManagePeople&#39;</span><span class="p">];</span>
2771
2772 <span class="sd">/**</span>
2773 <span class="sd"> * @inheritDoc</span>
2774 <span class="sd"> */</span>
2775 <span class="k">public</span> <span class="k">function</span> <span class="nf">assignVariables</span><span class="p">()</span> <span class="p">{</span>
2776 <span class="k">parent</span><span class="o">::</span><span class="na">assignVariables</span><span class="p">();</span>
2777
2778 <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>
2779 <span class="s1">&#39;action&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;add&#39;</span><span class="p">,</span>
2780 <span class="s1">&#39;firstName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="p">,</span>
2781 <span class="s1">&#39;lastName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span>
2782 <span class="p">]);</span>
2783 <span class="p">}</span>
2784
2785 <span class="sd">/**</span>
2786 <span class="sd"> * @inheritDoc</span>
2787 <span class="sd"> */</span>
2788 <span class="k">public</span> <span class="k">function</span> <span class="nf">readFormParameters</span><span class="p">()</span> <span class="p">{</span>
2789 <span class="k">parent</span><span class="o">::</span><span class="na">readFormParameters</span><span class="p">();</span>
2790
2791 <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;firstName&#39;</span><span class="p">]))</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</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;firstName&#39;</span><span class="p">]);</span>
2792 <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;lastName&#39;</span><span class="p">]))</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</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;lastName&#39;</span><span class="p">]);</span>
2793 <span class="p">}</span>
2794
2795 <span class="sd">/**</span>
2796 <span class="sd"> * @inheritDoc</span>
2797 <span class="sd"> */</span>
2798 <span class="k">public</span> <span class="k">function</span> <span class="nf">save</span><span class="p">()</span> <span class="p">{</span>
2799 <span class="k">parent</span><span class="o">::</span><span class="na">save</span><span class="p">();</span>
2800
2801 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">objectAction</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PersonAction</span><span class="p">([],</span> <span class="s1">&#39;create&#39;</span><span class="p">,</span> <span class="p">[</span>
2802 <span class="s1">&#39;data&#39;</span> <span class="o">=&gt;</span> <span class="nb">array_merge</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">additionalFields</span><span class="p">,</span> <span class="p">[</span>
2803 <span class="s1">&#39;firstName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="p">,</span>
2804 <span class="s1">&#39;lastName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span>
2805 <span class="p">])</span>
2806 <span class="p">]);</span>
2807 <span class="nv">$returnValues</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">objectAction</span><span class="o">-&gt;</span><span class="na">executeAction</span><span class="p">();</span>
2808
2809 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">saved</span><span class="p">();</span>
2810
2811 <span class="c1">// reset values</span>
2812 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2813 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="p">;</span>
2814
2815 <span class="c1">// show success message</span>
2816 <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>
2817 <span class="s1">&#39;success&#39;</span> <span class="o">=&gt;</span> <span class="k">true</span><span class="p">,</span>
2818 <span class="s1">&#39;objectEditLink&#39;</span> <span class="o">=&gt;</span> <span class="nx">LinkHandler</span><span class="o">::</span><span class="na">getInstance</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">getControllerLink</span><span class="p">(</span><span class="nx">PersonEditForm</span><span class="o">::</span><span class="na">class</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;id&#39;</span> <span class="o">=&gt;</span> <span class="nv">$returnValues</span><span class="p">[</span><span class="s1">&#39;returnValues&#39;</span><span class="p">]</span><span class="o">-&gt;</span><span class="na">personID</span><span class="p">]),</span>
2819 <span class="p">]);</span>
2820 <span class="p">}</span>
2821
2822 <span class="sd">/**</span>
2823 <span class="sd"> * @inheritDoc</span>
2824 <span class="sd"> */</span>
2825 <span class="k">public</span> <span class="k">function</span> <span class="nf">validate</span><span class="p">()</span> <span class="p">{</span>
2826 <span class="k">parent</span><span class="o">::</span><span class="na">validate</span><span class="p">();</span>
2827
2828 <span class="c1">// validate first name</span>
2829 <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">firstName</span><span class="p">))</span> <span class="p">{</span>
2830 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;firstName&#39;</span><span class="p">);</span>
2831 <span class="p">}</span>
2832 <span class="k">if</span> <span class="p">(</span><span class="nb">mb_strlen</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">)</span> <span class="p">{</span>
2833 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;firstName&#39;</span><span class="p">,</span> <span class="s1">&#39;tooLong&#39;</span><span class="p">);</span>
2834 <span class="p">}</span>
2835
2836 <span class="c1">// validate last name</span>
2837 <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">lastName</span><span class="p">))</span> <span class="p">{</span>
2838 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;lastName&#39;</span><span class="p">);</span>
2839 <span class="p">}</span>
2840 <span class="k">if</span> <span class="p">(</span><span class="nb">mb_strlen</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">)</span> <span class="p">{</span>
2841 <span class="k">throw</span> <span class="k">new</span> <span class="nx">UserInputException</span><span class="p">(</span><span class="s1">&#39;lastName&#39;</span><span class="p">,</span> <span class="s1">&#39;tooLong&#39;</span><span class="p">);</span>
2842 <span class="p">}</span>
2843 <span class="p">}</span>
2844 <span class="p">}</span>
2845 </code></pre></div>
2846 <p>The properties here consist of two types:
2847 the “housekeeping” properties <code>$activeMenuItem</code> and <code>$neededPermissions</code>, which fulfill the same roles as for <code>PersonListPage</code>, and the “data” properties <code>$firstName</code> and <code>$lastName</code>, which will contain the data entered by the user of the person to be created.</p>
2848 <p>Now, let's go through each method in execution order:</p>
2849 <ol>
2850 <li><code>readFormParameters()</code> is called after the form has been submitted and reads the entered first and last name and sanitizes the values by calling <code>StringUtil::trim()</code>.</li>
2851 <li><code>validate()</code> is called after the form has been submitted and is used to validate the input data.
2852 In case of invalid data, the method is expected to throw a <code>UserInputException</code>.
2853 Here, the validation for first and last name is the same and quite basic:
2854 We check that any name has been entered and that it is not longer than the database table column permits.</li>
2855 <li><code>save()</code> is called after the form has been submitted and the entered data has been validated and it creates the new person via <code>PersonAction</code>.
2856 Please note that we do not just pass the first and last name to the action object but merge them with the <code>$this-&gt;additionalFields</code> array which can be used by event listeners of plugins to add additional data.
2857 After creating the object, the <code>saved()</code> method is called which fires an event for plugins and the data properties are cleared so that the input fields on the page are empty so that another new person can be created.
2858 Lastly, a <code>success</code> variable is assigned to the template which will show a message that the person has been successfully created.</li>
2859 <li><code>assignVariables()</code> assigns the values of the “data” properties to the template and additionally assigns an <code>action</code> variable.
2860 This <code>action</code> variable will be used in the template to distinguish between adding a new person and editing an existing person so that which minimal adjustments, we can use the template for both cases.</li>
2861 </ol>
2862 <h4 id="personaddtpl"><code>personAdd.tpl</code><a class="headerlink" href="#personaddtpl" title="Permanent link">#</a></h4>
2863 <div class="highlight"><pre><span></span><code><span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;header&#39;</span> <span class="na">pageTitle</span><span class="o">=</span><span class="s1">&#39;wcf.acp.person.&#39;</span><span class="o">|</span><span class="na">concat</span><span class="o">:</span><span class="nv">$action</span><span class="cp">}</span><span class="x"></span>
2864
2865 <span class="x">&lt;header class=&quot;contentHeader&quot;&gt;</span>
2866 <span class="x"> &lt;div class=&quot;contentHeaderTitle&quot;&gt;</span>
2867 <span class="x"> &lt;h1 class=&quot;contentTitle&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.person.</span><span class="cp">{</span><span class="nv">$action</span><span class="cp">}{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/h1&gt;</span>
2868 <span class="x"> &lt;/div&gt;</span>
2869
2870 <span class="x"> &lt;nav class=&quot;contentHeaderNavigation&quot;&gt;</span>
2871 <span class="x"> &lt;ul&gt;</span>
2872 <span class="x"> &lt;li&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="nf">/link</span><span class="cp">}</span><span class="x">&quot; class=&quot;button&quot;&gt;&lt;span class=&quot;icon icon16 fa-list&quot;&gt;&lt;/span&gt; &lt;span&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.menu.link.person.list</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;</span>
2873
2874 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;contentHeaderNavigation&#39;</span><span class="cp">}</span><span class="x"></span>
2875 <span class="x"> &lt;/ul&gt;</span>
2876 <span class="x"> &lt;/nav&gt;</span>
2877 <span class="x">&lt;/header&gt;</span>
2878
2879 <span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;formNotice&#39;</span><span class="cp">}</span><span class="x"></span>
2880
2881 <span class="x">&lt;form method=&quot;post&quot; action=&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$action</span> <span class="o">==</span> <span class="s1">&#39;add&#39;</span><span class="cp">}{</span><span class="nf">link</span> <span class="na">controller</span><span class="o">=</span><span class="s1">&#39;PersonAdd&#39;</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}{</span><span class="nf">else</span><span class="cp">}{</span><span class="nf">link</span> <span class="na">controller</span><span class="o">=</span><span class="s1">&#39;PersonEdit&#39;</span> <span class="na">object</span><span class="o">=</span><span class="nv">$person</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&quot;&gt;</span>
2882 <span class="x"> &lt;div class=&quot;section&quot;&gt;</span>
2883 <span class="x"> &lt;dl</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorField</span> <span class="o">==</span> <span class="s1">&#39;firstName&#39;</span><span class="cp">}</span><span class="x"> class=&quot;formError&quot;</span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&gt;</span>
2884 <span class="x"> &lt;dt&gt;&lt;label for=&quot;firstName&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.firstName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/label&gt;&lt;/dt&gt;</span>
2885 <span class="x"> &lt;dd&gt;</span>
2886 <span class="x"> &lt;input type=&quot;text&quot; id=&quot;firstName&quot; name=&quot;firstName&quot; value=&quot;</span><span class="cp">{</span><span class="nv">$firstName</span><span class="cp">}</span><span class="x">&quot; required autofocus maxlength=&quot;255&quot; class=&quot;long&quot;&gt;</span>
2887 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorField</span> <span class="o">==</span> <span class="s1">&#39;firstName&#39;</span><span class="cp">}</span><span class="x"></span>
2888 <span class="x"> &lt;small class=&quot;innerError&quot;&gt;</span>
2889 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorType</span> <span class="o">==</span> <span class="s1">&#39;empty&#39;</span><span class="cp">}</span><span class="x"></span>
2890 <span class="x"> </span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.form.error.empty</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x"></span>
2891 <span class="x"> </span><span class="cp">{</span><span class="nf">else</span><span class="cp">}</span><span class="x"></span>
2892 <span class="x"> </span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.person.firstName.error.</span><span class="cp">{</span><span class="nv">$errorType</span><span class="cp">}{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x"></span>
2893 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2894 <span class="x"> &lt;/small&gt;</span>
2895 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2896 <span class="x"> &lt;/dd&gt;</span>
2897 <span class="x"> &lt;/dl&gt;</span>
2898
2899 <span class="x"> &lt;dl</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorField</span> <span class="o">==</span> <span class="s1">&#39;lastName&#39;</span><span class="cp">}</span><span class="x"> class=&quot;formError&quot;</span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x">&gt;</span>
2900 <span class="x"> &lt;dt&gt;&lt;label for=&quot;lastName&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.lastName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/label&gt;&lt;/dt&gt;</span>
2901 <span class="x"> &lt;dd&gt;</span>
2902 <span class="x"> &lt;input type=&quot;text&quot; id=&quot;lastName&quot; name=&quot;lastName&quot; value=&quot;</span><span class="cp">{</span><span class="nv">$lastName</span><span class="cp">}</span><span class="x">&quot; required maxlength=&quot;255&quot; class=&quot;long&quot;&gt;</span>
2903 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorField</span> <span class="o">==</span> <span class="s1">&#39;lastName&#39;</span><span class="cp">}</span><span class="x"></span>
2904 <span class="x"> &lt;small class=&quot;innerError&quot;&gt;</span>
2905 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$errorType</span> <span class="o">==</span> <span class="s1">&#39;empty&#39;</span><span class="cp">}</span><span class="x"></span>
2906 <span class="x"> </span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.form.error.empty</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x"></span>
2907 <span class="x"> </span><span class="cp">{</span><span class="nf">else</span><span class="cp">}</span><span class="x"></span>
2908 <span class="x"> </span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.acp.person.lastName.error.</span><span class="cp">{</span><span class="nv">$errorType</span><span class="cp">}{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x"></span>
2909 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2910 <span class="x"> &lt;/small&gt;</span>
2911 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
2912 <span class="x"> &lt;/dd&gt;</span>
2913 <span class="x"> &lt;/dl&gt;</span>
2914
2915 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;dataFields&#39;</span><span class="cp">}</span><span class="x"></span>
2916 <span class="x"> &lt;/div&gt;</span>
2917
2918 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;sections&#39;</span><span class="cp">}</span><span class="x"></span>
2919
2920 <span class="x"> &lt;div class=&quot;formSubmit&quot;&gt;</span>
2921 <span class="x"> &lt;input type=&quot;submit&quot; value=&quot;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.button.submit</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&quot; accesskey=&quot;s&quot;&gt;</span>
2922 <span class="x"> </span><span class="cp">{</span><span class="nf">csrfToken</span><span class="cp">}</span><span class="x"></span>
2923 <span class="x"> &lt;/div&gt;</span>
2924 <span class="x">&lt;/form&gt;</span>
2925
2926 <span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;footer&#39;</span><span class="cp">}</span><span class="x"></span>
2927 </code></pre></div>
2928 <p>We will now only concentrate on the new parts compared to <code>personList.tpl</code>:</p>
2929 <ol>
2930 <li>We use the <code>$action</code> variable to distinguish between the languages items used for adding a person and for creating a person.</li>
2931 <li>Including the <code>formError</code> template automatically shows an error message if the validation failed.</li>
2932 <li>The <code>.success</code> element is shown after successful saving the data and, again, shows different a text depending on the executed action.</li>
2933 <li>The main part is the <code>form</code> element which has a common structure you will find in many forms in WoltLab Suite Core.
2934 The notable parts here are:</li>
2935 <li>The <code>action</code> attribute of the <code>form</code> element is set depending on which controller will handle the request.
2936 In the link for the edit controller, we can now simply pass the edited <code>Person</code> object directly as the <code>Person</code> class implements the <code>IRouteController</code> interface.</li>
2937 <li>The field that caused the validation error can be accessed via <code>$errorField</code>.</li>
2938 <li>The type of the validation error can be accessed via <code>$errorType</code>.
2939 For an empty input field, we show the generic <code>wcf.global.form.error.empty</code> language item.
2940 In all other cases, we use the error type to determine the object- and property-specific language item to show.
2941 The approach used here allows plugins to easily add further validation error messages by simply using a different error type and providing the associated language item.</li>
2942 <li>Input fields can be grouped into different <code>.section</code> elements.
2943 At the end of each <code>.section</code> element, there should be an template event whose name ends with <code>Fields</code>.
2944 The first part of the event name should reflect the type of fields in the particular <code>.section</code> element.
2945 Here, the input fields are just general “data” fields so that the event is called <code>dataFields</code>.</li>
2946 <li>After the last <code>.section</code> element, fire a <code>section</code> event so that plugins can add further sections.</li>
2947 <li>Lastly, the <code>.formSubmit</code> shows the submit button and <code>{csrfToken}</code> contains a CSRF token that is automatically validated after the form is submitted.</li>
2948 </ol>
2949 <h3 id="person-edit-form">Person Edit Form<a class="headerlink" href="#person-edit-form" title="Permanent link">#</a></h3>
2950 <p>As mentioned before, for the form to edit existing people, we only need a new controller as the template has already been implemented in a way that it handles both, adding and editing.</p>
2951 <h4 id="personeditform"><code>PersonEditForm</code><a class="headerlink" href="#personeditform" title="Permanent link">#</a></h4>
2952 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
2953 <span class="k">namespace</span> <span class="nx">wcf\acp\form</span><span class="p">;</span>
2954 <span class="k">use</span> <span class="nx">wcf\data\person\Person</span><span class="p">;</span>
2955 <span class="k">use</span> <span class="nx">wcf\data\person\PersonAction</span><span class="p">;</span>
2956 <span class="k">use</span> <span class="nx">wcf\form\AbstractForm</span><span class="p">;</span>
2957 <span class="k">use</span> <span class="nx">wcf\system\exception\IllegalLinkException</span><span class="p">;</span>
2958 <span class="k">use</span> <span class="nx">wcf\system\WCF</span><span class="p">;</span>
2959
2960 <span class="sd">/**</span>
2961 <span class="sd"> * Shows the form to edit an existing person.</span>
2962 <span class="sd"> * </span>
2963 <span class="sd"> * @author Matthias Schmidt</span>
2964 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
2965 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
2966 <span class="sd"> * @package WoltLabSuite\Core\Acp\Form</span>
2967 <span class="sd"> */</span>
2968 <span class="k">class</span> <span class="nc">PersonEditForm</span> <span class="k">extends</span> <span class="nx">PersonAddForm</span> <span class="p">{</span>
2969 <span class="sd">/**</span>
2970 <span class="sd"> * @inheritDoc</span>
2971 <span class="sd"> */</span>
2972 <span class="k">public</span> <span class="nv">$activeMenuItem</span> <span class="o">=</span> <span class="s1">&#39;wcf.acp.menu.link.person&#39;</span><span class="p">;</span>
2973
2974 <span class="sd">/**</span>
2975 <span class="sd"> * edited person object</span>
2976 <span class="sd"> * @var Person</span>
2977 <span class="sd"> */</span>
2978 <span class="k">public</span> <span class="nv">$person</span> <span class="o">=</span> <span class="k">null</span><span class="p">;</span>
2979
2980 <span class="sd">/**</span>
2981 <span class="sd"> * id of the edited person</span>
2982 <span class="sd"> * @var integer</span>
2983 <span class="sd"> */</span>
2984 <span class="k">public</span> <span class="nv">$personID</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
2985
2986 <span class="sd">/**</span>
2987 <span class="sd"> * @inheritDoc</span>
2988 <span class="sd"> */</span>
2989 <span class="k">public</span> <span class="k">function</span> <span class="nf">assignVariables</span><span class="p">()</span> <span class="p">{</span>
2990 <span class="k">parent</span><span class="o">::</span><span class="na">assignVariables</span><span class="p">();</span>
2991
2992 <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>
2993 <span class="s1">&#39;action&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;edit&#39;</span><span class="p">,</span>
2994 <span class="s1">&#39;person&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span>
2995 <span class="p">]);</span>
2996 <span class="p">}</span>
2997
2998 <span class="sd">/**</span>
2999 <span class="sd"> * @inheritDoc</span>
3000 <span class="sd"> */</span>
3001 <span class="k">public</span> <span class="k">function</span> <span class="nf">readData</span><span class="p">()</span> <span class="p">{</span>
3002 <span class="k">parent</span><span class="o">::</span><span class="na">readData</span><span class="p">();</span>
3003
3004 <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>
3005 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="p">;</span>
3006 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span><span class="o">-&gt;</span><span class="na">lastName</span><span class="p">;</span>
3007 <span class="p">}</span>
3008 <span class="p">}</span>
3009
3010 <span class="sd">/**</span>
3011 <span class="sd"> * @inheritDoc</span>
3012 <span class="sd"> */</span>
3013 <span class="k">public</span> <span class="k">function</span> <span class="nf">readParameters</span><span class="p">()</span> <span class="p">{</span>
3014 <span class="k">parent</span><span class="o">::</span><span class="na">readParameters</span><span class="p">();</span>
3015
3016 <span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_REQUEST</span><span class="p">[</span><span class="s1">&#39;id&#39;</span><span class="p">]))</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">personID</span> <span class="o">=</span> <span class="nb">intval</span><span class="p">(</span><span class="nv">$_REQUEST</span><span class="p">[</span><span class="s1">&#39;id&#39;</span><span class="p">]);</span>
3017 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Person</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">personID</span><span class="p">);</span>
3018 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span><span class="o">-&gt;</span><span class="na">personID</span><span class="p">)</span> <span class="p">{</span>
3019 <span class="k">throw</span> <span class="k">new</span> <span class="nx">IllegalLinkException</span><span class="p">();</span>
3020 <span class="p">}</span>
3021 <span class="p">}</span>
3022
3023 <span class="sd">/**</span>
3024 <span class="sd"> * @inheritDoc</span>
3025 <span class="sd"> */</span>
3026 <span class="k">public</span> <span class="k">function</span> <span class="nf">save</span><span class="p">()</span> <span class="p">{</span>
3027 <span class="nx">AbstractForm</span><span class="o">::</span><span class="na">save</span><span class="p">();</span>
3028
3029 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">objectAction</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PersonAction</span><span class="p">([</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">person</span><span class="p">],</span> <span class="s1">&#39;update&#39;</span><span class="p">,</span> <span class="p">[</span>
3030 <span class="s1">&#39;data&#39;</span> <span class="o">=&gt;</span> <span class="nb">array_merge</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">additionalFields</span><span class="p">,</span> <span class="p">[</span>
3031 <span class="s1">&#39;firstName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">firstName</span><span class="p">,</span>
3032 <span class="s1">&#39;lastName&#39;</span> <span class="o">=&gt;</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">lastName</span>
3033 <span class="p">])</span>
3034 <span class="p">]);</span>
3035 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">objectAction</span><span class="o">-&gt;</span><span class="na">executeAction</span><span class="p">();</span>
3036
3037 <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">saved</span><span class="p">();</span>
3038
3039 <span class="c1">// show success message</span>
3040 <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;success&#39;</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
3041 <span class="p">}</span>
3042 <span class="p">}</span>
3043 </code></pre></div>
3044 <p>In general, edit forms extend the associated add form so that the code to read and to validate the input data is simply inherited.</p>
3045 <p>After setting a different active menu item, we declare two new properties for the edited person:
3046 the id of the person passed in the URL is stored in <code>$personID</code> and based on this ID, a <code>Person</code> object is created that is stored in the <code>$person</code> property.</p>
3047 <p>Now let use go through the different methods in chronological order again:</p>
3048 <ol>
3049 <li><code>readParameters()</code> reads the passed ID of the edited person and creates a <code>Person</code> object based on this ID.
3050 If the ID is invalid, <code>$this-&gt;person-&gt;personID</code> is <code>null</code> and an <code>IllegalLinkException</code> is thrown.</li>
3051 <li><code>readData()</code> only executes additional code in the case if <code>$_POST</code> is empty, thus only for the initial request before the form has been submitted.
3052 The data properties of <code>PersonAddForm</code> are populated with the data of the edited person so that this data is shown in the form for the initial request.</li>
3053 <li><code>save()</code> handles saving the changed data.</li>
3054 </ol>
3055 <p>!!! warning "Do not call <code>parent::save()</code> because that would cause <code>PersonAddForm::save()</code> to be executed and thus a new person would to be created! In order for the <code>save</code> event to be fired, call <code>AbstractForm::save()</code> instead!"</p>
3056 <p>The only differences compared to <code>PersonAddForm::save()</code> are that we pass the edited object to the <code>PersonAction</code> constructor, execute the <code>update</code> action instead of the <code>create</code> action and do not clear the input fields after saving the changes.
3057 1. In <code>assignVariables()</code>, we assign the edited <code>Person</code> object to the template, which is required to create the link in the form’s action property.
3058 Furthermore, we assign the template variable <code>$action</code> <code>edit</code> as value.</p>
3059 <p>!!! info "After calling <code>parent::assignVariables()</code>, the template variable <code>$action</code> actually has the value <code>add</code> so that here, we are overwriting this already assigned value."</p>
3060 <h2 id="frontend">Frontend<a class="headerlink" href="#frontend" title="Permanent link">#</a></h2>
3061 <p>For the front end, that means the part with which the visitors of a website interact, we want to implement a simple sortable page that lists the people.
3062 This page should also be directly linked in the main menu.</p>
3063 <h3 id="pagexml"><code>page.xml</code><a class="headerlink" href="#pagexml" title="Permanent link">#</a></h3>
3064 <p>First, let us register the page with the system because every front end page or form needs to be explicitly registered using the <a href="../../../package/pip/page/">page package installation plugin</a>:</p>
3065 <div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
3066 <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/page.xsd&quot;</span><span class="nt">&gt;</span>
3067 <span class="nt">&lt;import&gt;</span>
3068 <span class="nt">&lt;page</span> <span class="na">identifier=</span><span class="s">&quot;com.woltlab.wcf.people.PersonList&quot;</span><span class="nt">&gt;</span>
3069 <span class="nt">&lt;pageType&gt;</span>system<span class="nt">&lt;/pageType&gt;</span>
3070 <span class="nt">&lt;controller&gt;</span>wcf\page\PersonListPage<span class="nt">&lt;/controller&gt;</span>
3071 <span class="nt">&lt;name</span> <span class="na">language=</span><span class="s">&quot;de&quot;</span><span class="nt">&gt;</span>Personen-Liste<span class="nt">&lt;/name&gt;</span>
3072 <span class="nt">&lt;name</span> <span class="na">language=</span><span class="s">&quot;en&quot;</span><span class="nt">&gt;</span>Person List<span class="nt">&lt;/name&gt;</span>
3073
3074 <span class="nt">&lt;content</span> <span class="na">language=</span><span class="s">&quot;de&quot;</span><span class="nt">&gt;</span>
3075 <span class="nt">&lt;title&gt;</span>Personen<span class="nt">&lt;/title&gt;</span>
3076 <span class="nt">&lt;/content&gt;</span>
3077 <span class="nt">&lt;content</span> <span class="na">language=</span><span class="s">&quot;en&quot;</span><span class="nt">&gt;</span>
3078 <span class="nt">&lt;title&gt;</span>People<span class="nt">&lt;/title&gt;</span>
3079 <span class="nt">&lt;/content&gt;</span>
3080 <span class="nt">&lt;/page&gt;</span>
3081 <span class="nt">&lt;/import&gt;</span>
3082 <span class="nt">&lt;/data&gt;</span>
3083 </code></pre></div>
3084 <p>For more information about what each of the elements means, please refer to the <a href="../../../package/pip/page/">page package installation plugin page</a>.</p>
3085 <h3 id="menuitemxml"><code>menuItem.xml</code><a class="headerlink" href="#menuitemxml" title="Permanent link">#</a></h3>
3086 <p>Next, we register the menu item using the <a href="../../../package/pip/menu-item/">menuItem package installation plugin</a>:</p>
3087 <div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
3088 <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/menuItem.xsd&quot;</span><span class="nt">&gt;</span>
3089 <span class="nt">&lt;import&gt;</span>
3090 <span class="nt">&lt;item</span> <span class="na">identifier=</span><span class="s">&quot;com.woltlab.wcf.people.PersonList&quot;</span><span class="nt">&gt;</span>
3091 <span class="nt">&lt;menu&gt;</span>com.woltlab.wcf.MainMenu<span class="nt">&lt;/menu&gt;</span>
3092 <span class="nt">&lt;title</span> <span class="na">language=</span><span class="s">&quot;de&quot;</span><span class="nt">&gt;</span>Personen<span class="nt">&lt;/title&gt;</span>
3093 <span class="nt">&lt;title</span> <span class="na">language=</span><span class="s">&quot;en&quot;</span><span class="nt">&gt;</span>People<span class="nt">&lt;/title&gt;</span>
3094 <span class="nt">&lt;page&gt;</span>com.woltlab.wcf.people.PersonList<span class="nt">&lt;/page&gt;</span>
3095 <span class="nt">&lt;/item&gt;</span>
3096 <span class="nt">&lt;/import&gt;</span>
3097 <span class="nt">&lt;/data&gt;</span>
3098 </code></pre></div>
3099 <p>Here, the import parts are that we register the menu item for the main menu <code>com.woltlab.wcf.MainMenu</code> and link the menu item with the page <code>com.woltlab.wcf.people.PersonList</code>, which we just registered.</p>
3100 <h3 id="people-list_1">People List<a class="headerlink" href="#people-list_1" title="Permanent link">#</a></h3>
3101 <p>As in the ACP, we need a controller and a template.
3102 You might notice that both the controller’s (unqualified) class name and the template name are the same for the ACP and the front end.
3103 This is no problem because the qualified names of the classes differ and the files are stored in different directories and because the templates are installed by different package installation plugins and are also stored in different directories.</p>
3104 <h4 id="personlistpage_1"><code>PersonListPage</code><a class="headerlink" href="#personlistpage_1" title="Permanent link">#</a></h4>
3105 <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span>
3106 <span class="k">namespace</span> <span class="nx">wcf\page</span><span class="p">;</span>
3107 <span class="k">use</span> <span class="nx">wcf\data\person\PersonList</span><span class="p">;</span>
3108
3109 <span class="sd">/**</span>
3110 <span class="sd"> * Shows the list of people.</span>
3111 <span class="sd"> * </span>
3112 <span class="sd"> * @author Matthias Schmidt</span>
3113 <span class="sd"> * @copyright 2001-2019 WoltLab GmbH</span>
3114 <span class="sd"> * @license GNU Lesser General Public License &lt;http://opensource.org/licenses/lgpl-license.php&gt;</span>
3115 <span class="sd"> * @package WoltLabSuite\Core\Page</span>
3116 <span class="sd"> */</span>
3117 <span class="k">class</span> <span class="nc">PersonListPage</span> <span class="k">extends</span> <span class="nx">SortablePage</span> <span class="p">{</span>
3118 <span class="sd">/**</span>
3119 <span class="sd"> * @inheritDoc</span>
3120 <span class="sd"> */</span>
3121 <span class="k">public</span> <span class="nv">$defaultSortField</span> <span class="o">=</span> <span class="s1">&#39;lastName&#39;</span><span class="p">;</span>
3122
3123 <span class="sd">/**</span>
3124 <span class="sd"> * @inheritDoc</span>
3125 <span class="sd"> */</span>
3126 <span class="k">public</span> <span class="nv">$objectListClassName</span> <span class="o">=</span> <span class="nx">PersonList</span><span class="o">::</span><span class="na">class</span><span class="p">;</span>
3127
3128 <span class="sd">/**</span>
3129 <span class="sd"> * @inheritDoc</span>
3130 <span class="sd"> */</span>
3131 <span class="k">public</span> <span class="nv">$validSortFields</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;personID&#39;</span><span class="p">,</span> <span class="s1">&#39;firstName&#39;</span><span class="p">,</span> <span class="s1">&#39;lastName&#39;</span><span class="p">];</span>
3132 <span class="p">}</span>
3133 </code></pre></div>
3134 <p>This class is almost identical to the ACP version.
3135 In the front end, we do not need to set the active menu item manually because the system determines the active menu item automatically based on the requested page.
3136 Furthermore, <code>$neededPermissions</code> has not been set because in the front end, users do not need any special permission to access the page.
3137 In the front end, we explicitly set the <code>$defaultSortField</code> so that the people listed on the page are sorted by their last name (in ascending order) by default.</p>
3138 <h4 id="personlisttpl_1"><code>personList.tpl</code><a class="headerlink" href="#personlisttpl_1" title="Permanent link">#</a></h4>
3139 <div class="highlight"><pre><span></span><code><span class="cp">{</span><span class="nf">capture</span> <span class="na">assign</span><span class="o">=</span><span class="s1">&#39;contentTitle&#39;</span><span class="cp">}{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.person.list</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x"> &lt;span class=&quot;badge&quot;&gt;</span><span class="cp">{</span><span class="err">#</span><span class="nv">$items</span><span class="cp">}</span><span class="x">&lt;/span&gt;</span><span class="cp">{</span><span class="nf">/capture</span><span class="cp">}</span><span class="x"></span>
3140
3141 <span class="cp">{</span><span class="nf">capture</span> <span class="na">assign</span><span class="o">=</span><span class="s1">&#39;headContent&#39;</span><span class="cp">}</span><span class="x"></span>
3142 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$pageNo</span> <span class="o">&lt;</span> <span class="nv">$pages</span><span class="cp">}</span><span class="x"></span>
3143 <span class="x"> &lt;link rel=&quot;next&quot; 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="o">+</span><span class="m">1</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot;&gt;</span>
3144 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
3145 <span class="x"> </span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$pageNo</span> <span class="o">&gt;</span> <span class="m">1</span><span class="cp">}</span><span class="x"></span>
3146 <span class="x"> &lt;link rel=&quot;prev&quot; 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="nf">if</span> <span class="nv">$pageNo</span> <span class="o">&gt;</span> <span class="m">2</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="o">-</span><span class="m">1</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>
3147 <span class="x"> </span><span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
3148 <span class="x"> &lt;link rel=&quot;canonical&quot; 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="nf">if</span> <span class="nv">$pageNo</span> <span class="o">&gt;</span> <span class="m">1</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="nf">/if</span><span class="cp">}{</span><span class="nf">/link</span><span class="cp">}</span><span class="x">&quot;&gt;</span>
3149 <span class="cp">{</span><span class="nf">/capture</span><span class="cp">}</span><span class="x"></span>
3150
3151 <span class="cp">{</span><span class="nf">capture</span> <span class="na">assign</span><span class="o">=</span><span class="s1">&#39;sidebarRight&#39;</span><span class="cp">}</span><span class="x"></span>
3152 <span class="x"> &lt;section class=&quot;box&quot;&gt;</span>
3153 <span class="x"> &lt;form method=&quot;post&quot; action=&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="nf">/link</span><span class="cp">}</span><span class="x">&quot;&gt;</span>
3154 <span class="x"> &lt;h2 class=&quot;boxTitle&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.sorting</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/h2&gt;</span>
3155
3156 <span class="x"> &lt;div class=&quot;boxContent&quot;&gt;</span>
3157 <span class="x"> &lt;dl&gt;</span>
3158 <span class="x"> &lt;dt&gt;&lt;/dt&gt;</span>
3159 <span class="x"> &lt;dd&gt;</span>
3160 <span class="x"> &lt;select id=&quot;sortField&quot; name=&quot;sortField&quot;&gt;</span>
3161 <span class="x"> &lt;option value=&quot;firstName&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;firstName&#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.firstName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/option&gt;</span>
3162 <span class="x"> &lt;option value=&quot;lastName&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortField</span> <span class="o">==</span> <span class="s1">&#39;lastName&#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.lastName</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/option&gt;</span>
3163 <span class="x"> </span><span class="cp">{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;sortField&#39;</span><span class="cp">}</span><span class="x"></span>
3164 <span class="x"> &lt;/select&gt;</span>
3165 <span class="x"> &lt;select name=&quot;sortOrder&quot;&gt;</span>
3166 <span class="x"> &lt;option value=&quot;ASC&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortOrder</span> <span class="o">==</span> <span class="s1">&#39;ASC&#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.global.sortOrder.ascending</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/option&gt;</span>
3167 <span class="x"> &lt;option value=&quot;DESC&quot;</span><span class="cp">{</span><span class="nf">if</span> <span class="nv">$sortOrder</span> <span class="o">==</span> <span class="s1">&#39;DESC&#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.global.sortOrder.descending</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/option&gt;</span>
3168 <span class="x"> &lt;/select&gt;</span>
3169 <span class="x"> &lt;/dd&gt;</span>
3170 <span class="x"> &lt;/dl&gt;</span>
3171
3172 <span class="x"> &lt;div class=&quot;formSubmit&quot;&gt;</span>
3173 <span class="x"> &lt;input type=&quot;submit&quot; value=&quot;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.button.submit</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&quot; accesskey=&quot;s&quot;&gt;</span>
3174 <span class="x"> &lt;/div&gt;</span>
3175 <span class="x"> &lt;/div&gt;</span>
3176 <span class="x"> &lt;/form&gt;</span>
3177 <span class="x"> &lt;/section&gt;</span>
3178 <span class="cp">{</span><span class="nf">/capture</span><span class="cp">}</span><span class="x"></span>
3179
3180 <span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;header&#39;</span><span class="cp">}</span><span class="x"></span>
3181
3182 <span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
3183 <span class="x"> &lt;div class=&quot;paginationTop&quot;&gt;</span>
3184 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}</span><span class="x"></span>
3185 <span class="x"> </span><span class="cp">{</span><span class="nf">pages</span> <span class="na">print</span><span class="o">=</span><span class="kc">true</span> <span class="na">assign</span><span class="o">=</span><span class="na">pagesLinks</span> <span class="na">controller</span><span class="o">=</span><span class="s1">&#39;PersonList&#39;</span> <span class="na">link</span><span class="o">=</span><span class="s2">&quot;pageNo=%d&amp;sortField=$sortField&amp;sortOrder=$sortOrder&quot;</span><span class="cp">}</span><span class="x"></span>
3186 <span class="x"> </span><span class="cp">{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
3187 <span class="x"> &lt;/div&gt;</span>
3188 <span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
3189
3190 <span class="cp">{</span><span class="nf">if</span> <span class="nv">$items</span><span class="cp">}</span><span class="x"></span>
3191 <span class="x"> &lt;div class=&quot;section sectionContainerList&quot;&gt;</span>
3192 <span class="x"> &lt;ol class=&quot;containerList personList&quot;&gt;</span>
3193 <span class="x"> </span><span class="cp">{</span><span class="nf">foreach</span> <span class="na">from</span><span class="o">=</span><span class="nv">$objects</span> <span class="na">item</span><span class="o">=</span><span class="na">person</span><span class="cp">}</span><span class="x"></span>
3194 <span class="x"> &lt;li&gt;</span>
3195 <span class="x"> &lt;div class=&quot;box48&quot;&gt;</span>
3196 <span class="x"> &lt;span class=&quot;icon icon48 fa-user&quot;&gt;&lt;/span&gt;</span>
3197
3198 <span class="x"> &lt;div class=&quot;details personInformation&quot;&gt;</span>
3199 <span class="x"> &lt;div class=&quot;containerHeadline&quot;&gt;</span>
3200 <span class="x"> &lt;h3&gt;</span><span class="cp">{</span><span class="nv">$person</span><span class="cp">}</span><span class="x">&lt;/h3&gt;</span>
3201 <span class="x"> &lt;/div&gt;</span>
3202
3203 <span class="x"> </span><span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
3204 <span class="x"> &lt;ul class=&quot;inlineList commaSeparated&quot;&gt;</span>
3205 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;personData&#39;</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
3206 <span class="x"> &lt;/ul&gt;</span>
3207 <span class="x"> </span><span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
3208
3209 <span class="x"> </span><span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
3210 <span class="x"> &lt;dl class=&quot;plain inlineDataList small&quot;&gt;</span>
3211 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;personStatistics&#39;</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
3212 <span class="x"> &lt;/dl&gt;</span>
3213 <span class="x"> </span><span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
3214 <span class="x"> &lt;/div&gt;</span>
3215 <span class="x"> &lt;/div&gt;</span>
3216 <span class="x"> &lt;/li&gt;</span>
3217 <span class="x"> </span><span class="cp">{</span><span class="nf">/foreach</span><span class="cp">}</span><span class="x"></span>
3218 <span class="x"> &lt;/ol&gt;</span>
3219 <span class="x"> &lt;/div&gt;</span>
3220 <span class="cp">{</span><span class="nf">else</span><span class="cp">}</span><span class="x"></span>
3221 <span class="x"> &lt;p class=&quot;info&quot;&gt;</span><span class="cp">{</span><span class="nf">lang</span><span class="cp">}</span><span class="x">wcf.global.noItems</span><span class="cp">{</span><span class="nf">/lang</span><span class="cp">}</span><span class="x">&lt;/p&gt;</span>
3222 <span class="cp">{</span><span class="nf">/if</span><span class="cp">}</span><span class="x"></span>
3223
3224 <span class="x">&lt;footer class=&quot;contentFooter&quot;&gt;</span>
3225 <span class="x"> </span><span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
3226 <span class="x"> &lt;div class=&quot;paginationBottom&quot;&gt;</span>
3227 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="o">@</span><span class="nv">$pagesLinks</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
3228 <span class="x"> &lt;/div&gt;</span>
3229 <span class="x"> </span><span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
3230
3231 <span class="x"> </span><span class="cp">{</span><span class="nf">hascontent</span><span class="cp">}</span><span class="x"></span>
3232 <span class="x"> &lt;nav class=&quot;contentFooterNavigation&quot;&gt;</span>
3233 <span class="x"> &lt;ul&gt;</span>
3234 <span class="x"> </span><span class="cp">{</span><span class="nf">content</span><span class="cp">}{</span><span class="nf">event</span> <span class="na">name</span><span class="o">=</span><span class="s1">&#39;contentFooterNavigation&#39;</span><span class="cp">}{</span><span class="nf">/content</span><span class="cp">}</span><span class="x"></span>
3235 <span class="x"> &lt;/ul&gt;</span>
3236 <span class="x"> &lt;/nav&gt;</span>
3237 <span class="x"> </span><span class="cp">{</span><span class="nf">/hascontent</span><span class="cp">}</span><span class="x"></span>
3238 <span class="x">&lt;/footer&gt;</span>
3239
3240 <span class="cp">{</span><span class="nf">include</span> <span class="na">file</span><span class="o">=</span><span class="s1">&#39;footer&#39;</span><span class="cp">}</span><span class="x"></span>
3241 </code></pre></div>
3242 <p>If you compare this template to the one used in the ACP, you will recognize similar elements like the <code>.paginationTop</code> element, the <code>p.info</code> element if no people exist, and the <code>.contentFooter</code> element.
3243 Furthermore, we include a template called <code>header</code> before actually showing any of the page contents and terminate the template by including the <code>footer</code> template.</p>
3244 <p>Now, let us take a closer look at the differences:</p>
3245 <ul>
3246 <li>We do not explicitly create a <code>.contentHeader</code> element but simply assign the title to the <code>contentTitle</code> variable.
3247 The value of the assignment is simply the title of the page and a badge showing the number of listed people.
3248 The <code>header</code> template that we include later will handle correctly displaying the content header on its own based on the <code>$contentTitle</code> variable.</li>
3249 <li>Next, we create additional element for the HTML document’s <code>&lt;head&gt;</code> element.
3250 In this case, we define the <a href="https://en.wikipedia.org/wiki/Canonical_link_element">canonical link of the page</a> and, because we are showing paginated content, add links to the previous and next page (if they exist).</li>
3251 <li>We want the page to be sortable but as we will not be using a table for listing the people like in the ACP, we are not able to place links to sort the people into the table head.
3252 Instead, usually a box is created in the sidebar on the right-hand side that contains <code>select</code> elements to determine sort field and sort order.</li>
3253 <li>The main part of the page is the listing of the people.
3254 We use a structure similar to the one used for displaying registered users.
3255 Here, for each person, we simply display a FontAwesome icon representing a person and show the person’s full name relying on <code>Person::__toString()</code>.
3256 Additionally, like in the user list, we provide the initially empty <code>ul.inlineList.commaSeparated</code> and <code>dl.plain.inlineDataList.small</code> elements that can be filled by plugins using the templates events. </li>
3257 </ul>
3258 <h2 id="usergroupoptionxml"><code>userGroupOption.xml</code><a class="headerlink" href="#usergroupoptionxml" title="Permanent link">#</a></h2>
3259 <p>We have already used the <code>admin.content.canManagePeople</code> permissions several times, now we need to install it using the <a href="../../../package/pip/user-group-option/">userGroupOption package installation plugin</a>:</p>
3260 <div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
3261 <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/userGroupOption.xsd&quot;</span><span class="nt">&gt;</span>
3262 <span class="nt">&lt;import&gt;</span>
3263 <span class="nt">&lt;options&gt;</span>
3264 <span class="nt">&lt;option</span> <span class="na">name=</span><span class="s">&quot;admin.content.canManagePeople&quot;</span><span class="nt">&gt;</span>
3265 <span class="nt">&lt;categoryname&gt;</span>admin.content<span class="nt">&lt;/categoryname&gt;</span>
3266 <span class="nt">&lt;optiontype&gt;</span>boolean<span class="nt">&lt;/optiontype&gt;</span>
3267 <span class="nt">&lt;defaultvalue&gt;</span>0<span class="nt">&lt;/defaultvalue&gt;</span>
3268 <span class="nt">&lt;admindefaultvalue&gt;</span>1<span class="nt">&lt;/admindefaultvalue&gt;</span>
3269 <span class="nt">&lt;usersonly&gt;</span>1<span class="nt">&lt;/usersonly&gt;</span>
3270 <span class="nt">&lt;/option&gt;</span>
3271 <span class="nt">&lt;/options&gt;</span>
3272 <span class="nt">&lt;/import&gt;</span>
3273 <span class="nt">&lt;/data&gt;</span>
3274 </code></pre></div>
3275 <p>We use the existing <code>admin.content</code> user group option category for the permission as the people are “content” (similar the the ACP menu item).
3276 As the permission is for administrators only, we set <code>defaultvalue</code> to <code>0</code> and <code>admindefaultvalue</code> to <code>1</code>.
3277 This permission is only relevant for registered users so that it should not be visible when editing the guest user group.
3278 This is achieved by setting <code>usersonly</code> to <code>1</code>.</p>
3279 <h2 id="packagexml"><code>package.xml</code><a class="headerlink" href="#packagexml" title="Permanent link">#</a></h2>
3280 <p>Lastly, we need to create the <code>package.xml</code> file.
3281 For more information about this kind of file, please refer to <a href="../../../package/package-xml/">the <code>package.xml</code> page</a>.</p>
3282 <div class="highlight"><pre><span></span><code><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
3283 <span class="nt">&lt;package</span> <span class="na">name=</span><span class="s">&quot;com.woltlab.wcf.people&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>
3284 <span class="nt">&lt;packageinformation&gt;</span>
3285 <span class="nt">&lt;packagename&gt;</span>WoltLab Suite Core Tutorial: People<span class="nt">&lt;/packagename&gt;</span>
3286 <span class="nt">&lt;packagedescription&gt;</span>Adds a simple management system for people as part of a tutorial to create packages.<span class="nt">&lt;/packagedescription&gt;</span>
3287 <span class="nt">&lt;version&gt;</span>3.1.0<span class="nt">&lt;/version&gt;</span>
3288 <span class="nt">&lt;date&gt;</span>2018-03-30<span class="nt">&lt;/date&gt;</span>
3289 <span class="nt">&lt;/packageinformation&gt;</span>
3290
3291 <span class="nt">&lt;authorinformation&gt;</span>
3292 <span class="nt">&lt;author&gt;</span>WoltLab GmbH<span class="nt">&lt;/author&gt;</span>
3293 <span class="nt">&lt;authorurl&gt;</span>http://www.woltlab.com<span class="nt">&lt;/authorurl&gt;</span>
3294 <span class="nt">&lt;/authorinformation&gt;</span>
3295
3296 <span class="nt">&lt;requiredpackages&gt;</span>
3297 <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>
3298 <span class="nt">&lt;/requiredpackages&gt;</span>
3299
3300 <span class="nt">&lt;excludedpackages&gt;</span>
3301 <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>
3302 <span class="nt">&lt;/excludedpackages&gt;</span>
3303
3304 <span class="nt">&lt;compatibility&gt;</span>
3305 <span class="nt">&lt;api</span> <span class="na">version=</span><span class="s">&quot;2018&quot;</span> <span class="nt">/&gt;</span>
3306 <span class="nt">&lt;/compatibility&gt;</span>
3307
3308 <span class="nt">&lt;instructions</span> <span class="na">type=</span><span class="s">&quot;install&quot;</span><span class="nt">&gt;</span>
3309 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;acpTemplate&quot;</span> <span class="nt">/&gt;</span>
3310 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;file&quot;</span> <span class="nt">/&gt;</span>
3311 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;sql&quot;</span> <span class="nt">/&gt;</span>
3312 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;template&quot;</span> <span class="nt">/&gt;</span>
3313 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;language&quot;</span> <span class="nt">/&gt;</span>
3314
3315 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;acpMenu&quot;</span> <span class="nt">/&gt;</span>
3316 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;page&quot;</span> <span class="nt">/&gt;</span>
3317 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;menuItem&quot;</span> <span class="nt">/&gt;</span>
3318 <span class="nt">&lt;instruction</span> <span class="na">type=</span><span class="s">&quot;userGroupOption&quot;</span> <span class="nt">/&gt;</span>
3319 <span class="nt">&lt;/instructions&gt;</span>
3320 <span class="nt">&lt;/package&gt;</span>
3321 </code></pre></div>
3322 <p>As this is a package for WoltLab Suite Core 3, we need to require it using <code>&lt;requiredpackage&gt;</code>.
3323 We require the latest version (when writing this tutorial) <code>3.0.0 RC 4</code>.
3324 Additionally, we disallow installation of the package in the next major version <code>3.1</code> by excluding the <code>3.1.0 Alpha 1</code> version.
3325 This ensures that if changes from WoltLab Suite Core 3.0 to 3.1 require changing some parts of the package, it will not break the instance in which the package is installed.</p>
3326 <p>The most important part are to installation instructions.
3327 First, we install the ACP templates, files and templates, create the database table and import the language item.
3328 Afterwards, the ACP menu items and the permission are added.
3329 Now comes the part of the instructions where the order of the instructions is crucial:
3330 In <code>menuItem.xml</code>, we refer to the <code>com.woltlab.wcf.people.PersonList</code> page that is delivered by <code>page.xml</code>.
3331 As the menu item package installation plugin validates the given page and throws an exception if the page does not exist, we need to install the page before the menu item! </p>
3332 <hr />
3333 <p>This concludes the first part of our tutorial series after which you now have a working simple package with which you can manage people in the ACP and show the visitors of your website a simple list of all created people in the front end.</p>
3334 <p>The complete source code of this part can be found on <a href="https://github.com/WoltLab/docs.woltlab.com/tree/5.4/snippets/tutorial/tutorial-series/part-1">GitHub</a>.</p>
3335
3336
3337
3338
3339 <hr>
3340 <div class="md-source-date">
3341 <small>
3342
3343 Last update: 2021-04-07
3344
3345 </small>
3346 </div>
3347
3348
3349
3350
3351
3352
3353
3354
3355 </article>
3356 </div>
3357 </div>
3358
3359 </main>
3360
3361
3362 <footer class="md-footer">
3363
3364 <nav class="md-footer__inner md-grid" aria-label="Footer">
3365
3366 <a href="../overview/" class="md-footer__link md-footer__link--prev" rel="prev">
3367 <div class="md-footer__button md-icon">
3368 <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>
3369 </div>
3370 <div class="md-footer__title">
3371 <div class="md-ellipsis">
3372 <span class="md-footer__direction">
3373 Previous
3374 </span>
3375 Overview
3376 </div>
3377 </div>
3378 </a>
3379
3380
3381 <a href="../part_2/" class="md-footer__link md-footer__link--next" rel="next">
3382 <div class="md-footer__title">
3383 <div class="md-ellipsis">
3384 <span class="md-footer__direction">
3385 Next
3386 </span>
3387 Part 2
3388 </div>
3389 </div>
3390 <div class="md-footer__button md-icon">
3391 <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>
3392 </div>
3393 </a>
3394
3395 </nav>
3396
3397 <div class="md-footer-meta md-typeset">
3398 <div class="md-footer-meta__inner md-grid">
3399 <div class="md-footer-copyright">
3400
3401 <div class="md-footer-copyright__highlight">
3402 Copyright © 2020 WoltLab GmbH
3403 </div>
3404
3405 Made with
3406 <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
3407 Material for MkDocs
3408 </a>
3409
3410 </div>
3411 <div class="md-footer-copyright">
3412 <a href="https://www.woltlab.com/legal-notice/">Legal Notice</a>
3413 <a href="https://www.woltlab.com/privacy-policy/">Privacy Policy</a>
3414 </div>
3415 </div>
3416 </div>
3417 </footer>
3418
3419 </div>
3420 <div class="md-dialog" data-md-component="dialog">
3421 <div class="md-dialog__inner md-typeset"></div>
3422 </div>
3423 <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.fe42c31b.min.js", "version": {"provider": "mike"}}</script>
3424
3425
3426 <script src="../../../assets/javascripts/bundle.d892486b.min.js"></script>
3427
3428
3429 </body>
3430 </html>