-
Notifications
You must be signed in to change notification settings - Fork 24
/
developers-documentation.html
1082 lines (929 loc) · 68.4 KB
/
developers-documentation.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html class="no-js">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Documentation</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="img/icon.ico">
<link rel="stylesheet" href="site.css">
</head>
<style>
.highlight-text {
font-size: 18px;
color: #0e0d0d;
}
.highlight-secondary {
color: #635108;
}
.code-block {
display: block;
background-color: #1e1e1e;
color: #f8f8f2;
padding: 15px;
border-radius: 5px;
font-size: 16px;
line-height: 1;
overflow-x: auto;
white-space: pre-wrap;
margin-bottom: 20px;
}
.function-keyword {
color: #66d9ef;
}
.default-text {
color: #f8f8f2;
}
.bold-title {
font-weight: bold;
}
</style>
<body>
<header>
<nav class="navbar navbar-expand-lg navbar-dark fixed-top">
<div class="container">
<a class="navbar-brand" href="index.html">
<img src="img/logo.svg" width="120" height="60" alt="">
</a>
<button class="navbar-toggler toggler-home" type="button" data-toggle="collapse"
data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse " id="navbarSupportedContent">
<ul class="navbar-nav ml-lg-auto">
<li class="nav-item">
<a class="nav-link" href="index.html">Home <span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="features.html">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="security.html">Security</a>
</li>
<li class="nav-item">
<a class="nav-link" href="modules.html">Mods</a>
</li>
<li class="nav-item">
<a class="nav-link" href="license.html">License</a>
</li>
<li class="nav-item">
<!--<a class="nav-link" href="tests.html">Test</a>-->
</li>
<li class="nav-item">
<a class="nav-link" href="documentation.html">Documentation</a>
</li>
<li class="nav-item active">
<a class="nav-link" href="developers-documentation.html">DEVS-DOC </a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://unencumberedbyfacts.com" target="_blank">Blog</a>
</li>
<li class="nav-item">
<a onclick="document.getElementsByClassName('navbar-collapse')[0].style.display='none';"
class="nav-link" href="contribute.html">Contribute</a>
</li>
<li class="nav-item">
<a class="nav-link" href="https://github.com/cypht-org/cypht-website/blob/master/developers-documentation.html">
<img src="img/edit_icon.png" alt="Edit Icon" width="23" height="23" style="vertical-align:middle;" title="Edit this page">
</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<section class="contibute-section container">
<div class="container">
<h2>Folder structure</h2>
<h3 class="bold-title"><b>.github</b></h3>
<p>This folder contains files used for CI/CD on GitHub, such as running tests during merge requests and
building Docker images. It streamlines collaboration, automates processes, and ensures consistency
across the repository. You'll also find templates for pull requests, issues, and bug reports. To edit
the default text in a merge request description, simply modify the relevant file in this folder.</p>
<h3 class="bold-title"><b>.travis</b></h3>
<p>Refer to <a href="https://docs.travis-ci.com/user/for-beginners/" target="_blank">https://docs.travis-ci.com/user/for-beginners/</a></p>
<h3 class="bold-title"><b>Config</b></h3>
<p>This was introduced with the support of .env files in Cypht</p>
<p>It contains all the contents of the .env file grouped by semantics within the files. Files in this folder
return an array. They simply take the values from the .env file and set a default value if it is not
defined in .env.</p>
<p>A special case is that the Dynamic.php file (not tracked by git) is automatically generated every time
you change something in the .env file. After compiling all other files in this folder, this is the
configuration file used by Cypht at runtime.</p>
<p>Related PR: Switching cypht config hm3.* to dotenv</p>
<p><a href="https://github.com/cypht-org/cypht/pull/823/" target="_blank">https://github.com/cypht-org/cypht/pull/823/</a></p>
<h3 class="bold-title"><b>language</b></h3>
<p>Contain translation files</p>
<h3 class="bold-title"><b>lib</b></h3>
<p>Contain the core of Cypht</p>
<h3 class="bold-title"><b>modules</b></h3>
<ul>
<li>site.js: contains module-specific javascript code</li>
<li>site.css: contains module-specific CSS code</li>
<li>setup.php: These are management modules, output modules, pages, allowed post variables, allowed get
variables, allowed output variables, allowed cookies are set.</li>
<li>modules.php: This file contains handlers and output classes. Some modules with multiple handlers,
outputs and functions may use this file to simply include other files. Take a look at
modules/core/modules.php to get an idea.</li>
<li>Assets folder: some modules have assets (fonts, images, samples). Please see the core or local_contacts
modules.</li>
</ul>
<p>Contains all modules. Each module contains the files below:</p>
<p>You will get a detailed explanation of how each module works in config/app.php.</p>
<h3 class="bold-title"><b>scripts</b></h3>
<p>Contains script files to generate configuration (done once after installation), create/delete/update user
accounts, generate necessary tables to manage users, sessions, or settings based on values in the
environment file and finally others script for development purpose.</p>
<h3 class="bold-title"><b>site</b></h3>
<p>The site folder contains production files generated by scripts/config_gen.php</p>
<h3 class="bold-title"><b>tests</b></h3>
<p>contains PHPUnit and selenium tests</p>
<h3 class="bold-title"><b>third_party</b></h3>
<p>Contains third party minified files used in Cypht</p>
<h3 class="bold-title"><b>.env</b></h3>
<p>The .env file is for high-level configuration. For any variables you're unsure about, check the
configuration folder for detailed explanations.</p>
<h1>How to</h1>
<h3 class="bold-title"><b>See the new link in the left menu</b></h3>
<p>Sometimes you can add a link to the left menu without seeing it. Cypht caches all menus. You need to
click on the reload link below the navigation menu.</p>
<h3 class="bold-title"><b>Create a page</b></h3>
<p>Cypht supports two types of pages: basic and AJAX. Basic pages are accessible via URL and require a page
reload, while AJAX pages load asynchronously.</p>
<p>To create a page called list_messages that displays all messages from the database, navigate to the
correct module (e.g., the nux module) and insert the following code just before the return array:</p>
<p class="highlight-text">In <span class="highlight-secondary">nux/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">setup_base_page</span>('all_messages', 'core');<br>
<span class="function-keyword">add_handler</span>('all_messages', 'load_messages', true);<br>
<span class="function-keyword">add_output</span>('all_messages', 'print_messages', true);
</code>
<p>- setup_base_page adds your new page to the routes, the first argument is the name of the page accessible
by typing /?page=all_messages in the browser in this case</p>
<p>- add_handler adds a handler to the page. A handler class contains logic, validates forms, binds alert
messages, and passes output variables for use in the output.</p>
<p>- add_output adds output to the page. An output class contains HTML that will be returned to the client by typing /?page=all_messages</p>
<h3 class="bold-title"><b>Handler and Output Modules</b></h3>
<p class="highlight-text">In <span class="highlight-secondary">nux/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Handler_load_messages</span> extends Hm_Handler_Module {<br>
<span class="default-text"> public function process() {</span><br>
<span class="default-text"> // Do some logic here</span><br>
<span class="default-text"> // Get the list of messages</span><br>
<span class="default-text"> // Pass the list to the output</span><br>
<span class="default-text"> }</span><br>
<span class="default-text">}</span><br><br>
<span class="function-keyword">class Hm_Output_print_messages</span> extends Hm_Output_Module {<br>
<span class="default-text"> protected function output() {</span><br>
<span class="default-text"> // Get the list of message passed by Hm_Handler_load_messages</span><br>
<span class="default-text"> // Build the html that will be returned</span><br>
<span class="default-text"> // Use return statement to return the html</span><br>
<span class="default-text"> }</span><br>
<span class="default-text">}</span>
</code>
<p>Let's say you want to add an ajax page called ajax_load_new_messages which will receive new messages sent
to the user and update the message list every 15 seconds.</p>
<p class="highlight-text">In <span class="highlight-secondary">nux/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">setup_base_ajax_page</span>('ajax_load_new_messages', 'core');<br>
<span class="function-keyword">add_handler</span>('ajax_load_new_messages', 'get_new_messages', true);<br>
<span class="function-keyword">add_output</span>('ajax_load_new_messages', 'print_new_messages', true);
</code>
<p class="highlight-text">In <span class="highlight-secondary">nux/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Handler_get_new_messages</span> extends Hm_Handler_Module {<br>
<span class="default-text"> public function process() {</span><br>
<span class="default-text"> // Do some logic here</span><br>
<span class="default-text"> // Get the list of new messages</span><br>
<span class="default-text"> // Pass the list to the output</span><br>
<span class="default-text"> }</span><br>
<span class="default-text">}</span><br><br>
<span class="function-keyword">class Hm_Output_print_new_messages</span> extends Hm_Output_Module {<br>
<span class="default-text"> protected function output() {</span><br>
<span class="default-text"> // Get the list of messages passed by Hm_Handler_get_new_messages</span><br>
<span class="default-text"> // Build the HTML that will be returned</span><br>
<span class="default-text"> // Use the out to return data</span><br>
<span class="default-text"> $this->out('ajax_messages', $new_messages);</span><br>
<span class="default-text"> }</span><br>
<span class="default-text">}</span>
</code>
<p>Add this code in <span class="highlight-secondary">nux/site.js</span> to run your ajax page every 15 seconds:
</p>
<code class="code-block">
<span class="function-keyword">$(function() {</span><br>
<span class="default-text"> if (hm_page_name() === 'all_messages') {</span><br>
<span class="default-text"> setInterval(function() {</span><br>
<span class="default-text"> Hm_Ajax.request(</span><br>
<span class="default-text"> [{'name': 'hm_ajax_hook', 'value': 'ajax_load_new_messages'}],</span><br>
<span class="default-text"> function(res) {</span><br>
<span class="default-text"> if (res.ajax_messages) {</span><br>
<span class="default-text"> // Append new messages to the list of messages</span><br>
<span class="default-text"> $('#messages_list').append(res.messages);</span><br>
<span class="default-text"> }</span><br>
<span class="default-text"> }</span><br>
<span class="default-text"> );</span><br>
<span class="default-text"> }, 15000);</span><br>
<span class="default-text"> }</span><br>
<span class="function-keyword">});</span>
</code>
<p><b><b>Note:</b></b> If <span class="function-keyword">setup_base_ajax_page</span> does not have output modules, the
values returned with <span class="function-keyword">$this->out()</span> will be accessible in <span
class="function-keyword">res</span> in JavaScript.</p>
<p>Finally, add the following code to <span class="highlight-secondary">nux/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">return array(</span><br>
<span class="default-text"> 'allowed_pages' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> 'ajax_load_new_messages',</span><br>
<span class="default-text"> 'all_messages'</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_get' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_output' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> 'ajax_messages'</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_post' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> )</span><br>
<span class="function-keyword">);</span>
</code>
<p>Add <span class="function-keyword">all_messages</span> and <span
class="function-keyword">ajax_load_new_messages</span> to the list of allowed pages.</p>
<p>Add <span class="function-keyword">ajax_messages</span> to the list of allowed outputs.</p>
<p>Add post/get variables if they exist in the list of allowed get/posts.</p>
<p><b><b>Note:</b></b> A page can have multiple handlers/outputs. A full list of all handlers and outputs attached to
a page can be seen by accessing <span class="function-keyword">?page=info</span> page on your instance in
the <b>configuration map section</b>.</p>
<h3 class="bold-title"><b>Add third party</b></h3>
<p>Copy the minified file to the third-party directory</p>
<p>Add the file path in Hm_Output_page_js~np~::~/np~output or Hm_Output_header_css~np~::~/np~output if it is
a CSS file.</p>
<p>Finally, add the file in the combine_includes function in scripts/config_gen.php so that it is added when
generating the production site.</p>
<h3 class="bold-title"><b>Enable a module</b></h3>
<p>Edit .env file and add your module to CYPHT_MODULES variable</p>
<h3 class="bold-title"><b>Create a module</b></h3>
<p>In the modules folder, you'll find a <b>hello_world</b> module with the necessary scaffolding for creating a
new module. Customize your module by following the code explained above.</p>
<h3 class="bold-title"><b>Translate string</b></h3>
<p>If you need to translate a string in:</p>
<p><span class="highlight-secondary">Handler module</span>:</p>
<p>Most strings in handlers are only intended to alert the user of success/information/failure operations.
And so they are all written without a translation method but they are all translated later in the
Hm_Output_msgs::output function.</p>
<p><span class="highlight-secondary">Output module</span>:</p>
<code class="code-block">
<span class="default-text">$this->trans("Your text here");</span>
</code>
<p></p><span class="highlight-secondary">JS file</span>:</p>
<code class="code-block">
<span class="default-text">hm_trans("Your text here", "en");</span>
</code>
<p>The second parameter here is optional because the default language is the user's language in the settings
page.</p>
<h3 class="bold-title"><b>Add new translation string</b></h3>
<p>Add your string to each file contained in the language folder.</p>
<p>The file names in this folder are language codes. And all return an array. Just add your new string to
the end of the array. If you know the translation of the text in a language, add it as a value,
otherwise add false as a value.</p>
<h3 class="bold-title"><b>Add new language</b></h3>
<p>Duplicate the en.php file into the language folder</p>
<p>Rename it (Cypht uses 2 digit code, please refer to <a
href="https://en.wikipedia.org/wiki/List_of_ISO_639_lingual_codes"
target="_blank">https://en.wikipedia.org/wiki/List_of_ISO_639_lingual_codes</a>)</p>
<p>In the renamed file, modify interface_lang and interface_direction in the array accordingly.</p>
<p>Add your language in the interface_langs() function.</p>
<p>Update this test to add your language: Hm_Test_Core_Output_Modules::test_lingual_setting</p>
<h3 class="bold-title"><b>Run test</b></h3>
<p>Cypht has PHPUnit tests and selenium</p>
<h3 class="bold-title"><b> PHPUnit</b></h3>
<p>To run all tests:</p>
<code style="display: block; background-color: #1e1e1e; color: #f8f8f2; padding: 15px; border-radius: 5px; font-size: 16px; line-height: 1.5; overflow-x: auto; white-space: pre-wrap; margin-bottom: 20px;">
<span class="function-keyword"> php vendor/phpunit/phpunit/phpunit --configuration tests/phpunit/phpunit.xml</span>
</code>
<p>To run tests contained in a class or a single test method:</p>
<p class="highlight-text">To run tests contained in a class or a single test method:</p>
<code class="code-block">
<span class="function-keyword">php vendor/phpunit/phpunit/phpunit --configuration tests/phpunit/phpunit.xml --filter classOrMethodName</span>
</code>
<p class="highlight-text">To run Selenium tests, make sure you have Python installed (<a
href="https://www.python.org/downloads/">https://www.python.org/downloads/</a>). Once Python is
installed, you should be able to run the following command:</p>
<code class="code-block">
<span class="function-keyword">pip install -r requirements.txt</span>
</code>
<p class="highlight-text">This command will install all required packages. The
requirements.txt file is located at tests/selenium, so make sure to specify the correct path in the
command line, or navigate to that location.</p>
<p class="highlight-text">Once all packages are installed, you can run Selenium tests
using this command:</p>
<code class="code-block">
<span class="function-keyword">sh runall.sh</span>
</code>
<h3 class="bold-title"><b>Fix PHPUnit failing tests</b></h3>
<p>Run all tests or filter specific ones, and the console will show the results. In VSCode or similar, click
on the file path to go directly to the line causing the issue. Check recent changes to classes, divs, or
logic in the handlers that may be causing the failure.</p>
<code class="code-block">
<span class="default-text">There was 1 failure:</span>
<span class="default-text">1) Hm_Test_Uid_Cache::test_uid_is_read</span>
<span class="default-text">Failed asserting that true is false.</span>
<span class="default-text">/Applications/MAMP/htdocs/cypht/tests/phpunit/cache.php:19</span>
</code>
<h3 class="bold-title"><b>Debug AJAX requests</b></h3>
<p>Cypht relies heavily on AJAX requests. To debug, add var_dump and exit in your code. Open your browser's
developer tools, go to the Network tab, filter by Fetch/XHR to see AJAX requests, then run the code.
Click on the request you want to inspect and view the preview or response.</p>
<h3 class="bold-title"><b>The core module</b></h3>
<p>The core module handles:</p>
<p>- Rendering CSS headers</p>
<p>- Displaying alert messages</p>
<p>- Loading JS files and defining shared JS functions</p>
<p>- Configuring basic features like backups, server pages, settings, etc.</p>
<h3 class="bold-title"><b>Practical Example: 1. Add a <code>test</code> page</b></h3>
<p>As mentioned earlier, we use the setup_base_page function to add a new page. We'll call this function to
make our test page available: </p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">setup_base_page</span>('test');
</code>
<p>My Cypht project is available locally at cypht.test. Once we have added this page, we should be able to
access cypht.test as follows: cypht.test?page=test</p>
<img src="./img/screenshots/1.png" style="width:100%; margin-bottom: 10px;"/>
<p>You might find it strange that we see "Page Not Found!" 😔, but understand that this is completely normal
because we have explained the concept of authorizing pages, forms, etc.</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/* allowed input */</span><br>
<span class="function-keyword">return array(</span><br>
<span class="default-text"> 'allowed_pages' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> 'test'</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_output' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_cookie' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_server' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_get' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_post' => array(</span><br>
<span class="default-text"> ...</span><br>
<span class="default-text"> )</span><br>
<span class="function-keyword">);</span>
</code>
<p>After authorizing our page, we can now see a blank page. At least we are getting a result, even if it's
not yet what we expected 😁.</p>
<img src="./img/screenshots/2.png" style="width:100%; margin-bottom: 10px;"/>
<p>As mentioned earlier, the concepts of output and handle are important. At this stage, we define outputs
to display the content of our pages. To show content to the user, create outputs containing HTML.</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_output('test', 'test_heading', true, 'core', 'content_section_start', 'after');</span>
</code>
<p>The first parameter refers to the page that we defined above: test. The second parameter refers to the
class (module) that we need to define in core/modules.php with the name Hm_Output_test_heading (In the
definition of the output in setup.php, we are not going to include Hm_Output_ because it is a prefix
that will be detected automatically). The third parameter allows you to indicate whether this content
will be displayed based on the user's authentication status. The fourth parameter indicates the module
that contains the module (output) code. The fifth parameter indicates the position on our page where
this content will be displayed, and the sixth parameter, which goes with the fifth, determines whether
this content will be before or after the fifth parameter.</p>
<p>Here is Hm_Output_content_section_start, the content after which we want to display our header. </p>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Output_content_section_start </span> extends Hm_Output_Module {</br>
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Opens a main tag for the primary content section</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> return '.$this->trans('Offline').'
}</span><br>
</code>
<p>In our case, we want to place our html content before closing: </p>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
<span class="default-text"><div class="row m-0 position-relative"></span><br>
<span class="default-text"><main class="container-fluid content_cell"></span>
</code>
<p>Here is the definition of the module: test_heading which will display the title Test in the header</p>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
<span class="function-keyword">class Hm_Output_test_heading</span> extends Hm_Output_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> return '<div class="content_title">'.$this->trans('Test').'</div>';</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>And here is the result we get; we start to see the result that we are looking for 👌.</p>
<img src="./img/screenshots/3.png" style="width:100%; margin-bottom: 10px;"/>
<p>Now that we know how to add content, let's add another section after the header. </p>
<p>Define the output in core/setup.php to display it right after the header. You should already know what
the penultimate and last parameters will be.</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_output('test', 'test_first_div', true, 'core', 'test_heading', 'after');</span>
</code>
<p class="highlight-text">Let's define our class(module): <span
class="highlight-secondary">test_first_div</span></p>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
<span class="function-keyword">class Hm_Output_test_first_div</span> extends Hm_Output_Module {<br>
<span class="function-keyword">protected function output()</span> {<br>
return '<div class="mt-3 col-lg-6 col-md-12 col-sm-12">
<div class="card">
<div class="card-body">
<div class="card_title">
<h4>'.$this->trans('Test').'</h4>
</div>
'.$this->trans('We are just testing').'
</div>
</div>
</div>';
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p class="highlight-text">And here is the result we hope for:</p>
<img src="./img/screenshots/4.png" style="width:100%; margin-bottom: 10px;"/>
<p class="highlight-text">Let's add another element to our page just after our second
element:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_output('test', 'test_second_div', true, 'core', 'test_first_div', 'after');</span>
</code>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
<span class="function-keyword">class Hm_Output_test_second_div</span> extends Hm_Output_Module {<br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> return '<div class="mt-3 col-lg-6 col-md-12 col-sm-12">'.</span><br>
<span class="default-text"> '<div class="card">'.</span><br>
<span class="default-text"> '<div class="card-body">'.</span><br>
<span class="default-text"> '<div class="card_title">'.</span><br>
<span class="default-text"> '<h4>'.$this->trans('Test again').'</h4></div>'.</span><br>
<span class="default-text"> $this->trans('We are again just testing').' '.</span><br>
<span class="default-text"> '</div>'.</span><br>
<span class="default-text"> '</div>'.</span><br>
<span class="default-text"> '</div>';</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p class="highlight-text">And here is the result:</p>
<img src="./img/screenshots/5.png" style="width:100%; margin-bottom: 10px;"/>
<p class="highlight-text">Now that we know how to add content, let's add a form element to
the page. We'll submit the form and handle its processing in the backend</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_output('test', 'test_third_div', true, 'core', 'test_second_div', 'after');</span>
</code>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
class Hm_Output_test_third_div extends Hm_Output_Module {
protected function output() {
return '<div class="nux_help mt-3 col-lg-12 col-md-12 col-sm-12">' +
'<div class="card">' +
'<div class="card-body">' +
'<div class="card_title"><h4>' +
$this->trans('Test Our Form') + '</h4></div>' +
'<form class="add_server me-0" method="POST" action="?page=test">' +
'<input type="hidden" name="hm_page_key" value="' +
$this->html_safe(Hm_Request_Key::generate()) + '" />' +
'<div class="subtitle mt-4">Tag name</div>' +
'<div class="form-floating mb-3">' +
'<input required type="text" id="new_tag_name" name="new_tag_name" class="txt_fld'
'form-control" value="" placeholder="' + $this->trans('Tag name') + '" />' +
'<label class="" for="new_tag_name">' +
$this->trans('Tag name') + '</label>' +
'</div>' +
'<input type="submit" class="btn btn-primary px-5" value="' +
$this->trans('Add') + '" name="submit_tag" />' +
'</form>' +
'</div>' +
'</div>' +
'</div>';
}
}
</code>
<p>Here is the result:</p>
<img src="./img/screenshots/6.png" style="width:100%; margin-bottom: 10px;"/>
<p>We learned that add_output allows us to add HTML content to the page, while add_handler is used for
backend processing (like a controller). Both accept similar parameters, with the concepts of 'before'
and 'after' applying to handlers instead of outputs. Remember that these refer to other handlers, and
the class will extend Hm_Handler_Module</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_handler('test', 'process_test_third_div', true, 'core', 'load_user_data', 'after');</span>
</code>
<p>Don't forget to authorize the form field in 'allowed_post'; otherwise, you won't be able to access it.
</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/* allowed input */</span><br>
<span class="function-keyword">return array(</span><br>
<span class="default-text"> 'allowed_pages' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> 'test'</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_output' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_cookie' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_server' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_get' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> ),</span><br>
<span class="default-text"> 'allowed_post' => array(</span><br>
<span class="default-text"> '...',</span><br>
<span class="default-text"> 'new_tag_name' => FILTER_DEFAULT</span><br>
<span class="default-text"> )</span><br>
<span class="function-keyword">);</span>
</code>
<p>We can now define our handler:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/handler_modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Handler_process_test_third_div</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="function-keyword"> * If "stay logged in" is checked, set the session lifetime</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> list($success, $form) = $this->process_form(array('new_tag_name'));</span><br>
<span class="default-text"> if ($success && $form['new_tag_name']) {</span><br>
<span class="default-text"> // var_dump($form);die();</span><br>
<span class="default-text"> }</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>In this documentation, we've discussed how to pass data from POST to GET. Let's consider a scenario where
we want to display the entered data as the label for a field in our form. This involves using sessions
in Cypht:</p>
<p class="highlight-text">In <span
class="highlight-secondary">core/handler_modules.php</span>:</p>
<code class="code-block">
<span class="default-text">$this->session->set('key', 'value');</span>
</code>
<p>Our code will look like this:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/handler_modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Handler_process_test_third_div</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="function-keyword"> * If "stay logged in" is checked, set the session lifetime</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> list($success, $form) = $this->process_form(array('new_tag_name'));</span><br>
<span class="default-text"> if ($success && $form['new_tag_name']) {</span><br>
<span class="default-text"> $this->session->set('tag_name', $form['new_tag_name']);</span><br>
<span class="default-text"> }</span><br>
<span class="function-keyword">}</span><br>
<span class="function-keyword">}</span>
</code>
<p>Remember: to retrieve our stored data, we need another handler, and it must be placed after the handlers
that store the data; otherwise, it may cause malfunctions.</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">add_handler('test', 'get_test_third_div', true, 'core', 'load_user_data', 'after');</span>
</code>
<p>To retrieve an element from the session, we will do it like this:</p>
<code class="code-block">
<span class="default-text">$res = $this->session->get('key', 'Default Value');</span>
</code>
<p>Let's also define the module in handler_modules.php</p>
<p class="highlight-text">In <span
class="highlight-secondary">core/handler_modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">class Hm_Handler_get_test_third_div</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="function-keyword"> * If "stay logged in" is checked, set the session lifetime</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> $res = $this->session->get('tag_name', 'Tag name');</span><br>
<span class="default-text"> if(!is_null($res)) {</span><br>
<span class="default-text"> $this->out('tag_name', $res);</span><br>
<span class="default-text"> // we can now delete session as we do not need no more</span><br>
<span class="default-text"> $this->session->del('tag_name');</span><br>
<span class="default-text"> }</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>This is how you can delete an element from the session</p>
<code class="code-block">
<span class="default-text">$this->session->del('key');</span>
</code>
<p>In the Hm_Handler_get_test_third_div module, we will never encounter a null value because we provided a
default. Therefore, the is_null condition is unnecessary. We use $this->out('tag_name', $res); to pass
our data to the output. To retrieve it from the output, we will do it like this:</p>
<code class="code-block">
<span class="default-text">$this->get('key');</span>
</code>
<p>We will have to edit our class <code>Hm_Output_test_third_div</code> like this:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/output_modules.php</span>:
</p>
<code class="code-block">
<span class="function-keyword">class Hm_Output_test_third_div</span> extends Hm_Output_Module {<br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> $tag_name = $this->get('tag_name');</span><br>
<span class="default-text"> return '<div class="nux_help mt-3 col-lg-12 col-md-12 col-sm-12">'.</span><br>
<span class="default-text"> '<div class="card">'.</span><br>
<span class="default-text"> '<div class="card-body">'.</span><br>
<span class="default-text"> '<div class="card_title"><h4>'.$this->trans('Test Our Form').'</h4></div>'.</span><br>
<span class="default-text"> '<form class="add_server me-0" method="POST" action="?page=test">'.</span><br>
<span class="default-text"> '<input type="hidden" name="hm_page_key" value="'.$this->'.</span><br>
<span class="default-text"> 'html_safe(Hm_Request_Key::generate()).'" />'.</span><br>
<span class="default-text"> '<div class="subtitle mt-4">'.$tag_name.'</div>'.</span><br>
<span class="default-text"> '<div class="form-floating mb-3">'.</span><br>
<span class="default-text"> '<input required type="text" id="new_tag_name" name="new_tag_name".</span><br>
<span class="default-text"> 'class="txt_fld form-control" value="" placeholder="'.$this->'.</span><br>
<span class="default-text"> 'trans('Tag name').'" />'.</span><br>
<span class="default-text"> '<label class="" for="new_tag_name">'.$tag_name.'</label>'.</span><br>
<span class="default-text"> '</div>'.</span><br>
<span class="default-text"> '<input type="submit" class="btn btn-primary px-5" value="'.$this->.</span><br>
<span class="default-text"> trans('Add').'" name="submit_tag" />'.</span><br>
<span class="default-text"> '</form>'.</span><br>
<span class="default-text"> '</div>'.</span><br>
<span class="default-text"> '</div>'.</span><br>
<span class="default-text"> '</div>';</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>Result before:</p>
<img src="./img/screenshots/7.png" style="width:100%; margin-bottom: 10px;"/>
<p>Result after:</p>
<img src="./img/screenshots/8.png" style="width:100%; margin-bottom: 10px;"/>
<h3 class="bold-title"><b>Practical Example: 2. How to add new settings</b></h3>
<p>Let's start by adding a simple parameter and then we'll see how to add another section after.</p>
<p>We will add our setting after this content:</p>
<p class="highlight-text">On page settings</p>
<code class="code-block">
<span class="default-text">Default message sort order</span>
</code>
<img src="./img/screenshots/9.png" style="width:100%; margin-bottom: 10px;"/>
<p>We will need a handle and an output for that:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">add_handler</span>('settings', 'process_test_enable_tag_with_parent', true, 'tags', 'save_user_settings', 'before');<br>
<span class="function-keyword">add_output</span>('settings', 'test_enable_tag_with_parent_setting', true, 'tags', 'default_sort_order_setting', 'after');
</code>
<p>default_sort_order_setting is the output for 'Default message sort order.' When adding an output, we need
to specify its position. In this case, we want to place it after default_sort_order_setting. The
process_test_enable_tag_with_parent_setting must come before save_user_settings since we need to save it
as well. Thus, our configuration process will be applied and saved before handling save_user_settings.
</p>
<p>Now that we’ve defined our routes, we need to write the module for processing:
process_test_enable_tag_with_parent_setting for the handle and test_enable_tag_with_parent_setting for
the output.</p>
<p class="highlight-text">In <span class="highlight-secondary">module.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * @subpackage tags/handler</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Handler_process_test_enable_tag_with_parent_setting</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> function test_tag_with_parent_enabled_callback($val) { return $val; }</span><br>
<span class="default-text"> process_site_setting('test_enable_tag_with_parent', $this, 'test_tag_with_parent_enabled_callback', true, true);</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>and this is the corresponding output:</p>
<p class="highlight-text">In <span class="highlight-secondary">module.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * @subpackage tags/output</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Output_test_enable_tag_with_parent_setting</span> extends Hm_Output_Module {<br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> $settings = $this->get('user_settings');</span><br>
<span class="default-text"> if (array_key_exists('test_enable_tag_with_parent', $settings) && $settings['test_enable_tag_with_parent']) {</span><br>
<span class="default-text"> $checked = ' checked="checked"';</span><br>
<span class="default-text"> $reset = '';</span><br>
<span class="default-text"> }</span><br>
<span class="default-text"> else {</span><br>
<span class="default-text"> $checked = '';</span><br>
<span class="default-text"> $reset = '<span class="tooltip_restore" restore_aria_label="Restore default value"><i class="bi bi-arrow-repeat refresh_list reset_default_value_checkbox"></i></span>';</span><br>
<span class="default-text"> }</span><br>
<span class="default-text"> return '<tr class="general_setting"><td><label class="form-check-label" for="test_enable_tag_with_parent">'.</span><br>
<span class="default-text"> $this->trans('Test Tag enable parent').'</label></td>'.</span><br>
<span class="default-text"> '<td><input class="form-check-input" type="checkbox" '.$checked.</span><br>
<span class="default-text"> ' value="1" id="test_enable_tag_with_parent" name="test_enable_tag_with_parent" />'.$reset.'</td></tr>';</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>reset_default_value_checkbox class here <span class="tooltip_restore"
restore_aria_label="Restore default value"><i
class="bi bi-arrow-repeat refresh_list reset_default_value_checkbox"></i></span> is being used
in js (module core site.js) to restore the default value at line:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/site.js</span>:</p>
<code class="code-block">
<span class="default-text">$('.reset_default_value_checkbox').on("click", reset_default_value_checkbox);</span>
</code>
<p>Remember, if you make changes, you need to define the restore action in JavaScript, as this can occur in
various scenarios.</p>
<p>You can now refresh your page to see the result:</p>
<img src="./img/screenshots/10.png" style="width:100%; margin-bottom: 10px;"/>
<p>You might be wondering why updating our parameter gives an unexpected result; this is normal. Let me
explain: remember the crucial concepts in Cypht related to authorizations and validations (such as
allowed_pages, allowed_output, allowed_post, etc.). In this case, we need to authorize our
test_enable_tag_with_parent field in POST.</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">'allowed_post' => array(</span><br>
<span class="default-text"> 'test_enable_tag_with_parent' => FILTER_VALIDATE_INT</span><br>
<span class="default-text">)</span>
</code>
<p>By adding this permission, the display will remain unchanged, but the update will work as intended. To
access this setting in Cypht, use the usual syntax:</p>
<p class="highlight-text">In <span class="highlight-secondary">core/setup.php</span>:</p>
<code class="code-block">
<span class="default-text">$this->user_config->get('test_enable_tag_with_parent_setting')</span>
</code>
<p>Now that we know how to add a simple setting, let's explore how to add a section to the settings page:
</p>
<img src="./img/screenshots/11.png" style="width:100%; margin-bottom: 10px;"/>
<p>The goal is to add a new section to our settings page. Here’s how:</p>
<img src="./img/screenshots/13.jpg" style="width:100%; margin-bottom: 10px;"/>
<p>In the image above, our section contains two elements, so we will define two handles to process them,
along with three outputs: one for the section title, one for the first element, and another for the
second element.</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/setup.php</span>:</p>
<code class="code-block">
<span class="function-keyword">add_handler</span>('settings', 'process_tag_source_max_setting', true, 'tags', 'load_user_data', 'after');<br>
<span class="function-keyword">add_handler</span>('settings', 'process_tag_since_setting', true, 'tags', 'load_user_data', 'after');<br>
<span class="function-keyword">add_output</span>('settings', 'start_tag_settings', true, 'tags', 'sent_source_max_setting', 'after');<br>
<span class="function-keyword">add_output</span>('settings', 'tag_since_setting', true, 'tags', 'start_tag_settings', 'after');<br>
<span class="function-keyword">add_output</span>('settings', 'tag_per_source_setting', true, 'tags', 'tag_since_setting', 'after');
</code>
<p>We can now add modules, let's start with process_tag_source_max_setting:</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Process "tag_per_source" setting for the tag page in the settings page</span><br>
<span class="default-text"> * @subpackage core/handler</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Handler_process_tag_source_max_setting</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Allowed values are greater than zero and less than MAX_PER_SOURCE</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> process_site_setting('tag_per_source', $this, 'max_source_setting_callback', DEFAULT_PER_SOURCE);</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>and for process_tag_since_setting:</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Process "since" setting for the junk page in the settings page</span><br>
<span class="default-text"> * @subpackage core/handler</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Handler_process_tag_since_setting</span> extends Hm_Handler_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="default-text"> * valid values are defined in the process_since_argument function</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">public function process()</span> {<br>
<span class="default-text"> process_site_setting('tag_since', $this, 'since_setting_callback');</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>That's all for our handles, we need to add output modules too; let's start with "start_tag_settings":</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Starts the Tag section on the settings page</span><br>
<span class="default-text"> * @subpackage core/output</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Output_start_tag_settings</span> extends Hm_Output_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Settings in this section control the tag messages view</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> $res = '<tr><td data-target=".tag_setting" colspan="2" class="settings_subtitle cursor-pointer border-bottom p-2">'.</span><br>
<span class="default-text"> '<i class="bi bi-tags fs-5 me-2"></i>'.</span><br>
<span class="default-text"> $this->trans('Tags').'</td></tr>';</span><br>
<span class="default-text"> print_r($res);</span><br>
<span class="default-text"> return $res;</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>for "tag_since_setting" add:</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Option for the "junk since" date range for the Junk page</span><br>
<span class="default-text"> * @subpackage core/output</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">class Hm_Output_tag_since_setting</span> extends Hm_Output_Module {<br>
<span class="function-keyword">/**</span><br>
<span class="default-text"> * Processed by Hm_Handler_process_tag_since_setting</span><br>
<span class="function-keyword"> */</span><br>
<span class="function-keyword">protected function output()</span> {<br>
<span class="default-text"> $since = DEFAULT_SINCE;</span><br>
<span class="default-text"> $settings = $this->get('user_settings', array());</span><br>
<span class="default-text"> if (array_key_exists('tag_since', $settings) && $settings['tag_since']) {</span><br>
<span class="default-text"> $since = $settings['tag_since'];</span><br>
<span class="default-text"> }</span><br>
<span class="default-text"> return '<tr class="tag_setting"><td><label for="tag_since">'.</span><br>
<span class="default-text"> $this->trans('Show junk messages since').'</label></td>'.</span><br>
<span class="default-text"> '<td>'.message_since_dropdown($since, 'tag_since', $this).'</td></tr>';</span><br>
<span class="function-keyword"> }</span><br>
<span class="function-keyword">}</span>
</code>
<p>and for "tag_per_source_setting" add:</p>
<p class="highlight-text">In <span class="highlight-secondary">tags/modules.php</span>:</p>
<code class="code-block">
<span class="function-keyword">/**</span><br>