From f7cd610fb808d5363aad5127cb853e0e03eb5156 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 31 Jul 2024 21:52:12 +0200 Subject: [PATCH 01/42] new example: multiple choice question --- examples/multiple-choice-question/README.md | 24 ++++++++++++ examples/multiple-choice-question/admin.sql | 36 ++++++++++++++++++ .../create_question.sql | 4 ++ .../delete_option.sql | 7 ++++ .../multiple-choice-question/edit_option.sql | 6 +++ examples/multiple-choice-question/index.sql | 9 +++++ examples/multiple-choice-question/process.sql | 4 ++ examples/multiple-choice-question/results.sql | 7 ++++ .../screenshots/admin.png | Bin 0 -> 59091 bytes .../screenshots/main_form.png | Bin 0 -> 35889 bytes .../screenshots/results.png | Bin 0 -> 61361 bytes .../migrations/0001_create_users_table.sql | 14 +++++++ .../website_header.json | 11 ++++++ 13 files changed, 122 insertions(+) create mode 100644 examples/multiple-choice-question/README.md create mode 100644 examples/multiple-choice-question/admin.sql create mode 100644 examples/multiple-choice-question/create_question.sql create mode 100644 examples/multiple-choice-question/delete_option.sql create mode 100644 examples/multiple-choice-question/edit_option.sql create mode 100644 examples/multiple-choice-question/index.sql create mode 100644 examples/multiple-choice-question/process.sql create mode 100644 examples/multiple-choice-question/results.sql create mode 100644 examples/multiple-choice-question/screenshots/admin.png create mode 100644 examples/multiple-choice-question/screenshots/main_form.png create mode 100644 examples/multiple-choice-question/screenshots/results.png create mode 100644 examples/multiple-choice-question/sqlpage/migrations/0001_create_users_table.sql create mode 100644 examples/multiple-choice-question/website_header.json diff --git a/examples/multiple-choice-question/README.md b/examples/multiple-choice-question/README.md new file mode 100644 index 00000000..92fb0c35 --- /dev/null +++ b/examples/multiple-choice-question/README.md @@ -0,0 +1,24 @@ +# SQLPage multiple choice question example + +This is a very simple example of a website that stores a list of +possible answers to a multiple choice question in a database table, +and then displays the question and the possible answers to the user. + +When the user selects an answer, the website will save the user's +choice in the database and display other users' choices as well. + +## Screenshots + +| Question answering form | Results table | Question edition | +| --- | --- | --- | +| ![Question answering form](screenshots/main_form.png) | ![Results table](screenshots/results.png) | ![Question edition](screenshots/admin.png) | + +## How to run + +Just run the sqlpage binary (`./sqlpage.bin`) from this folder. + +## Interesting files + +[admin.sql](admin.sql) uses the [dynamic component](https://sql.ophir.dev/documentation.sql?component=dynamic#component) to create a single page with one form per MCQ option. + +[website_header.json](website_header.json) contains the [shell](https://sql.ophir.dev/documentation.sql?component=shell#component) that is then used in all pages using the `dynamic` component to create a consistent look and feel between pages. \ No newline at end of file diff --git a/examples/multiple-choice-question/admin.sql b/examples/multiple-choice-question/admin.sql new file mode 100644 index 00000000..37b6b246 --- /dev/null +++ b/examples/multiple-choice-question/admin.sql @@ -0,0 +1,36 @@ +select 'dynamic' as component, sqlpage.read_file_as_text('website_header.json') as properties; + +select 'alert' as component, 'Saved' as title, 'success' as color where $saved is not null; +select 'alert' as component, 'Deleted' as title, 'danger' as color where $deleted is not null; +select 'alert' as component, 'This option cannot be deleted' as title, 'danger' as color, 'If an option has already been chosen by at least one respondant, then it cannot be deleted' as description where $cannot_delete is not null; + +select 'dynamic' as component, + json_array( + json_object( + 'component', 'form', + 'title', CONCAT('Option ', id), + 'action', CONCAT('edit_option.sql?id=', id), + 'validate', '', + 'id', CONCAT('option', id) + ), + json_object( + 'type', 'text', + 'name', 'profile_description', + 'label', 'Profile description', + 'value', profile_description + ), + json_object( + 'type', 'number', + 'name', 'score', + 'min', 0, + 'label', 'Score', + 'value', score + ), + json_object('component', 'button', 'size', 'sm'), + json_object('title', 'Delete', 'outline', 'danger', 'icon', 'trash', 'link', CONCAT('delete_option.sql?id=', id)), + json_object('title', 'Save', 'outline', 'success', 'icon', 'device-floppy', 'form', CONCAT('option', id)) + ) as properties +from dog_lover_profiles; + +select 'button' as component, 'center' as justify; +select 'Create new question' as title, 'create_question.sql' as link; \ No newline at end of file diff --git a/examples/multiple-choice-question/create_question.sql b/examples/multiple-choice-question/create_question.sql new file mode 100644 index 00000000..9894f16c --- /dev/null +++ b/examples/multiple-choice-question/create_question.sql @@ -0,0 +1,4 @@ +insert into dog_lover_profiles(profile_description, score) values ('', 50) +returning + 'redirect' as component, + 'admin.sql' as link; \ No newline at end of file diff --git a/examples/multiple-choice-question/delete_option.sql b/examples/multiple-choice-question/delete_option.sql new file mode 100644 index 00000000..e6ecf4be --- /dev/null +++ b/examples/multiple-choice-question/delete_option.sql @@ -0,0 +1,7 @@ +select 'redirect' as component, 'admin.sql?cannot_delete' as link +where exists (select 1 from answers where profile_id = $id); + +delete from dog_lover_profiles where id = $id +returning + 'redirect' as component, + 'admin.sql?deleted' as link; \ No newline at end of file diff --git a/examples/multiple-choice-question/edit_option.sql b/examples/multiple-choice-question/edit_option.sql new file mode 100644 index 00000000..bd70bee5 --- /dev/null +++ b/examples/multiple-choice-question/edit_option.sql @@ -0,0 +1,6 @@ +update dog_lover_profiles +set profile_description = :profile_description, score = :score +where id = $id +returning + 'redirect' as component, + 'admin.sql?saved' as link; \ No newline at end of file diff --git a/examples/multiple-choice-question/index.sql b/examples/multiple-choice-question/index.sql new file mode 100644 index 00000000..6049fd18 --- /dev/null +++ b/examples/multiple-choice-question/index.sql @@ -0,0 +1,9 @@ +select 'dynamic' as component, sqlpage.read_file_as_text('website_header.json') as properties; + +SELECT + 'form' AS component, + 'What dog lover are you ?' AS title, + 'process.sql' AS action; + +select 'radio' as type, 'profile' as name, id as value, profile_description as label +from dog_lover_profiles; \ No newline at end of file diff --git a/examples/multiple-choice-question/process.sql b/examples/multiple-choice-question/process.sql new file mode 100644 index 00000000..0fa81dfd --- /dev/null +++ b/examples/multiple-choice-question/process.sql @@ -0,0 +1,4 @@ +insert into answers(profile_id) +select CAST(:profile as integer) +where :profile is not null +returning 'redirect' as component, 'results.sql' as link; \ No newline at end of file diff --git a/examples/multiple-choice-question/results.sql b/examples/multiple-choice-question/results.sql new file mode 100644 index 00000000..917e77c3 --- /dev/null +++ b/examples/multiple-choice-question/results.sql @@ -0,0 +1,7 @@ +select 'dynamic' as component, sqlpage.read_file_as_text('website_header.json') as properties; + +select timestamp, profile_description, score from answers +inner join dog_lover_profiles on dog_lover_profiles.id = answers.profile_id; + +select 'csv' as component; +select * from answers; \ No newline at end of file diff --git a/examples/multiple-choice-question/screenshots/admin.png b/examples/multiple-choice-question/screenshots/admin.png new file mode 100644 index 0000000000000000000000000000000000000000..36347a476afe2a911f62bf6249060e0d7238177f GIT binary patch literal 59091 zcmeEuWmFvBw`B|>1V{pe;1b;3-Q8V7a1ZX51PG8oa2j`Sw1GflNpN?!;LtPTs{#=w? zsC)xOu6{#aE=u(4?+)J`9?2F7lhcqrctxH`I$smlx_je=IlxlQpTfY?>nl9SjtTW} zNcFLFPejfmQdbf<1MmWz9$YI8KRY`T<3Tq<+h~O?33xK5J`A8r4Q<)aptfw?quTgs z`-jkzDD??&@_{hcz^BPyygZ{fCi7`4F+gW;KdrN(G`AzVJzq?zc!k0uSZr2aBd%8LR1!^QS zSGCTJRmQ=t8IE1$G~W3A6!Y(6UKU%`Cp-TciT|+2J~zMd_yqQH_tW2wiFkgybXM`? z=KMtz-VsHzSA+5V?_GbQc}%kR?JRJ1RmOx3(sutocIo7-W&Kj>4~FF56Qpe9*eG}R zEU7*D*V!M~Vyb@|VhUElzB>$*C;PvB>A_b9OBn1zC4az(bkTl>N;zF1ep|Cg{z4gF z-XMb~dhKsl@A2F(D91iQAQIc@o8v#4+jLFIR<9mUy8!cu1LE*Y$EH&kUuV#v^7r>w z`am1RU2a(Ce`r)oK9V=N;duS-Zoqpqb*0GOv(SyX35FDAyb%ht!Tc=u%TwT0%0yuSFGwGYsNv?E8&|i7G z#(CG2gr%gU@FsFbSoYMx>bjO*uB^4~736t0Dd)PM$gveFYsN+(K|Mc z2A}Y`zN{zYcj^(yA2ei;K6QvbJMi6^&~0!|J0@i5E476!Ts?MP8hUw@#OfH6CM@3Y zm=e3-mVF;KKIYs@?2-^4WI*;L4c)W_!Gn^^dXLryOpF$Pp&Y*Ya<`j4$BqVP-^^ZB zL*O%=pTqD2Ypo2lz1{5Tof=Ia^R?px>kP?8xmqI2Za%XUT;&Xxnufpw4%Y(H)#|jy zMPQ+3h=r>O5sfHAh>DPG3RS#8i<7$l%}VK|+x5pdMqLgS(&9h%gV)A-9_3^Xe;#-# z@RZD6S6H?P?4NLo8ug=s&F8D8V;JT0zb9ka%nUs6a!FQ!?-&Q9Jd;D0scD+O@F$C~ zP=0dKoC?yyfSveUu7+9XYstQ5RufU9kf*~W5sGQL@Q;?)E=zvbpr1i@xPx@DHYm?$ z&xlEbLr=S63zQYl@Zua3`{H2S4zkM9H6yjZzA^ zbR7Ro|A;Dc<>MZM>Q*e@LYAXYIOEcqm_O^ec9{2Ro-4D4pt+6-pD#1?shK^I;5Ag@ z0McSMS+>YAUi=#W7y2%usO`%&-PFi-|LOdB_rSa`NThtS^-q;Ql5lihklc!8sz{L? zR)%rytzk3fNXcx}$Rd8WMT$M|;deqmrTZlXfUYq4htYW{k3u(qkJZv;ig z+i=I)?ODdD8XxqX2|S^5|^Ky1k*pRN1B6i5C%-4pgCm0xeP zQ8&wKN-XHtrFa~m!`vN1fuULb_V7x*QOw?YCbJe|r^c+&8M#AF6(2#0l|R55wAnh? zki2_9hLT+i$yQ(7|8(I`H<&KSjl~)~K5olhnu1MlKsvNOU2TzoV4n_!pe2;OGF}X| zFoe$tILnYgMKq9Y2MKssQc2I$PGHfdhojfxL8m0DhwD8|lo78XySr0ii*R@FABT)V zX}Pd)k`z`tb#q!8uL$kvvL<2f@w~`Q<|{lGT=}qYHww22v6!3>C|kphg>#K=`s>WP zl(OuroS0zO=bK%Xu_M=`*XwT@?04VC=bA>{x0w1gbdGE#bD57rT|l$=iiz#+?dd6# zu}hSzQ7L92=w}EtVD0h6#!X*uws^ejKBw*M{CQ)JZPs2ivy?xhq)@3fl|YYHX6cyy zglMhZBF+cxj&5~Jh4JFDjMGoq`98j&;*aSrw2O~Jva}6bK3c5r>znle67A1RI zYIgPOCe2B(88-Gk^%P4wdyR{ebG!9)s&MI;(e>RE_UdkyK3pmDow8kwo^B2<#NgQG z{gVDhX&CuFM#$2sXu6`N=$#t{$HpxNxwrQWy-4Y*N}Vk~K$$L%k@m3%I6zwoUSs`p z=5lLgHO1OAFQ9zjaDVvik*;oN>qU9m%|&)xshRQd`d7#syAzKTN{hZXRo?LYo7^y6 zHp@8d?$~SU&TD2K&Ah@8w_?R3)S$9p)HLr66J+wq^41pZIDG((__-S<#Hg3b&3ve$ zlIjZ@y}VVC`_);snM@*8o1t%uU!7YT1~^5hQvhA&Q{%Z?vHtIw{@H7U)Ls&W{P}j| zuN-M+XbL?Te|$*(A&dSD+;@8Wq{_b+L%kiJ<@@gG^b6esI4X>ak0CI`%{%RI1Ks;0 zyeZEvK)92a_^!1?IfWKihHYOy%%`R4d^0}RaXE`M|?SnBaIUaP@ovnXhP z9LB9EdAx({HJcqE`8`U(->Y4oNPHEVoPmQzR3%yxe3IqiBlY~c?~fD4^?AzkH|Ozv z=`CZsUg&!#tmZo^$U++wm`j!*_wDQPOl%9sQO}l+u%P0#Sf14u5ENMZJfeVIx5>k^6b8+J^E`Gbct?G?Fof5u|f{-NqF26e8 z$gf0Pt-d#d!h^ZCdq0Dy6rESQDz!Zl!(aDpSmDs4<1=D8Tc4dBX9RpS6z1T#w42Q< zw=QY!8Lu#PFO;`)(Rp47(c+i`vnaW5P39(ROxa_HLvr3}s> ztAsv0g&MHX@oqt|z4jBF8JCB*Z$~?`30ZryCms9NYOv>dy_^hK%D&%{Q(sR-y;kqx zXhhYA=_my!`7xRx*f@lHIdq&LnK-avVHtQ?%jz0c|YHWK)};vFt-+ zD5Fh@Qe_5E2m8ocL!T4Cl8hfWmKqd*c5c=x3|(pv*;;OT-WC&%!^381a>AkObb72q zTjSOvN_?(+Bv$n!b{j_~E2~lL0eU`WdO~%NJxXtW^*C}T{ds9SS!gz6OG^mBNk&9r zkYr~3snISsvh~BXF4t{}^g$5C9dhD>%!7NzSX_>#Mi7-4q3>SKAl%Gw&Zq}v;~BKN zzP_Bvaj`u(I^s6`-m=)-`;{7BwI_~&&dpYf6(?4&R=}%+n=Rmg&U&FR65_DHqT-f~ z3Q5@4e8`Ai{V|r#oDDY0?J$qwFg}@0mal}LcZ{oAwxbI_Ezk_mi?o;TM4n};@UrX1 z=IPf3Uz6%ueV@s4Q->k)B5)HX(ulN4l+v17A7$9goc*y7qsg1U;4HKsD=q;A+N{ke zQZ=Ft^y_<+Du-(HfHz7nCJ&6_-4B%0k54)@3|qXQmtMP$)xzi!ZimbfjrGn+{O|No z_&okNyC{z3(NFfZ;LEw&FaE+moh;S*=}KMksE$^hf*lhasFj{hY_VKgk;LW_R3ip) zL}0jk#BKX(v|qB02fFX%>RQa{OLG%*&@4i1nok@Gv=Y`CRokD7XX13!nvRB?&fTt) zR!!d-k3#I84E>Jha7(IdqU&+9BQLMXe4O>83=EHn{a7ZoQWc@C$?8~SX`abCV4x^W z&mj}Nx#CG%Lk<}nU~2Kl$8qA)k+Or=zbU*So9jobKmI(-HCt<7^BW)^H9hFd*f9GA z;kB5byxJj9JwDn%qT#MxDNur;gs0p724wr8M7U zd_`8Z;1R1ulygwjV4D;TH3y8v#KZc0Y|fSjSARY<#yeZnv7R_p_c~lHa^k?gt4* z)bEC3v=AaRniBc)<{JDu6(D(7Uru%>7WDkKXySZ{^gWshJIM6L3P&J+dY@^aq4Ue* zFq<`O+Qy#*iAxiOl;J1LxZe!1)QP00POwUz7%*VDH}(HxSpvI6gb8Ncg(t$2Qy^Ow z&)gw(OMQ+Bw*e2E<@TFV6l3d6%?br{=qQ1pe{#{A%j^KmYJ&)i$V>NM*u>T}$#mw< z>ZGUbT~F024QNNj*#pv3*V*R-WPNnbFOVtQDqya$vZVyBEz5K3YTRTo?%v#qdKa@; zlG9!8c1b?rzM|29nDCb)-=M(Zqghx%4M= zNpWvuX$z%kKOr6xPAl`C0?i!n@&`R%vOOGy;Adewv-L_dwN_^6*eB?DEmjQdDfF`+ z4t}=8GNN>{k;OiHqtP5m62SLvmq{@L+Qsx@ytOLv(*Z|~ukoIab)H4Lo}%Jbv(`fT z&4p&74;s46No25G2A5){Y_7)oy;$WU=_ulq7mC(-`S3Q;bGPt56a~`avc(_f*XT-9 zcPH}>p=K}uXW`@RfVmRlzye;&kLSJi0*I9&P{OqYp9C|M9re98UUgjz0F zVnw9MFuPzlBAQRseeT~O60y>LQpr@En&_>6hqIz3fnXAzoXn@`d~|h^!Yq75jD+Xv zcE0*T2Ob*zJ}M?G4q>&32{O&nPF-)W7{)Tn_is@9?X?nmYl zkUFo@p&>FfHyv>>%f!8U*8?#s`O1Dwv7_tCq0|nxxb&)HgW5DKsg34gqe1*6RO(#7 z^1t2^Q-$-Dw{Rqnl~clntS>vt{D6qaOeLeNDqw?;T{Nb>@L;rbzRK-ARqvbp^L%uN zOVzyjD8^URa&P|FPtvvI%Dw`HMYyJ_q#YQQ@Z0+dx<$s7sV#nn@-1}#$>~&l>aJqy zxXo;aR<2V?SgX6|isH)uJig*woFu00DVC1*@bt{59Q$S$gt<&1na8iQEan=-Q2^c; zDt5w=*Y$Gnv_U`?*pN;BHqLM0PrFxabQEbJ<=JHW`sMDt&%60=_Ft^&6Z&mEE_O@g z-svZ>D%$HdoIO6S9YDV|%;XV)8J%-;Mp8@(A4!dkjYnhINM~g;7&8Pf0$8PDEPEXN zo)dBYw6o|YI<8Je&EeY%9w2zHkAgHF63aiKUr&~9$COXL$DyB~Fp)oZUMFn>Dn{QK zkau}1Kd1r+{V?wrqt&A+2f}rQXK`f~+^NPA-YK2Wy4Ol3&Gwp&`##Tsj^fy&)Z$^F ze%D`=5krju40aDy*RJ<`hWXv?V0?0m(q#SJSR)sFY!Mvu8;lc7X(w9cbGx0&YM3Qmi{mZu@irmLOk>Ucg&&1G3_Oykq_+qc>xzc&NtIYRIlXGrxE!O)cKQVN#4;175FLTY)p;u2$)~zMJ$8 z6|D${8@9~xR)d6}U0@J%8KN8~rj7)qLH)U=K2Mg*@9pZG3Hfe*@TlFu(fDDH{6wVZ zxbly)B&~X4oy5V+%pBUz%D0{)ITt{^1fK^RX4*!`DP|B3mxWnwtW|=Xec>)<5~) z8ljbF#a7b7doxNbM0!7oVD@@QxKylQe-g{wZpEwzJf8O`5kzMIe~Ax(rQj6(wlq|TYHTGO{ukBDr4E)TD0kH^-o=qseN3U_@r+*{uy z{q_21aDih_nczh!bVAlOnX}ZsW3oa6x;>S0e+x68@=cTHRx#)cEotu~iCPBpsqHx= z+WN_m@j@hV*4Bc9PnuinJNZdpY1cXQh``C|TIY#V%&1^W6_QAsb%7MgMn6esU}Tzrxnb^aQi
7yb| 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 GC;tl)g+i_X literal 0 HcmV?d00001 diff --git a/examples/multiple-choice-question/screenshots/main_form.png b/examples/multiple-choice-question/screenshots/main_form.png new file mode 100644 index 0000000000000000000000000000000000000000..3ccc792445c15baa34e3d451e8e5c41118a09ace GIT binary patch literal 35889 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 Date: Wed, 31 Jul 2024 21:53:30 +0200 Subject: [PATCH 02/42] fix long texts in pills in datagrid fixes https://github.com/lovasoa/SQLpage/issues/515 thanks @Pieter3033 for reporting the issue --- sqlpage/templates/datagrid.handlebars | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sqlpage/templates/datagrid.handlebars b/sqlpage/templates/datagrid.handlebars index 09829842..3acc28d7 100644 --- a/sqlpage/templates/datagrid.handlebars +++ b/sqlpage/templates/datagrid.handlebars @@ -32,13 +32,13 @@ {{~#if tooltip}} data-bs-toggle="tooltip" data-bs-placement="top" title="{{tooltip}}"{{/if}} > {{#if color}} - + {{/if}} {{#if image_url}} {{title}} {{/if}} {{#if icon}} - {{~icon_img icon~}} + {{~icon_img icon~}} {{/if}} {{#if description}} {{description}} From 8251c8d9a9f60103fd812ba75f4ac80b578affb3 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 31 Jul 2024 21:54:49 +0200 Subject: [PATCH 03/42] fix js bug with empty tables see https://github.com/lovasoa/SQLpage/issues/513 we still need to isolate the different sqlpage functions to prevent cascading failures --- sqlpage/templates/table.handlebars | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sqlpage/templates/table.handlebars b/sqlpage/templates/table.handlebars index f4bedb5c..571f5413 100644 --- a/sqlpage/templates/table.handlebars +++ b/sqlpage/templates/table.handlebars @@ -57,6 +57,13 @@ {{/each_row}} {{flush_delayed}} + {{#if (eq @row_index 0)}} + + + {{default empty_description 'No data'}} + + + {{/if}} From 0a66c403a13aa69bfa465aa989f6d5eb5a74cd2f Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 31 Jul 2024 21:55:34 +0200 Subject: [PATCH 04/42] fix 0 values not being displayed in forms --- sqlpage/templates/form.handlebars | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sqlpage/templates/form.handlebars b/sqlpage/templates/form.handlebars index 184c2e08..a80bce1c 100644 --- a/sqlpage/templates/form.handlebars +++ b/sqlpage/templates/form.handlebars @@ -1,6 +1,6 @@

-
+
{{#if title}} -

{{title}}

+

{{title}}

{{/if}}
{{#each_row}} @@ -50,7 +50,7 @@ placeholder="{{placeholder}}" rows="{{default rows 3}}" {{#if id}}id="{{id}}" {{/if~}} - {{~#if value}}value="{{value}}" {{/if~}} + {{~#if value includeZero=true}}value="{{value}}" {{/if~}} {{~#if minlength}}minlength="{{minlength}}" {{/if~}} {{~#if maxlength}}maxlength="{{maxlength}}" {{/if~}} {{~#if required}}required="required" {{/if~}} @@ -58,7 +58,7 @@ {{~#if disabled}}disabled {{/if~}} {{~#if readonly}}readonly {{/if~}} > - {{~#if value}}{{value}}{{/if~}} + {{~#if value includeZero=true}}{{value}}{{/if~}} {{else}}{{#if (eq type 'select')}} - - - {{/if}} -
+ {{~/if~}} + {{~/each}} + + {{#if search_target}} + + {{/if}} diff --git a/tests/end-to-end/.gitignore b/tests/end-to-end/.gitignore new file mode 100644 index 00000000..68c5d18f --- /dev/null +++ b/tests/end-to-end/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/tests/end-to-end/official-site.spec.ts b/tests/end-to-end/official-site.spec.ts new file mode 100644 index 00000000..9800235a --- /dev/null +++ b/tests/end-to-end/official-site.spec.ts @@ -0,0 +1,60 @@ +import { test, expect } from '@playwright/test'; + +const BASE = 'http://localhost:8080/'; + +test('Open documentation', async ({ page }) => { + await page.goto(BASE); + + // Expect a title "to contain" a substring. + await expect(page).toHaveTitle("SQLPage"); + + // open the submenu + await page.getByText('Documentation', { exact: true }).first().click(); + await page.getByText('All Components').click(); + const components = ['form', 'map', 'chart', 'button']; + for (const component of components) { + await expect(page.getByRole('link', { name: component }).first()).toBeVisible(); + } +}); + +test('chart', async ({ page }) => { + await page.goto(BASE + '/documentation.sql?component=chart#component'); + await expect(page.getByText('Loading...')).not.toBeVisible(); + await expect(page.locator('.apexcharts-canvas').first()).toBeVisible(); +}); + +test('map', async ({ page }) => { + await page.goto(BASE + '/documentation.sql?component=map#component'); + await expect(page.getByText('Loading...')).not.toBeVisible(); + await expect(page.locator('.leaflet-marker-icon').first()).toBeVisible(); +}); + +test('form example', async ({ page }) => { + await page.goto(BASE + '/examples/multistep-form'); + // Single selection matching the value or label + await page.getByLabel('From').selectOption('Paris'); + await page.getByText('Next').click(); + await page.getByLabel(/\bTo\b/).selectOption('Mexico'); + await page.getByText('Next').click(); + await page.getByLabel('Number of Adults').fill('1'); + await page.getByText('Next').click(); + await page.getByLabel('Passenger 1 (adult)').fill('John Doe'); + await page.getByText('Book the flight').click(); + await expect(page.getByText('John Doe').first()).toBeVisible(); +}); + +test('File upload', async ({ page }) => { + await page.goto(BASE); + await page.getByText('Examples', { exact: true }).click(); + await page.getByText('File uploads').click(); + const my_svg = 'Hello World'; + // @ts-ignore + const buffer = Buffer.from(my_svg); + await page.getByLabel('Picture').setInputFiles({ + name: 'small.svg', + mimeType: 'image/svg+xml', + buffer, + }); + await page.getByRole('button', { name: 'Upload picture' }).click(); + await expect(page.locator('img[src^=data]').first().getAttribute('src')).resolves.toBe('data:image/svg+xml;base64,' + buffer.toString('base64')); +}); \ No newline at end of file diff --git a/tests/end-to-end/package-lock.json b/tests/end-to-end/package-lock.json new file mode 100644 index 00000000..a50b1096 --- /dev/null +++ b/tests/end-to-end/package-lock.json @@ -0,0 +1,91 @@ +{ + "name": "end-to-end", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "end-to-end", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^22.1.0" + } + }, + "node_modules/@playwright/test": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.45.3.tgz", + "integrity": "sha512-UKF4XsBfy+u3MFWEH44hva1Q8Da28G6RFtR2+5saw+jgAFQV5yYnB1fu68Mz7fO+5GJF3wgwAIs0UelU8TxFrA==", + "dev": true, + "dependencies": { + "playwright": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "22.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.1.0.tgz", + "integrity": "sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw==", + "dev": true, + "dependencies": { + "undici-types": "~6.13.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.45.3.tgz", + "integrity": "sha512-QhVaS+lpluxCaioejDZ95l4Y4jSFCsBvl2UZkpeXlzxmqS+aABr5c82YmfMHrL6x27nvrvykJAFpkzT2eWdJww==", + "dev": true, + "dependencies": { + "playwright-core": "1.45.3" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.13.0.tgz", + "integrity": "sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg==", + "dev": true + } + } +} diff --git a/tests/end-to-end/package.json b/tests/end-to-end/package.json new file mode 100644 index 00000000..2bba09a1 --- /dev/null +++ b/tests/end-to-end/package.json @@ -0,0 +1,14 @@ +{ + "name": "end-to-end", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.45.3", + "@types/node": "^22.1.0" + } +} diff --git a/tests/end-to-end/playwright.config.ts b/tests/end-to-end/playwright.config.ts new file mode 100644 index 00000000..38423e13 --- /dev/null +++ b/tests/end-to-end/playwright.config.ts @@ -0,0 +1,78 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// import dotenv from 'dotenv'; +// dotenv.config({ path: path.resolve(__dirname, '.env') }); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './.', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); From e8ba798f82988fdddc4d7778fb364f279b6fe889 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Mon, 5 Aug 2024 18:34:35 +0200 Subject: [PATCH 30/42] add support for using the system's root ca certificates in sqlpage.fetch fixes https://github.com/lovasoa/SQLpage/issues/507 --- CHANGELOG.md | 1 + Cargo.lock | 63 +++++++++++++++++++ Cargo.toml | 2 + configuration.md | 1 + src/app_config.rs | 10 +++ src/webserver/content_security_policy.rs | 4 +- .../database/sqlpage_functions/functions.rs | 29 +++++++-- src/webserver/http.rs | 10 +-- 8 files changed, 109 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93c682d1..f5ca470a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - reduce the margin at the botton of forms to make them more compact. - fix [datagrid](https://sql.ophir.dev/documentation.sql?component=datagrid#component) color pills display when they contain long text. - fix the "started successfully" message being displayed before the error message when the server failed to start. + - add support for using the system's native SSL Certificate Authority (CA) store in `sqlpage.fetch`. See the new `system_root_ca_certificates` configuration option. ## 0.25.0 (2024-07-13) diff --git a/Cargo.lock b/Cargo.lock index b3e8a2ed..f51b6ff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,6 +912,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -2106,6 +2116,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + [[package]] name = "option-ext" version = "0.2.0" @@ -2614,6 +2630,19 @@ dependencies = [ "x509-parser", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a88d6d420651b496bdd98684116959239430022a115c1240e6c3993be0b15fba" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.1.3" @@ -2647,12 +2676,44 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2848,7 +2909,9 @@ dependencies = [ "password-hash", "percent-encoding", "rand", + "rustls", "rustls-acme", + "rustls-native-certs", "serde", "serde_json", "sqlparser", diff --git a/Cargo.toml b/Cargo.toml index 6b038f7f..836013e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,8 @@ base64 = "0.22" rustls-acme = "0.9.2" dotenvy = "0.15.7" csv-async = { version = "1.2.6", features = ["tokio"] } +rustls = { version = "0.22.0" } # keep in sync with actix-web, awc, rustls-acme, and sqlx +rustls-native-certs = "0.7.0" awc = { version = "3", features = ["rustls-0_22-webpki-roots"] } [build-dependencies] diff --git a/configuration.md b/configuration.md index 8c3d4089..18658f71 100644 --- a/configuration.md +++ b/configuration.md @@ -31,6 +31,7 @@ Here are the available configuration options and their default values: | `https_acme_directory_url` | https://acme-v02.api.letsencrypt.org/directory | The URL of the ACME directory to use when requesting a certificate. | | `environment` | development | The environment in which SQLPage is running. Can be either `development` or `production`. In `production` mode, SQLPage will hide error messages and stack traces from the user, and will cache sql files in memory to avoid reloading them from disk. | | `content_security_policy` | | The [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to set in the HTTP headers. If you get CSP errors in the browser console, you can set this to the empty string to disable CSP. | +| `system_root_ca_certificates` | false | Whether to use the system root CA certificates to validate SSL certificates when making http requests with `sqlpage.fetch`. If set to false, SQLPage will use its own set of root CA certificates. If the `SSL_CERT_FILE` or `SSL_CERT_DIR` environment variables are set, they will be used instead of the system root CA certificates. | Multiple configuration file formats are supported: you can use a [`.json5`](https://json5.org/) file, a [`.toml`](https://toml.io/) file, or a [`.yaml`](https://en.wikipedia.org/wiki/YAML#Syntax) file. diff --git a/src/app_config.rs b/src/app_config.rs index 95e87d5e..daf20e43 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -100,6 +100,12 @@ pub struct AppConfig { /// Content-Security-Policy header to send to the client. If not set, a default policy allowing scripts from the same origin is used and from jsdelivr.net pub content_security_policy: Option, + + /// Whether `sqlpage.fetch` should load trusted certificates from the operating system's certificate store + /// By default, it loads Mozilla's root certificates that are embedded in the `SQLPage` binary, or the ones pointed to by the + /// `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables. + #[serde(default = "default_system_root_ca_certificates")] + pub system_root_ca_certificates: bool, } impl AppConfig { @@ -322,6 +328,10 @@ fn default_compress_responses() -> bool { true } +fn default_system_root_ca_certificates() -> bool { + std::env::var("SSL_CERT_FILE").is_ok() || std::env::var("SSL_CERT_DIR").is_ok() +} + #[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, Eq, Default)] #[serde(rename_all = "lowercase")] pub enum DevOrProd { diff --git a/src/webserver/content_security_policy.rs b/src/webserver/content_security_policy.rs index dab636c1..988ad2d8 100644 --- a/src/webserver/content_security_policy.rs +++ b/src/webserver/content_security_policy.rs @@ -8,8 +8,8 @@ pub struct ContentSecurityPolicy { pub nonce: u64, } -impl ContentSecurityPolicy { - pub fn new() -> Self { +impl Default for ContentSecurityPolicy { + fn default() -> Self { Self { nonce: random() } } } diff --git a/src/webserver/database/sqlpage_functions/functions.rs b/src/webserver/database/sqlpage_functions/functions.rs index 6ece4b9f..95be8682 100644 --- a/src/webserver/database/sqlpage_functions/functions.rs +++ b/src/webserver/database/sqlpage_functions/functions.rs @@ -22,7 +22,7 @@ super::function_definition_macro::sqlpage_functions! { environment_variable(name: Cow); exec((&RequestInfo), program_name: Cow, args: Vec>); - fetch(http_request: SqlPageFunctionParam>); + fetch((&RequestInfo), http_request: SqlPageFunctionParam>); hash_password(password: Option); header((&RequestInfo), name: Cow); @@ -129,12 +129,12 @@ async fn exec<'a>( } async fn fetch( + request: &RequestInfo, http_request: super::http_fetch_request::HttpFetchRequest<'_>, ) -> anyhow::Result { use awc::http::Method; - let client = awc::Client::builder() - .add_default_header((awc::http::header::USER_AGENT, env!("CARGO_PKG_NAME"))) - .finish(); + let client = make_http_client(&request.app_state.config); + let method = if let Some(method) = http_request.method { Method::from_str(&method)? } else { @@ -174,6 +174,27 @@ async fn fetch( Ok(response_str) } +fn make_http_client(config: &crate::app_config::AppConfig) -> awc::Client { + let connector = if config.system_root_ca_certificates { + let mut roots = rustls::RootCertStore::empty(); + for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") + { + roots.add(cert).unwrap(); + } + let tls_conf = rustls::ClientConfig::builder() + .with_root_certificates(roots) + .with_no_client_auth(); + + awc::Connector::new().rustls_0_22(std::sync::Arc::new(tls_conf)) + } else { + awc::Connector::new() + }; + awc::Client::builder() + .connector(connector) + .add_default_header((awc::http::header::USER_AGENT, env!("CARGO_PKG_NAME"))) + .finish() +} + pub(crate) async fn hash_password(password: Option) -> anyhow::Result> { let Some(password) = password else { return Ok(None); diff --git a/src/webserver/http.rs b/src/webserver/http.rs index ed445fc0..3d913fba 100644 --- a/src/webserver/http.rs +++ b/src/webserver/http.rs @@ -254,7 +254,7 @@ async fn render_sql( actix_web::rt::spawn(async move { let request_context = RequestContext { is_embedded: req_param.get_variables.contains_key("_sqlpage_embed"), - content_security_policy: ContentSecurityPolicy::new(), + content_security_policy: ContentSecurityPolicy::default(), }; let mut conn = None; let database_entries_stream = @@ -598,15 +598,15 @@ pub async fn run_server(config: &AppConfig, state: AppState) -> anyhow::Result<( } } - let (r, _) = tokio::join!(server.run(), log_welcome_message(&config)); - r.with_context(|| "Unable to start the application") + log_welcome_message(config); + server.run().await.with_context(|| "Unable to start the application") } -async fn log_welcome_message(config: &AppConfig) { +fn log_welcome_message(config: &AppConfig) { let address_message = if let Some(unix_socket) = &config.unix_socket { format!("unix socket {unix_socket:?}") } else if let Some(domain) = &config.https_domain { - format!("https://{}", domain) + format!("https://{domain}") } else { use std::fmt::Write; let listen_on = config.listen_on(); From 2f7087c472bd3526c5e0b0ddc9ad462928ce89fa Mon Sep 17 00:00:00 2001 From: lovasoa Date: Mon, 5 Aug 2024 18:54:01 +0200 Subject: [PATCH 31/42] avoid repetitively reloading system CA certificates --- src/webserver/content_security_policy.rs | 2 +- .../database/sqlpage_functions/functions.rs | 31 ++++++++++++------- src/webserver/http.rs | 5 ++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/webserver/content_security_policy.rs b/src/webserver/content_security_policy.rs index 988ad2d8..ecfb5edb 100644 --- a/src/webserver/content_security_policy.rs +++ b/src/webserver/content_security_policy.rs @@ -8,7 +8,7 @@ pub struct ContentSecurityPolicy { pub nonce: u64, } -impl Default for ContentSecurityPolicy { +impl Default for ContentSecurityPolicy { fn default() -> Self { Self { nonce: random() } } diff --git a/src/webserver/database/sqlpage_functions/functions.rs b/src/webserver/database/sqlpage_functions/functions.rs index 95be8682..8c738976 100644 --- a/src/webserver/database/sqlpage_functions/functions.rs +++ b/src/webserver/database/sqlpage_functions/functions.rs @@ -10,7 +10,7 @@ use crate::webserver::{ use anyhow::{anyhow, Context}; use futures_util::StreamExt; use mime_guess::mime; -use std::{borrow::Cow, ffi::OsStr, str::FromStr}; +use std::{borrow::Cow, ffi::OsStr, str::FromStr, sync::OnceLock}; super::function_definition_macro::sqlpage_functions! { basic_auth_password((&RequestInfo)); @@ -133,7 +133,7 @@ async fn fetch( http_request: super::http_fetch_request::HttpFetchRequest<'_>, ) -> anyhow::Result { use awc::http::Method; - let client = make_http_client(&request.app_state.config); + let client = make_http_client(&request.app_state.config)?; let method = if let Some(method) = http_request.method { Method::from_str(&method)? @@ -174,25 +174,34 @@ async fn fetch( Ok(response_str) } -fn make_http_client(config: &crate::app_config::AppConfig) -> awc::Client { +static NATIVE_CERTS: OnceLock> = OnceLock::new(); + +fn make_http_client(config: &crate::app_config::AppConfig) -> anyhow::Result { let connector = if config.system_root_ca_certificates { - let mut roots = rustls::RootCertStore::empty(); - for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") - { - roots.add(cert).unwrap(); - } + let roots = NATIVE_CERTS + .get_or_init(|| { + let certs = rustls_native_certs::load_native_certs()?; + let mut roots = rustls::RootCertStore::empty(); + for cert in certs { + roots.add(cert.clone()).unwrap(); + } + Ok(roots) + }) + .as_ref()?; + let tls_conf = rustls::ClientConfig::builder() - .with_root_certificates(roots) + .with_root_certificates(roots.clone()) .with_no_client_auth(); awc::Connector::new().rustls_0_22(std::sync::Arc::new(tls_conf)) } else { awc::Connector::new() }; - awc::Client::builder() + let client = awc::Client::builder() .connector(connector) .add_default_header((awc::http::header::USER_AGENT, env!("CARGO_PKG_NAME"))) - .finish() + .finish(); + Ok(client) } pub(crate) async fn hash_password(password: Option) -> anyhow::Result> { diff --git a/src/webserver/http.rs b/src/webserver/http.rs index 3d913fba..22e37275 100644 --- a/src/webserver/http.rs +++ b/src/webserver/http.rs @@ -599,7 +599,10 @@ pub async fn run_server(config: &AppConfig, state: AppState) -> anyhow::Result<( } log_welcome_message(config); - server.run().await.with_context(|| "Unable to start the application") + server + .run() + .await + .with_context(|| "Unable to start the application") } fn log_welcome_message(config: &AppConfig) { From 3bd86668a36a747d2f705291f10ad4054efc1067 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Mon, 5 Aug 2024 18:57:07 +0200 Subject: [PATCH 32/42] fix yaml newlines error --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index b242558a..f4483b06 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -21,7 +21,7 @@ jobs: cache: 'npm' cache-dependency-path: ./tests/end-to-end/package-lock.json - name: Install dependencies - run: > + run: | npm ci npx playwright install --with-deps chromium - name: build sqlpage From f382c952322b016f92ff2604e1cffaa7d576a566 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Mon, 5 Aug 2024 18:57:41 +0200 Subject: [PATCH 33/42] yaml newline fix --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f4483b06..7aace4f9 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -29,7 +29,7 @@ jobs: working-directory: ./examples/official-site - name: start official site and wait for it to be ready timeout-minutes: 1 - run: > + run: | cargo run 2>/tmp/stderrlog & tail -f /tmp/stderrlog | grep -q "started successfully" working-directory: ./examples/official-site From 005b562fb643e37488411c92a9d84cd2403d266d Mon Sep 17 00:00:00 2001 From: lovasoa Date: Mon, 5 Aug 2024 19:05:57 +0200 Subject: [PATCH 34/42] fix e2e results upload --- .github/workflows/playwright.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7aace4f9..46235b79 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -42,5 +42,5 @@ jobs: if: always() with: name: playwright-report - path: playwright-report/ + path: ./tests/end-to-end/playwright-report/ retention-days: 30 \ No newline at end of file From 7d1f6424e3005394c6daf374b4d703f01ef98d6c Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Aug 2024 06:54:00 +0200 Subject: [PATCH 35/42] configuration documentation --- configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration.md b/configuration.md index 18658f71..359a98c9 100644 --- a/configuration.md +++ b/configuration.md @@ -30,7 +30,7 @@ Here are the available configuration options and their default values: | `https_certificate_cache_dir` | ./sqlpage/https | A writeable directory where to cache the certificates, so that SQLPage can serve https traffic immediately when it restarts. | | `https_acme_directory_url` | https://acme-v02.api.letsencrypt.org/directory | The URL of the ACME directory to use when requesting a certificate. | | `environment` | development | The environment in which SQLPage is running. Can be either `development` or `production`. In `production` mode, SQLPage will hide error messages and stack traces from the user, and will cache sql files in memory to avoid reloading them from disk. | -| `content_security_policy` | | The [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to set in the HTTP headers. If you get CSP errors in the browser console, you can set this to the empty string to disable CSP. | +| `content_security_policy` | `script-src 'self' 'nonce-XXX` | The [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) to set in the HTTP headers. If you get CSP errors in the browser console, you can set this to the empty string to disable CSP. | | `system_root_ca_certificates` | false | Whether to use the system root CA certificates to validate SSL certificates when making http requests with `sqlpage.fetch`. If set to false, SQLPage will use its own set of root CA certificates. If the `SSL_CERT_FILE` or `SSL_CERT_DIR` environment variables are set, they will be used instead of the system root CA certificates. | Multiple configuration file formats are supported: From 58b2b4d1755b33e36a5a56334ec704cfab93532c Mon Sep 17 00:00:00 2001 From: lovasoa Date: Tue, 6 Aug 2024 07:09:00 +0200 Subject: [PATCH 36/42] Custom layouts! Finally ! Adds a `width` parameter in the card component fixes https://github.com/lovasoa/SQLpage/issues/476 --- CHANGELOG.md | 2 ++ .../migrations/31_card_docs_update.sql | 13 +++++---- sqlpage/templates/card.handlebars | 28 +++++++++---------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ca470a..137f2e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ - fix [datagrid](https://sql.ophir.dev/documentation.sql?component=datagrid#component) color pills display when they contain long text. - fix the "started successfully" message being displayed before the error message when the server failed to start. - add support for using the system's native SSL Certificate Authority (CA) store in `sqlpage.fetch`. See the new `system_root_ca_certificates` configuration option. + - New `width` attribute in the [card](https://sql.ophir.dev/documentation.sql?component=card#component) component to set the width of the card. This finally allows you to create custom layouts, by combining the `embed` and `width` attributes of the card component. + - This also updates the default layout of the card component: when `columns` is not set, there is now a default of 4 columns instead of 5. ## 0.25.0 (2024-07-13) diff --git a/examples/official-site/sqlpage/migrations/31_card_docs_update.sql b/examples/official-site/sqlpage/migrations/31_card_docs_update.sql index e68a16f1..901f7c76 100644 --- a/examples/official-site/sqlpage/migrations/31_card_docs_update.sql +++ b/examples/official-site/sqlpage/migrations/31_card_docs_update.sql @@ -7,7 +7,7 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S ('title', 'Text header at the top of the list of cards.', 'TEXT', TRUE, TRUE), ('description', 'A short paragraph displayed below the title.', 'TEXT', TRUE, TRUE), ('description_md', 'A short paragraph displayed below the title - formatted using markdown.', 'TEXT', TRUE, TRUE), - ('columns', 'The number of columns in the grid of cards. This is just a hint, the grid will adjust dynamically to the user''s screen size, rendering fewer columns if needed to fit the contents.', 'INTEGER', TRUE, TRUE), + ('columns', 'The number of columns in the grid of cards. This is just a hint, the grid will adjust dynamically to the user''s screen size, rendering fewer columns if needed to fit the contents. To control the size of cards individually, use the `width` row-level property instead.', 'INTEGER', TRUE, TRUE), -- item level ('title', 'Name of the card, displayed at the top.', 'TEXT', FALSE, FALSE), ('description', 'The body of the card, where you put the main text contents of the card. @@ -26,7 +26,8 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S ('icon', 'Name of an icon to display on the left side of the card.', 'ICON', FALSE, TRUE), ('color', 'The name of a color, to be displayed on the left of the card to highlight it.', 'COLOR', FALSE, TRUE), ('background_color', 'The background color of the card.', 'COLOR', FALSE, TRUE), - ('active', 'Whether this item in the grid is considered "active". Active items are displayed more prominently.', 'BOOLEAN', FALSE, TRUE) + ('active', 'Whether this item in the grid is considered "active". Active items are displayed more prominently.', 'BOOLEAN', FALSE, TRUE), + ('width', 'The width of the card, between 1 (smallest) and 12 (full-width). The default width is 3, resulting in 4 cards per line.', 'INTEGER', FALSE, TRUE) ) x; INSERT INTO parameter(component, name, description_md, type, top_level, optional) SELECT 'card', * FROM (VALUES ('embed', 'A url whose contents will be fetched and injected into the body of this card. @@ -61,12 +62,12 @@ INSERT INTO example(component, description, properties) VALUES {"title": "Squirrel", "description_md": "The **chipmunk** is a small, striped rodent of the family Sciuridae. Chipmunks are found in North America, with the exception of the Siberian chipmunk which is found primarily in Asia.", "top_image": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Tamias-rufus-001.jpg/640px-Tamias-rufus-001.jpg" }, {"title": "Spider", "description_md": "The **jumping spider family** (_Salticidae_) contains more than 600 described genera and about *6000 described species*, making it the largest family of spiders with about 13% of all species.", "top_image": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ab/Jumping_spiders_%28Salticidae%29.jpg/640px-Jumping_spiders_%28Salticidae%29.jpg" } ]')), - ('card', 'Beautifully colored cards', + ('card', 'Beautifully colored cards with variable width. The blue card (width 6) takes half the screen, whereas of the red and green cards have the default width of 3', json('[ - {"component":"card", "title":"Beautifully colored cards", "columns": 3}, + {"component":"card", "title":"Beautifully colored cards" }, {"title": "Red card", "color": "red", "background_color": "red-lt", "description": "Penalty! You are out!", "icon":"play-football" }, - {"title": "Green card", "color": "green", "background_color": "green-lt", "description": "Welcome to the United States of America !", "icon":"user-dollar" }, - {"title": "Blue card", "color": "blue", "background_color": "blue-lt", "description": "The Blue Card facilitates migration of foreigners to Europe.", "icon":"currency-euro" } + {"title": "Blue card", "color": "blue", "width": 6, "background_color": "blue-lt", "description": "The Blue Card facilitates migration of foreigners to Europe.", "icon":"currency-euro" }, + {"title": "Green card", "color": "green", "background_color": "green-lt", "description": "Welcome to the United States of America !", "icon":"user-dollar" } ]')), ('card', 'Cards with remote content', json('[ diff --git a/sqlpage/templates/card.handlebars b/sqlpage/templates/card.handlebars index 615f6691..db21c086 100644 --- a/sqlpage/templates/card.handlebars +++ b/sqlpage/templates/card.handlebars @@ -7,22 +7,22 @@ {{#if description_md}} {{{markdown description_md}}} {{/if}} -
+ {{/if}}"> {{#each_row}} -
+
Date: Tue, 6 Aug 2024 21:49:08 +0200 Subject: [PATCH 38/42] update docker to rust 1.80 --- Dockerfile | 2 +- lambda.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index a5ffb7c1..aef94289 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM rust:1.79-slim AS builder +FROM --platform=$BUILDPLATFORM rust:1.80-slim AS builder WORKDIR /usr/src/sqlpage ARG TARGETARCH ARG BUILDARCH diff --git a/lambda.Dockerfile b/lambda.Dockerfile index f1586ba0..27cef7f7 100644 --- a/lambda.Dockerfile +++ b/lambda.Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.79-alpine as builder +FROM rust:1.80-alpine as builder RUN rustup component add clippy rustfmt RUN apk add --no-cache musl-dev zip WORKDIR /usr/src/sqlpage From f57a58b89580006b822a7c389d99602969ebf08f Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 6 Aug 2024 22:46:56 +0200 Subject: [PATCH 39/42] Update CHANGELOG.md --- CHANGELOG.md | 54 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 137f2e4c..824d9531 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,56 @@ # CHANGELOG.md -## 0.26.0 +## 0.26.0 (2024-08-06) +### Components +#### Card +New `width` attribute in the [card](https://sql.ophir.dev/documentation.sql?component=card#component) component to set the width of the card. This finally allows you to create custom layouts, by combining the `embed` and `width` attributes of the card component! This also updates the default layout of the card component: when `columns` is not set, there is now a default of 4 columns instead of 5. -- fix ugly wrapping of items in the header when the page title is long. We now have a nice text ellipsis (...) when the title is too long. -- re-add a link to the website title in the shell component -- add `text` and `post_html` properties to the [html](https://sql.ophir.dev/documentation.sql?component=html#component) component. This allows to include sanitized user-generated content in the middle of custom HTML. - - allow loading javascript ESM modules in the shell component +![image](https://github.com/user-attachments/assets/98425bd8-c576-4628-9ae2-db3ba4650019) + + +#### Datagrid +fix [datagrid](https://sql.ophir.dev/documentation.sql?component=datagrid#component) color pills display when they contain long text. + +![image](https://github.com/user-attachments/assets/3b7dba27-8812-410c-a383-2b62d6a286ac) + +#### Table +Fixed a bug that could cause issues with other components when a table was empty. +Improved handling of empty tables. Added a new `empty_description` attribute, which defaults to `No data`. This allows you to display a custom message when a table is empty. + +![image](https://github.com/user-attachments/assets/c370f841-20c5-4cbf-8c9e-7318dce9b87c) + +#### Form + - Fixed a bug where a form input with a value of `0` would diplay as empty instead of showing the `0`. + - Reduced the margin at the botton of forms to fix the appearance of forms that are validated by a `button` component declared separately from the form. + +#### Shell +Fixed ugly wrapping of items in the header when the page title is long. We now have a nice text ellipsis (...) when the title is too long. +![image](https://github.com/user-attachments/assets/3ac22d98-dde5-49c2-8f72-45ee7595fe82) + +Fixed the link to the website title in the shell component. + +Allow loading javascript ESM modules in the shell component with the new `javascript_module` property. + +#### html +Added `text` and `post_html` properties to the [html](https://sql.ophir.dev/documentation.sql?component=html#component) component. This allows to include sanitized user-generated content in the middle of custom HTML. + +```sql +select + 'html' as component; +select + 'Username: ' as html, + 'username that will be safely escaped: <"& ' as text, + '' as post_html; +``` + +### Other - allow customizing the [Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy) in the configuration. - the new default *content security policy* is both more secure and easier to use. You can now include inline javascript in your custom components with ``. - update to [sqlparser v0.49.0](https://github.com/sqlparser-rs/sqlparser-rs/blob/main/CHANGELOG.md#0490-2024-07-23) - support [`WITH ORDINALITY`](https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-TABLEFUNCTIONS) in postgres `FROM` clauses - update to [handlebars-rs v6](https://github.com/sunng87/handlebars-rust/blob/master/CHANGELOG.md#600---2024-07-20) - - Improved handling of empty tables. Added a new `empty_description` attribute, which defaults to `No data`. This allows you to display a custom message when a table is empty. - - fixed a bug where a form input with a value of `0` would diplay as empty instead of showing the `0`. - - reduce the margin at the botton of forms to make them more compact. - - fix [datagrid](https://sql.ophir.dev/documentation.sql?component=datagrid#component) color pills display when they contain long text. - fix the "started successfully" message being displayed before the error message when the server failed to start. - add support for using the system's native SSL Certificate Authority (CA) store in `sqlpage.fetch`. See the new `system_root_ca_certificates` configuration option. - - New `width` attribute in the [card](https://sql.ophir.dev/documentation.sql?component=card#component) component to set the width of the card. This finally allows you to create custom layouts, by combining the `embed` and `width` attributes of the card component. - - This also updates the default layout of the card component: when `columns` is not set, there is now a default of 4 columns instead of 5. ## 0.25.0 (2024-07-13) From ab8fd079ad221584f82e1f7baf107f055b20d0ef Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 7 Aug 2024 07:21:00 +0200 Subject: [PATCH 40/42] README badges --- README.md | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index ab16ae81..c5843625 100644 --- a/README.md +++ b/README.md @@ -130,21 +130,6 @@ select - [MySQL](https://www.mysql.com/), and other compatible databases such as *MariaDB* and *TiDB*. - [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server), and all compatible databases and providers such as *Azure SQL* and *Amazon RDS*. -## How it works - -![architecture diagram](./docs/architecture-detailed.png) - -SQLPage is a [web server](https://en.wikipedia.org/wiki/Web_server) written in -[rust](https://en.wikipedia.org/wiki/Rust_(programming_language)) -and distributed as a single executable file. -When it receives a request to a URL ending in `.sql`, it finds the corresponding -SQL file, runs it on the database, -passing it information from the web request as SQL statement parameters. -When the database starts returning rows for the query, -SQLPage maps each piece of information in the row to a parameter -in one of its pre-defined components' templates, and streams the result back -to the user's browser. - ## Get started [Read the official *get started* guide on SQLPage's website](https://sql.ophir.dev/get_started.sql). @@ -191,10 +176,24 @@ An alternative for Mac OS users is to use [SQLPage's homebrew package](https://f - In a terminal, run the following commands: - `brew install sqlpage` +## How it works + +![architecture diagram](./docs/architecture-detailed.png) + +SQLPage is a [web server](https://en.wikipedia.org/wiki/Web_server) written in +[rust](https://en.wikipedia.org/wiki/Rust_(programming_language)) +and distributed as a single executable file. +When it receives a request to a URL ending in `.sql`, it finds the corresponding +SQL file, runs it on the database, +passing it information from the web request as SQL statement parameters. +When the database starts returning rows for the query, +SQLPage maps each piece of information in the row to a parameter +in one of its pre-defined components' templates, and streams the result back +to the user's browser. ## Examples - - [TODO list](./examples/todo%20application/): a simple todo list application, illustrating how to create a basic CRUD application with SQLPage. +- [TODO list](./examples/todo%20application/): a simple todo list application, illustrating how to create a basic CRUD application with SQLPage. - [Plots, Tables, forms, and interactivity](./examples/plots%20tables%20and%20forms/): a short well-commented demo showing how to use plots, tables, forms, and interactivity to filter data based on an URL parameter. - [Tiny splitwise clone](./examples/splitwise): a shared expense tracker app - [Corporate Conundrum](./examples/corporate-conundrum/): a board game implemented in SQL @@ -208,7 +207,7 @@ An alternative for Mac OS users is to use [SQLPage's homebrew package](https://f - [Advanced authentication example using PostgreSQL stored procedures](https://github.com/mnesarco/sqlpage_auth_example) - [Complex web application in SQLite with user management, file uploads, plots, maps, tables, menus, ...](https://github.com/DSMejantel/Ecole_inclusive) - [Single sign-on](./examples/single%20sign%20on): An example of how to implement OAuth and OpenID Connect (OIDC) authentication in SQLPage. The demo also includes a CAS (Central Authentication Service) client. - - [Dark theme](./examples/light-dark-toggle/) : demonstrates how to let the user toggle between a light theme and a dark theme, and store the user's preference. +- [Dark theme](./examples/light-dark-toggle/) : demonstrates how to let the user toggle between a light theme and a dark theme, and store the user's preference. You can try all the examples online without installing anything on your computer using [SQLPage's online demo on replit](https://replit.com/@pimaj62145/SQLPage). @@ -315,3 +314,14 @@ We provide a set of components that look decent out of the box, so that you can However, if you really want to write your own HTML and CSS, you can do it by creating your own components. Just create a [`.handlebars`](https://handlebarsjs.com/guide/) file in `sqlpage/templates` and write your HTML and CSS in it. ([example](./sqlpage/templates/alert.handlebars)) + +## Download + +SQLPage is available for download on the from multiple sources: + +[![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/lovasoa/sqlpage/total?label=direct%20download)](https://github.com/lovasoa/SQLpage/releases/latest) +[![Docker Pulls](https://img.shields.io/docker/pulls/lovasoa/sqlpage?label=docker%3A%20lovasoa%2Fsqlpage)](https://hub.docker.com/r/lovasoa/sqlpage) +[![homebrew downloads](https://img.shields.io/homebrew/installs/dq/sqlpage?label=homebrew%20downloads&labelColor=%232e2a24&color=%23f9d094)](https://formulae.brew.sh/formula/sqlpage#default) +[![Scoop Version](https://img.shields.io/scoop/v/sqlpage?labelColor=%23696573&color=%23d7d4db)](https://scoop.sh/#/apps?q=sqlpage&id=305b3437817cd197058954a2f76ac1cf0e444116) +[![Crates.io Total Downloads](https://img.shields.io/crates/d/sqlpage?label=crates.io%20download&labelColor=%23264323&color=%23f9f7ec)](https://crates.io/crates/sqlpage) +[![](https://img.shields.io/badge/Nix-pkg-rgb(126,%20185,%20227))](https://search.nixos.org/packages?channel=unstable&show=sqlpage&from=0&size=50&sort=relevance&type=packages&query=sqlpage) \ No newline at end of file From 8bb4f4546fabd74c4fdb1905b915e135bcdd9035 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Wed, 7 Aug 2024 19:38:12 +0200 Subject: [PATCH 41/42] dynamic component docs --- .../sqlpage/migrations/01_documentation.sql | 78 ++++++++++--------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/examples/official-site/sqlpage/migrations/01_documentation.sql b/examples/official-site/sqlpage/migrations/01_documentation.sql index cbbd1e6f..06c0b0a3 100644 --- a/examples/official-site/sqlpage/migrations/01_documentation.sql +++ b/examples/official-site/sqlpage/migrations/01_documentation.sql @@ -738,15 +738,53 @@ INSERT INTO example(component, description, properties) VALUES INSERT INTO component(name, icon, description) VALUES - ('dynamic', 'repeat', 'A special component that can be used to render other components, the number and properties of which are not known in advance.'); + ('dynamic', 'repeat', 'Renders other components, given their properties as JSON. +If you are looking for a way to run FOR loops, to share similar code between pages of your site, +or to render multiple components for every line returned by your SQL query, then this is the component to use'); INSERT INTO parameter(component, name, description, type, top_level, optional) SELECT 'dynamic', * FROM (VALUES -- top level - ('properties', 'A json object or array that contains the names and properties of other components', 'JSON', TRUE, TRUE) + ('properties', 'A json object or array that contains the names and properties of other components.', 'JSON', TRUE, TRUE) ) x; INSERT INTO example(component, description, properties) VALUES - ('dynamic', 'Rendering a text paragraph dynamically.', json('[{"component":"dynamic", "properties": "[{\"component\":\"text\"}, {\"contents\":\"Blah\", \"bold\":true}]"}]')), + ('dynamic', 'The dynamic component has a single top-level property named `properties`, but it can render any number of other components. +Let''s start with something simple to illustrate the logic. We''ll render a `text` component with two row-level properties: `contents` and `bold`. +', json('[{"component":"dynamic", "properties": "[{\"component\":\"text\"}, {\"contents\":\"Blah\", \"bold\":true}]"}]')), + ('dynamic', ' +## Static component data stored in `.json` files + +You can also store the data for a component in a `.json` file, and load it using the `dynamic` component. + +This can be useful to store the data for a component in a separate file, +shared between multiple pages, +and avoid having to escape quotes in SQL strings. + +For instance, the following query will load the data for a `shell` component from the file `shell.json`: + +```sql +SELECT ''dynamic'' AS component, sqlpage.read_file_as_text(''shell.json'') AS properties; +``` + +and `shell.json` would be placed at the website''s root and contain the following: + +```json +{ + "component": "shell", + "title": "SQLPage documentation", + "link": "/", + "menu_item": [ + {"link": "index.sql", "title": "Home"}, + {"title": "Community", "submenu": [ + {"link": "blog.sql", "title": "Blog"}, + {"link": "https//github.com/lovasoa/sqlpage/issues", "title": "Issues"}, + {"link": "https//github.com/lovasoa/sqlpage/discussions", "title": "Discussions"}, + {"link": "https//github.com/lovasoa/sqlpage", "title": "Github"} + ]} + ] +} +``` +', NULL), ('dynamic', ' ## Dynamic shell @@ -793,40 +831,6 @@ SELECT ''dynamic'' AS component, json_object( [View the result of this query, as well as an example of how to generate a dynamic menu based on the database contents](./examples/dynamic_shell.sql). -', NULL), - ('dynamic', ' -## Static component data stored in `.json` files - -You can also store the data for a component in a `.json` file, and load it using the `dynamic` component. - -This can be useful to store the data for a component in a separate file, -shared between multiple pages, -and avoid having to escape quotes in SQL strings. - -For instance, the following query will load the data for a `shell` component from the file `shell.json`: - -```sql -SELECT ''dynamic'' AS component, sqlpage.read_file_as_text(''shell.json'') AS properties; -``` - -and `shell.json` would be placed at the website''s root and contain the following: - -```json -{ - "component": "shell", - "title": "SQLPage documentation", - "link": "/", - "menu_item": [ - {"link": "index.sql", "title": "Home"}, - {"title": "Community", "submenu": [ - {"link": "blog.sql", "title": "Blog"}, - {"link": "https//github.com/lovasoa/sqlpage/issues", "title": "Issues"}, - {"link": "https//github.com/lovasoa/sqlpage/discussions", "title": "Discussions"}, - {"link": "https//github.com/lovasoa/sqlpage", "title": "Github"} - ]} - ] -} -``` ', NULL); INSERT INTO component(name, icon, description) VALUES From c46f817695a39c7990c73dff8c4933a9d34e0948 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 8 Aug 2024 07:52:23 +0200 Subject: [PATCH 42/42] dynamic docs --- .../sqlpage/migrations/01_documentation.sql | 59 +++++++------------ 1 file changed, 21 insertions(+), 38 deletions(-) diff --git a/examples/official-site/sqlpage/migrations/01_documentation.sql b/examples/official-site/sqlpage/migrations/01_documentation.sql index 06c0b0a3..df947013 100644 --- a/examples/official-site/sqlpage/migrations/01_documentation.sql +++ b/examples/official-site/sqlpage/migrations/01_documentation.sql @@ -749,18 +749,17 @@ INSERT INTO parameter(component, name, description, type, top_level, optional) S INSERT INTO example(component, description, properties) VALUES ('dynamic', 'The dynamic component has a single top-level property named `properties`, but it can render any number of other components. -Let''s start with something simple to illustrate the logic. We''ll render a `text` component with two row-level properties: `contents` and `bold`. -', json('[{"component":"dynamic", "properties": "[{\"component\":\"text\"}, {\"contents\":\"Blah\", \"bold\":true}]"}]')), +Let''s start with something simple to illustrate the logic. We''ll render a `text` component with two row-level properties: `contents` and `italics`. +', json('[{"component":"dynamic", "properties": "[{\"component\":\"text\"}, {\"contents\":\"Hello, I am a dynamic component !\", \"italics\":true}]"}]')), ('dynamic', ' ## Static component data stored in `.json` files You can also store the data for a component in a `.json` file, and load it using the `dynamic` component. -This can be useful to store the data for a component in a separate file, -shared between multiple pages, -and avoid having to escape quotes in SQL strings. +This is particularly useful to create a single [shell](?component=shell#component) defining the site''s overall appearance and menus, +and displaying it on all pages without duplicating its code. -For instance, the following query will load the data for a `shell` component from the file `shell.json`: +The following will load the data for a `shell` component from a file named `shell.json` : ```sql SELECT ''dynamic'' AS component, sqlpage.read_file_as_text(''shell.json'') AS properties; @@ -795,38 +794,22 @@ json data to pass to components that expect it. This example generates a menu similar to the [shell example](?component=shell#component), but without using a native JSON type. ```sql -SELECT ''dynamic'' AS component, json_object( - ''component'', ''shell'', - ''title'', ''SQLPage documentation'', - ''link'', ''/'', - ''menu_item'', json_array( - json_object( - ''link'', ''index.sql'', - ''title'', ''Home'' - ), - json_object( - ''title'', ''Community'', - ''submenu'', json_array( - json_object( - ''link'', ''blog.sql'', - ''title'', ''Blog'' - ), - json_object( - ''link'', ''//github.com/lovasoa/sqlpage/issues'', - ''title'', ''Issues'' - ), - json_object( - ''link'', ''//github.com/lovasoa/sqlpage/discussions'', - ''title'', ''Discussions'' - ), - json_object( - ''link'', ''//github.com/lovasoa/sqlpage'', - ''title'', ''Github'' - ) - ) - ) - ) -) AS properties +SELECT ''dynamic'' AS component, '' +{ + "component": "shell", + "title": "SQLPage documentation", + "link": "/", + "menu_item": [ + {"link": "index.sql", "title": "Home"}, + {"title": "Community", "submenu": [ + {"link": "blog.sql", "title": "Blog"}, + {"link": "https//github.com/lovasoa/sqlpage/issues", "title": "Issues"}, + {"link": "https//github.com/lovasoa/sqlpage/discussions", "title": "Discussions"}, + {"link": "https//github.com/lovasoa/sqlpage", "title": "Github"} + ]} + ] +} +'' AS properties ``` [View the result of this query, as well as an example of how to generate a dynamic menu