1topic "Skylark Tutorial";
2[l288;i1120;a17;O9;~~~.1408;2 $$1,0#10431211400427159095818037425705:param]
3[a83;*R6 $$2,5#31310162474203024125188417583966:caption]
4[H4;b83;*4 $$3,5#07864147445237544204411237157677:title]
5[i288;O9;C2 $$4,6#40027414424643823182269349404212:item]
6[b42;a42;ph2 $$5,5#45413000475342174754091244180557:text]
7[l288;b17;a17;2 $$6,6#27521748481378242620020725143825:desc]
8[l321;C@5;1 $$7,7#20902679421464641399138805415013:code]
9[b2503;2 $$8,0#65142375456100023862071332075487:separator]
10[*@(0.0.255)2 $$9,0#83433469410354161042741608181528:base]
11[C2 $$10,0#37138531426314131251341829483380:class]
12[l288;a17;*1 $$11,11#70004532496200323422659154056402:requirement]
13[i417;b42;a42;O9;~~~.416;2 $$12,12#10566046415157235020018451313112:tparam]
14[b167;C2 $$13,13#92430459443460461911108080531343:item1]
15[i288;a42;O9;C2 $$14,14#77422149456609303542238260500223:item2]
16[*@2$(0.128.128)2 $$15,15#34511555403152284025741354420178:NewsDate]
17[l321;*C$7;2 $$16,16#03451589433145915344929335295360:result]
18[l321;b83;a83;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line]
19[l160;*C+117 $$18,5#88603949442205825958800053222425:package`-title]
20[2 $$19,0#53580023442335529039900623488521:gap]
21[C2 $$20,20#70211524482531209251820423858195:class`-nested]
22[b50;2 $$21,21#03324558446220344731010354752573:Par]
23[2 $$0,0#00000000000000000000000000000000:Default]
24[{_}%EN-US
25[s2; Skylark Tutorial&]
26[s3; Table of contents&]
27[s0; &]
28[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#1^ 1. SKYLARK
29handlers and path patterns]&]
30[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#2^ 2. Witz templates]&]
31[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#3^ 3. Witz links
32to handlers]&]
33[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#4^ 4. Combining
34Witz templates using #define and #include]&]
35[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#5^ 5. FORM GET]&]
36[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#6^ 6. FORM POST]&]
37[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#7^ 7. Session
38variables]&]
39[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#8^ 8. Ajax support]&]
40[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#9^ 9. Connecting
41SQL database]&]
42[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#10^ 10. Advanced
43SQL]&]
44[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#11^ 11. Language
45support]&]
46[s0; [^topic`:`/`/Skylark`/srcdoc`/Tutorial`$en`-us`#12^ 12. Packs]&]
47[s0; &]
48[s3;:1: 1. SKYLARK handlers and path patterns&]
49[s5; Skylark application is divided to `'handlers`' which provide
50response to various HTTP requests based on url pattern:&]
51[s7; &]
52[s7; #include <Skylark/Skylark.h>&]
53[s7; &]
54[s7; using namespace Upp;&]
55[s7; &]
56[s7; [* SKYLARK](HomePage, `"`")&]
57[s7; `{&]
58[s7; -|http << `"<!DOCTYPE html><html><body>Hello world!</body></html>`";&]
59[s7; `}&]
60[s7; &]
61[s7; [* SKYLARK](Param, `"`*/param`")&]
62[s7; `{&]
63[s7; -|http << `"<!DOCTYPE html><html><body>Parameter: `" << http`[0`]
64<< `"</html></body>`";&]
65[s7; `}&]
66[s7; &]
67[s7; [* SKYLARK](Params, `"params/`*`*`")&]
68[s7; `{&]
69[s7; -|http << `"<!DOCTYPE html><html><body>Parameters: `";&]
70[s7; -|for(int i `= 0; i < http.GetParamCount(); i`+`+)&]
71[s7; -|-|http << http`[i`] << `" `";&]
72[s7; -|http << `"</html></body>`";&]
73[s7; `}&]
74[s7; &]
75[s7; [* SKYLARK](CatchAll, `"`*`*`")&]
76[s7; `{&]
77[s7; -|http.[* Redirect](HomePage);&]
78[s7; `}&]
79[s7; &]
80[s7; [* SKYLARK](Favicon, `"/favicon.ico`")&]
81[s7; `{&]
82[s7; -|http.ContentType(`"image/png`") << LoadFile(GetDataFile(`"favicon.png`"));&]
83[s7; `}&]
84[s7; &]
85[s7; struct MyApp : SkylarkApp `{&]
86[s7; -|MyApp() `{&]
87[s7; -|-|[* root `= `"myapp`";]&]
88[s7; -|#ifdef `_DEBUG&]
89[s7; -|-|prefork `= 0;&]
90[s7; -|-|use`_caching `= false;&]
91[s7; -|#endif&]
92[s7; -|`}&]
93[s7; `};&]
94[s7; &]
95[s7; CONSOLE`_APP`_MAIN&]
96[s7; `{&]
97[s7; #ifdef `_DEBUG&]
98[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
99[s7; -|Ini`::skylark`_log `= true;&]
100[s7; #endif&]
101[s7; &]
102[s7; -|[* MyApp().Run();]-|&]
103[s7; `}&]
104[s7; &]
105[s5; Handlers are introduced by macro [@5 SKYLARK], first parameters
106is the name of handler (macro creates a function that is named
107this way and this id is also used in various places like page
108templates) and path pattern which handler is supposed to respond
109to.&]
110[s5; Handler is represented by C`+`+ function with single parameter
111Http`& [/ http], which is used for request processing. [@5 SKYLARK]
112macro creates a header for this function and also registers it
113with Skylark so that requestes matching the path pattern are
114dispatched to this function. Important note: as [@5 SKYLARK] macro
115uses global constructors to register handlers, handlers should
116be put into .icpp files (unless they are in the same file with
117`'main`').&]
118[s5; Path pattern can contain parameter placeholders `'`*`' (see
119[@5 Param] handler), which are then provided by [/ http] as parameters
120(through operator`[`](int)). Path is always split by `'/`' characters
121and each part must either be text or placeholder `- patterns
122like `"something`*/to`*avoid`" are not allowed&]
123[s5; Special wildcard `'`*`*`' represents any other number of placeholders
124(see [@5 Params]); in special case it is useful to catch all unresolved
125requests to website and [@5 Redirect] them to the application homepage
126(see [@5 CatchAll]).&]
127[s5; The priority of various handlers for particular request is determined
128by number of text parts matched, which means that for request
129`"foo/bar/32`" placeholder `"foo/bar/`*`" has priority over `"foo/`*/`*`".&]
130[s5; Another concern is setting of root path for application, in
131code above done by `"[* root ]`= `"[@3 myapp]`";`". This defines
132the root path of application, in other words [@5 HomePage ]handler
133will react to `"[@3 myapp]`" path, Param handler to e.g. `"[@3 myapp]/test/param`"
134etc. In situation where this is not desirable, it is possible
135to exclude root by adding `'/`' at the start of path, as demonstrated
136by [@5 Favicon] handler.&]
137[s5; [@5 MyApp().Run()] starts a HTTP server (and also SCGI server)
138on default port 8001, so after starting application, you should
139be able to access it from your browser by entering &]
140[s5; 127.0.0.1:8001/myapp&]
141[s5; We then recommend to try e.g.&]
142[s0; 127.0.0.1:8001/myapp/anything/param&]
143[s0; 127.0.0.1:8001/myapp/params/1/2/3&]
144[s0; 127.0.0.1:8001/myapp/anythingelse&]
145[s0; 127.0.0.1:8001&]
146[s5; Last one should result in error `"Page not found`", as no matching
147handler is present.&]
148[s5; &]
149[s3;:2: 2. Witz templates&]
150[s5; To decouple presentation layer from application logic, Skylark
151features template language `'Witz`':&]
152[s7; &]
153[s7; #include <Skylark/Skylark.h>&]
154[s7; &]
155[s7; using namespace Upp;&]
156[s7; &]
157[s7; SKYLARK(HomePage, `"`")&]
158[s7; `{&]
159[s7; -|ValueArray va;&]
160[s7; -|va.Add(1);&]
161[s7; -|va.Add(`"Hello`");&]
162[s7; -|ValueMap m;&]
163[s7; -|m.Add(`"key1`", `"first value`");&]
164[s7; -|m.Add(`"key2`", `"second value`");&]
165[s7; &]
166[s7; -|[* http(`"MyValue`", `"some value`")]&]
167[s7; [* -|    (`"MyRawValue`", Raw(`"<b>raw <u>html</u></b>`"))]&]
168[s7; [* -|    (`"MyRawValue2`", `"<b>another raw <u>html</u></b>`")]&]
169[s7; [* -|    (`"MyArray`", va)]&]
170[s7; [* -|    (`"MyMap`", m)]&]
171[s7; [* -|    .RenderResult(`"Skylark02/index`");]&]
172[s7; `}&]
173[s7; &]
174[s7; struct MyApp : SkylarkApp `{&]
175[s7; -|MyApp() `{&]
176[s7; -|-|root `= `"myapp`";&]
177[s7; -|#ifdef `_DEBUG&]
178[s7; -|-|prefork `= 0;&]
179[s7; -|-|[* use`_caching ]`= false;&]
180[s7; -|#endif&]
181[s7; -|`}&]
182[s7; `};&]
183[s7; &]
184[s7; CONSOLE`_APP`_MAIN&]
185[s7; `{&]
186[s7; #ifdef `_DEBUG&]
187[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
188[s7; -|Ini`::skylark`_log `= true;&]
189[s7; #endif&]
190[s7; &]
191[s7; -|MyApp().Run();-|&]
192[s7; `}&]
193[s7; &]
194[s7; &]
195[s5; [* Skylark02/index.witz:]&]
196[s7; &]
197[s7; <!DOCTYPE html>&]
198[s7; <html>&]
199[s7; <body>&]
200[s7; MyValue: [* `$MyValue]<br>&]
201[s7; MyRawValue: [* `$MyRawValue]<br>&]
202[s7; MyRawValue2: [* `$raw(MyRawValue2)]<br>&]
203[s7; <br>MyArray:<br>&]
204[s7; [* `$for(i in MyArray)]&]
205[s7; [* -|`$i.`_index. `$i<br>]&]
206[s7; [* `$endfor]&]
207[s7; <br>MyMap:<br>&]
208[s7; [* `$for(i in MyMap)]&]
209[s7; [* -|`$i.`_index. `$i.`_key: `$i<br>]&]
210[s7; [* `$endfor]&]
211[s7; </body>&]
212[s7; </html>&]
213[s7; &]
214[s5; Witz template language loosely ressembles JavaScript. It is
215able to evaluate expression, supports `"if`" and looping through
216arrays or maps with `"for`".&]
217[s5; Witz templates are compiled at runtime. Settings configuration
218variable [* use`_caching] to false makes them to compile each time
219they are invoked (otherwise the compilation result is cached)
220`- this is usefull while debugging, as templates can be adjusted
221while application is running.&]
222[s5; The search path for templates is set by another configuration
223variable [* path]. Default value of [* path] is set to environment
224variable `'UPP`_ASSEMBLY`_`_`', which is set by theide to current
225assembly path `- which means that for debugging you do not need
226to worry about [* path] as long as all templates are in U`+`+ packages
227and you start the application from theide. Note that [* path] is
228also used to search for static files.&]
229[s5; String values are normally HTML escaped; if you need to pass
230raw html code as parameter, you have to either use [@5 Raw ]C`+`+
231function in application logic or [@5 raw] function in Witz.&]
232[s5; &]
233[s3;:3: 3. Witz links to handlers&]
234[s5; Skylark handlers are represented in witz templates as function
235calls or simple variables with the same identifiers as the id
236of handler; both yield `" quoted path that matches the handler
237path pattern. If pattern contains parameter placeholder, it is
238passed as argument of the function:&]
239[s7; &]
240[s7; #include <Skylark/Skylark.h>&]
241[s7; &]
242[s7; using namespace Upp;&]
243[s7; &]
244[s7; SKYLARK([* HomePage], `"`")&]
245[s7; `{&]
246[s7; -|http.RenderResult(`"Skylark03/index`");&]
247[s7; `}&]
248[s7; &]
249[s7; SKYLARK([* Page2], `"page2`")&]
250[s7; `{&]
251[s7; -|http.RenderResult(`"Skylark03/page2`");&]
252[s7; `}&]
253[s7; &]
254[s7; SKYLARK([* Param], `"paramtest/`*`")&]
255[s7; `{&]
256[s7; -|http(`"PARAM`", http`[0`]).RenderResult(`"Skylark03/param`");&]
257[s7; `}&]
258[s7; &]
259[s7; struct MyApp : SkylarkApp `{&]
260[s7; -|MyApp() `{&]
261[s7; -|-|root `= `"myapp`";&]
262[s7; -|#ifdef `_DEBUG&]
263[s7; -|-|prefork `= 0;&]
264[s7; -|-|use`_caching `= false;&]
265[s7; -|#endif&]
266[s7; -|`}&]
267[s7; `};&]
268[s7; &]
269[s7; CONSOLE`_APP`_MAIN&]
270[s7; `{&]
271[s7; #ifdef `_DEBUG&]
272[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
273[s7; -|Ini`::skylark`_log `= true;&]
274[s7; #endif&]
275[s7; &]
276[s7; -|MyApp().Run();-|&]
277[s7; `}&]
278[s7; &]
279[s5; [* Skylark03/index.witz:]&]
280[s7; &]
281[s7; <!DOCTYPE html>&]
282[s7; <html>&]
283[s7; <body>&]
284[s7; <a href`=[* `$Page2]>Link to page2</a><br>&]
285[s7; `$for(i in `[`"param`_test`", 123, `"param`_test3`"`])&]
286[s7; -|<a href`=[* `$Param(i)]>Param test: `$i</a><br>&]
287[s7; `$endfor&]
288[s7; </body>&]
289[s7; </html>&]
290[s7; &]
291[s5; [* Skylark03/page2.witz:]&]
292[s7; &]
293[s7; <!DOCTYPE html>&]
294[s7; <html>&]
295[s7; <body>&]
296[s7; <a href`=[* `$HomePage()]>Back to index</a><br>&]
297[s7; </body>&]
298[s7; </html>&]
299[s7; &]
300[s5; [* Skylark03/index.witz:]&]
301[s7; &]
302[s7; <!DOCTYPE html>&]
303[s7; <html>&]
304[s7; <body>&]
305[s7; <a href`=[* `$Page2()]>Link to page2</a><br>&]
306[s7; `$for(i in `[`"param`_test`", 123, `"param`_test3`"`])&]
307[s7; -|<a href`=[* `$Param(i)]>Param test: `$i</a><br>&]
308[s7; `$endfor&]
309[s7; </body>&]
310[s7; </html>&]
311[s7; &]
312[s5; &]
313[s3;:4: 4. Combining Witz templates using #define and #include&]
314[s5; Witz templates can be parametrized using subblock [@5 #define]
315[/@5 id] and [@5 #][/@5 id] insertion and combined from several files
316using [@5 #include]:&]
317[s5; [* Skylark04/base.witz:]&]
318[s7; &]
319[s7; <!DOCTYPE html>&]
320[s7; <html>&]
321[s7; <title>#TITLE</title>&]
322[s7; <body>&]
323[s7; #BODY&]
324[s7; </body>&]
325[s7; </html>&]
326[s7; &]
327[s7; #define TITLE Generic title&]
328[s7; &]
329[s5; [* Skylark04/index.witz:]&]
330[s7; &]
331[s7; #include Skylark04/base&]
332[s7; &]
333[s7; #define TITLE MyApp title&]
334[s7; &]
335[s7; #define BODY&]
336[s7; This is MyApp body html!&]
337[s7; &]
338[s5; &]
339[s3;:5: 5. FORM GET&]
340[s5; Whenever Skylark founds `'?`' sign in the request path, it converts
341attached values into http`'s [/ shared variable space], which is
342accessible using [* operator`[`]].&]
343[s7; &]
344[s7; #include <Skylark/Skylark.h>&]
345[s7; &]
346[s7; using namespace Upp;&]
347[s7; &]
348[s7; SKYLARK(HomePage, `"`")&]
349[s7; `{&]
350[s7; -|http.RenderResult(`"Skylark05/index`");&]
351[s7; `}&]
352[s7; &]
353[s7; SKYLARK([* Submit], `"submit`")&]
354[s7; `{&]
355[s7; -|http(`"[* RESULT]`", ToUpper((String)[* http`[`"id`"`]]))&]
356[s7; -|-|.RenderResult(`"Skylark05/submit`");&]
357[s7; `}&]
358[s7; &]
359[s7; struct MyApp : SkylarkApp `{&]
360[s7; -|MyApp() `{&]
361[s7; -|-|root `= `"myapp`";&]
362[s7; -|#ifdef `_DEBUG&]
363[s7; -|-|prefork `= 0;&]
364[s7; -|-|use`_caching `= false;&]
365[s7; -|#endif&]
366[s7; -|`}&]
367[s7; `};&]
368[s7; &]
369[s7; CONSOLE`_APP`_MAIN&]
370[s7; `{&]
371[s7; #ifdef `_DEBUG&]
372[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
373[s7; -|Ini`::skylark`_log `= true;&]
374[s7; #endif&]
375[s7; &]
376[s7; -|MyApp().Run();-|&]
377[s7; `}&]
378[s7; &]
379[s5; [* Skylark05/index.witz]&]
380[s7; &]
381[s7; <!DOCTYPE html>&]
382[s7; <html>&]
383[s7; <body>&]
384[s7; <form action`=[* `$Submit] method`=`"[* get]`" accept`-charset`=`"utf`-8`"
385enctype`=`"multipart/form`-data`">&]
386[s7;    <P>&]
387[s7;     <INPUT type`=`"text`" name`=`"[* id]`">&]
388[s7;     <INPUT type`=`"submit`" value`=`"Submit`">&]
389[s7;    </P>&]
390[s7; </form>&]
391[s7; </body>&]
392[s7; </html>&]
393[s7; &]
394[s5; [* Skylark05/submit.witz]&]
395[s7; &]
396[s7; <!DOCTYPE html>&]
397[s7; <html>&]
398[s7; <body>&]
399[s7; Result: [* `$RESULT]<br>&]
400[s7; <a href`=`$HomePage>Back to homepage</a>&]
401[s7; </body>&]
402[s7; </html>&]
403[s7; &]
404[s5; &]
405[s3;:6: 6. FORM POST&]
406[s5; If HTTP method is POST and content`-type field of header contains
407standard values (either `"application/x`-www`-form`-urlencoded`"
408or starts with `"multipart/`", Skylark parses the posted content
409and assigns posted values into shared variable space.&]
410[s5; This makes handling FORM POSTs similar to GET with one exception:
411To avoid [^http`:`/`/en`.wikipedia`.org`/wiki`/Cross`-site`_request`_forgery^ CSRF]
412attacks, it is mandatory to include hidden field into dialog
413definition provided by function [@5 `$post`_identity()] `- Skylark
414manages everything required using session subsystem.&]
415[s5; Request method is also part of handler path pattern (GET is
416default if not specified).&]
417[s7; &]
418[s7; #include <Skylark/Skylark.h>&]
419[s7; &]
420[s7; using namespace Upp;&]
421[s7; &]
422[s7; SKYLARK(HomePage, `"`")&]
423[s7; `{&]
424[s7; -|http.RenderResult(`"Skylark06/index`");&]
425[s7; `}&]
426[s7; &]
427[s7; SKYLARK(Submit, `"submit:[* POST]`")&]
428[s7; `{&]
429[s7; -|http(`"RESULT`", ToUpper((String)http`[`"id`"`]))&]
430[s7; -|-|.RenderResult(`"Skylark06/submit`");&]
431[s7; `}&]
432[s7; &]
433[s7; struct MyApp : SkylarkApp `{&]
434[s7; -|MyApp() `{&]
435[s7; -|-|root `= `"myapp`";&]
436[s7; -|#ifdef `_DEBUG&]
437[s7; -|-|prefork `= 0;&]
438[s7; -|-|use`_caching `= false;&]
439[s7; -|#endif&]
440[s7; -|`}&]
441[s7; `};&]
442[s7; &]
443[s7; CONSOLE`_APP`_MAIN&]
444[s7; `{&]
445[s7; #ifdef `_DEBUG&]
446[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
447[s7; -|Ini`::skylark`_log `= true;&]
448[s7; #endif&]
449[s7; &]
450[s7; -|MyApp().Run();-|&]
451[s7; `}&]
452[s7; &]
453[s5; [* Skylark06/index.witz:]&]
454[s7; &]
455[s7; <!DOCTYPE html>&]
456[s7; <html>&]
457[s7; <body>&]
458[s7; <form action`=`$Submit method`=`"[* post]`" accept`-charset`=`"utf`-8`"
459enctype`=`"multipart/form`-data`">&]
460[s7;    <P>&]
461[s7;     [* `$post`_identity()]&]
462[s7;     <INPUT type`=`"text`" name`=`"id`">&]
463[s7;     <INPUT type`=`"submit`" value`=`"Submit`">&]
464[s7;    </P>&]
465[s7; </form>&]
466[s7; </body>&]
467[s7; </html>&]
468[s7; &]
469[s7; &]
470[s5; [* Skylark06/submit.witz:]&]
471[s7; &]
472[s7; <!DOCTYPE html>&]
473[s7; <html>&]
474[s7; <body>&]
475[s7; Result: `$RESULT<br>&]
476[s7; <a href`=`$HomePage>Back to homepage</a>&]
477[s7; </body>&]
478[s7; </html>&]
479[s7; &]
480[s7; &]
481[s5; Note: There also exists alternative POST`_RAW method marker in
482SKYLARK handler definition `- such handler also reacts to POST
483requests, but it avoids identity checks to prevent CSRF attacks.&]
484[s5; &]
485[s3;:7: 7. Session variables&]
486[s5; Http`::SessioSet method can be used to store `'session variables`'
487that are persistent for specific browser across requests. Implementation
488is based on cookie, session variables are stored either in filesystem
489or in database (see Skylark configuration for details). Session
490variables are reflected in shared variable space (means its values
491are accessible using [* Http`::operator`[`]]) and are distinguished
492with `'.`' as the first character.&]
493[s7; &]
494[s7; #include <Skylark/Skylark.h>&]
495[s7; &]
496[s7; using namespace Upp;&]
497[s7; &]
498[s7; SKYLARK(HomePage, `"`")&]
499[s7; `{&]
500[s7; -|http.RenderResult(`"Skylark07/index`");&]
501[s7; `}&]
502[s7; &]
503[s7; SKYLARK(Submit, `"submit:POST`")&]
504[s7; `{&]
505[s7; -|Value h `= [* http`[`".LIST`"`];]&]
506[s7; -|ValueArray va;&]
507[s7; -|if(IsValueArray(h))&]
508[s7; -|-|va `= h;&]
509[s7; -|va.Add(http`[`"id`"`]);&]
510[s7; -|[* http.SessionSet(`".LIST`", va);]&]
511[s7; -|http.Redirect(HomePage);&]
512[s7; `}&]
513[s7; &]
514[s7; struct MyApp : SkylarkApp `{&]
515[s7; -|MyApp() `{&]
516[s7; -|-|root `= `"myapp`";&]
517[s7; -|#ifdef `_DEBUG&]
518[s7; -|-|prefork `= 0;&]
519[s7; -|-|use`_caching `= false;&]
520[s7; -|#endif&]
521[s7; -|`}&]
522[s7; `};&]
523[s7; &]
524[s7; CONSOLE`_APP`_MAIN&]
525[s7; `{&]
526[s7; #ifdef `_DEBUG&]
527[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
528[s7; -|Ini`::skylark`_log `= true;&]
529[s7; #endif&]
530[s7; &]
531[s7; -|MyApp().Run();-|&]
532[s7; `}&]
533[s7; &]
534[s5; [* Skylark07/index.witz:]&]
535[s7; &]
536[s7; <!DOCTYPE html>&]
537[s7; <html>&]
538[s7; <body>&]
539[s7; <form action`=`$Submit method`=`"post`" accept`-charset`=`"utf`-8`"
540enctype`=`"multipart/form`-data`">&]
541[s7;    <P>&]
542[s7;     `$post`_identity()&]
543[s7;     <INPUT type`=`"text`" name`=`"id`">&]
544[s7;     <INPUT type`=`"submit`" value`=`"Add to list`">&]
545[s7;    </P>&]
546[s7; </form>&]
547[s7; List (`$count([* .LIST])):<br>&]
548[s7; `$for(i in [* .LIST])&]
549[s7; -|`"`$i`" &]
550[s7; `$endfor&]
551[s7; </body>&]
552[s7; </html>&]
553[s7; &]
554[s5; &]
555[s3;:8: 8. Ajax support&]
556[s5; Skylark provides optional direct support for handling Ajax requests.
557On client side, this support is implemented using tiny JavaScript
558library `"skylark.js`".&]
559[s7; &]
560[s7; #include <Skylark/Skylark.h>&]
561[s7; &]
562[s7; using namespace Upp;&]
563[s7; &]
564[s7; SKYLARK(HomePage, `"`")&]
565[s7; `{&]
566[s7; -|http.RenderResult(`"Skylark08/index`");&]
567[s7; `}&]
568[s7; &]
569[s7; SKYLARK([* Add], `"add:POST`")&]
570[s7; `{&]
571[s7; -|String r `= AsString(Nvl(http.Int(`"number1`")) `+ Nvl(http.Int(`"number2`")));
572&]
573[s7; [* -|http.Ux(`"result`", `"The result is: `" `+ r)]&]
574[s7; [* -|    .UxSet(`"number1`", r);]&]
575[s7; `}&]
576[s7; &]
577[s7; struct MyApp : SkylarkApp `{&]
578[s7; -|MyApp() `{&]
579[s7; -|-|root `= `"myapp`";&]
580[s7; -|#ifdef `_DEBUG&]
581[s7; -|-|prefork `= 0;&]
582[s7; -|-|use`_caching `= false;&]
583[s7; -|#endif&]
584[s7; -|`}&]
585[s7; `};&]
586[s7; &]
587[s7; CONSOLE`_APP`_MAIN&]
588[s7; `{&]
589[s7; #ifdef `_DEBUG&]
590[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
591[s7; -|Ini`::skylark`_log `= true;&]
592[s7; #endif&]
593[s7; &]
594[s7; -|MyApp().Run();-|&]
595[s7; `}&]
596[s7; &]
597[s5; [* Skylark08/index.witz:]&]
598[s7; &]
599[s7; <!DOCTYPE html>&]
600[s7; <html>&]
601[s7; <script type`=`"text/javascript`" src`=`"[* `$static/Skylark/skylark.js]`"></scrip
602t>&]
603[s7; <body>&]
604[s7; [* `$js`_identity()]&]
605[s7; <div id`=`"[* numbers]`">&]
606[s7;     <INPUT type`=`"text`" id`=`"[* number1]`">&]
607[s7;     <INPUT type`=`"text`" id`=`"[* number2]`">&]
608[s7;     <INPUT type`=`"button`" value`=`"Add`" onclick`=`'[* UxPost(`$Add,
609`"numbers`")]`'><br>&]
610[s7; </div>&]
611[s7; <div id`=`"result`"/>&]
612[s7; </body>&]
613[s7; </html>&]
614[s7; &]
615[s5; [*@5 `$static] variable is set by Skylark to the [@5 static`_dir
616]configuration parameter, if missing it is set to [/ root]/static,
617which makes Skylark to handle requests for static files (we expect
618that for production environment, serving static files will be
619off`-loaded to webserver). JavaScript function [*@5 UxPost ]recursively
620scans through html elements provided as second, third etc.. parameters
621and gathers all ids and values of input elements, packs them
622into POST format and sends to Skylark handler specified as first
623parameter. In this case it basically means that is sends [@5 number1]
624and [@5 number2] values to server handler [@5 Add]. Note the use
625of [*@5 `$js`_identity()] call `- this plays the same role in prevention
626of [^http`:`/`/en`.wikipedia`.org`/wiki`/Cross`-site`_request`_forgery^ CSRF]
627attacks as [@5 `$post`_identity] for FORM POSTs (alternative [*@5 UxGet]
628function uses GET requests and does not require [@5 `$js`_identity]).
629Also note the use of single quotes `' for onclick handler `-
630this is because links to handlers are expanded as double`-quoted
631strings.&]
632[s5; The response to [@5 UxPost] or [@5 UxGet] requests is Skylark arbitrary
633format that can be used to directly alter current web page. [*@5 Ux]
634replaces innerHTML of matching (by id) element, [*@5 UxSet] sets
635the value attribute of matching element. Not shown here, [*@5 UxRender]
636is similar to Ux, but renders the text from witz template and
637finally [*@5 UxRun] can be used to run any JavaScript code in the
638client.&]
639[s5; &]
640[s3;:9: 9. Connecting SQL database&]
641[s5; Skylark naturally uses U`+`+ SQL support when dealing with persistent
642storage, using `"session per`-thread`" mode. Because of this,
643database session needs to be connected when starting each thread
644`- this is done by overloading [@5 WorkThread] virtual method of
645SkylarkApp:&]
646[s7; &]
647[s7; #include <Skylark/Skylark.h>&]
648[s7; #include <plugin/sqlite3/Sqlite3.h>&]
649[s7; &]
650[s7; using namespace Upp;&]
651[s7; &]
652[s7; #define  MODEL <Skylark09/myapp.sch>&]
653[s7; #define  SCHEMADIALECT <plugin/sqlite3/Sqlite3Schema.h>&]
654[s7; #include <Sql/sch`_header.h>&]
655[s7; #include <Sql/sch`_schema.h>&]
656[s7; #include <Sql/sch`_source.h>&]
657[s7; &]
658[s7; SKYLARK(HomePage, `"`")&]
659[s7; `{&]
660[s7; -|Sql sql;&]
661[s7; -|sql `* Select(ID, NAME, LASTNAME)&]
662[s7; -|      .From(PERSON)&]
663[s7; -|      .OrderBy(LASTNAME, NAME);&]
664[s7; -|ValueArray person;&]
665[s7; -|ValueMap vm;&]
666[s7; -|while(sql.Fetch(vm))&]
667[s7; -|-|person.Add(vm);&]
668[s7; -|http(`"PERSON`", person)&]
669[s7; -|    .RenderResult(`"Skylark09/index`");&]
670[s7; `}&]
671[s7; &]
672[s7; struct MyApp : SkylarkApp `{&]
673[s7; -|[* virtual void WorkThread();]&]
674[s7; &]
675[s7; -|MyApp() `{&]
676[s7; -|-|root `= `"myapp`";&]
677[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
678[s7; -|#ifdef `_DEBUG&]
679[s7; -|-|prefork `= 0;&]
680[s7; -|-|use`_caching `= false;&]
681[s7; -|#endif&]
682[s7; -|`}&]
683[s7; `};&]
684[s7; &]
685[s7; void InitModel()&]
686[s7; `{&]
687[s7; #ifdef `_DEBUG&]
688[s7; -|SqlSchema sch(SQLITE3);&]
689[s7; -|All`_Tables(sch);&]
690[s7; -|SqlPerformScript(sch.Upgrade());&]
691[s7; -|SqlPerformScript(sch.Attributes());&]
692[s7; -|sch.SaveNormal();&]
693[s7; #endif&]
694[s7; `}&]
695[s7; &]
696[s7; void OpenSQL(Sqlite3Session`& sqlite3)&]
697[s7; `{&]
698[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&]
699[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&]
700[s7; -|-|Exit(1);&]
701[s7; -|`}&]
702[s7; #ifdef `_DEBUG&]
703[s7; -|sqlite3.LogErrors();&]
704[s7; -|sqlite3.SetTrace();&]
705[s7; #endif&]
706[s7; -|SQL `= sqlite3;&]
707[s7; `}&]
708[s7; &]
709[s7; [* void MyApp`::WorkThread()]&]
710[s7; [* `{]&]
711[s7; [* -|Sqlite3Session sqlite3;]&]
712[s7; [* -|OpenSQL(sqlite3);]&]
713[s7; [* -|RunThread();]&]
714[s7; [* `}]&]
715[s7; &]
716[s7; void InitDB()&]
717[s7; `{&]
718[s7; -|Sqlite3Session sqlsession;&]
719[s7; -|OpenSQL(sqlsession);&]
720[s7; -|SqlSchema sch(SQLITE3);&]
721[s7; -|All`_Tables(sch);&]
722[s7; -|SqlPerformScript(sch.Upgrade());&]
723[s7; -|SqlPerformScript(sch.Attributes());&]
724[s7; &]
725[s7; -|SQL `* Insert(PERSON)(NAME,`"Joe`")(LASTNAME,`"Smith`");&]
726[s7; -|SQL `* Insert(PERSON)(NAME,`"Mike`")(LASTNAME,`"Carpenter`");&]
727[s7; -|SQL `* Insert(PERSON)(NAME,`"Jon`")(LASTNAME,`"Goober`");&]
728[s7; `}&]
729[s7; &]
730[s7; CONSOLE`_APP`_MAIN&]
731[s7; `{&]
732[s7; #ifdef `_DEBUG&]
733[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
734[s7; -|Ini`::skylark`_log `= true;&]
735[s7; #endif&]
736[s7; &]
737[s7; -|DeleteFile(ConfigFile(`"db`")); // for this example, always
738create a new DB&]
739[s7; -|InitDB();&]
740[s7; &]
741[s7; -|MyApp().Run();-|&]
742[s7; `}&]
743[s7; &]
744[s5; [* Skylark09/index.witz:]&]
745[s7; &]
746[s7; <!DOCTYPE html>&]
747[s7; <html>&]
748[s7; <body>&]
749[s7; <table border`=`"1`">&]
750[s7; <tr>&]
751[s7;   <th>No.</th>&]
752[s7;   <th>First Name</th>&]
753[s7;   <th>Last Name</th>&]
754[s7; </tr>&]
755[s7; `$for(i in PERSON)&]
756[s7; -|<tr>&]
757[s7; -|  <td>`$i.`_index.</td>&]
758[s7; -|  <td>`$i.NAME</td>&]
759[s7; -|  <td>`$i.LASTNAME</td>&]
760[s7; -|</tr>-|&]
761[s7; `$/&]
762[s7; </table>&]
763[s7; </body>&]
764[s7; </html>&]
765[s7; &]
766[s5; [* Skylark09/myapp.sch:]&]
767[s7; &]
768[s7; TABLE`_(PERSON)&]
769[s7; -|INT`_    (ID)      PRIMARY`_KEY AUTO`_INCREMENT&]
770[s7; -|STRING`_ (NAME, 200)&]
771[s7; -|STRING`_ (LASTNAME, 200)&]
772[s7; END`_TABLE&]
773[s7; &]
774[s5; &]
775[s3;:10: 10. Advanced SQL&]
776[s5; Http class contains some advanced convenience support for dealing
777with SQL databases:&]
778[s5;l160;i150;O0; It is possible to fill result set of SqlSelect
779into witz variable as demonstrated by [@5 HomePage ]handler&]
780[s5;l160;i150;O0; Http`::Insert inserts a row to database, reading
781values of columns based on known (from .sch file) names of columns
782from equally named variables of shared variable space (which
783come from POST or GET request), as demonstrated by [@5 SubmitNew].&]
784[s5;l160;i150;O0; Http`::Update updates a row in database, again based
785on names of columns and shared variable space, as demonstrated
786by [@5 SubmitEdit].&]
787[s5;l160;i150;O0; It is also possible to add shared variables based
788on single row select, names being then same as names of columns,
789as demonstrated by [@5 Edit].&]
790[s5; &]
791[s7; &]
792[s7; #include <Skylark/Skylark.h>&]
793[s7; #include <plugin/sqlite3/Sqlite3.h>&]
794[s7; &]
795[s7; using namespace Upp;&]
796[s7; &]
797[s7; #define  MODEL <Skylark09/myapp.sch>&]
798[s7; #define  SCHEMADIALECT <plugin/sqlite3/Sqlite3Schema.h>&]
799[s7; #include <Sql/sch`_header.h>&]
800[s7; #include <Sql/sch`_schema.h>&]
801[s7; #include <Sql/sch`_source.h>&]
802[s7; &]
803[s7; SKYLARK(HomePage, `"`")&]
804[s7; `{&]
805[s7; /`*&]
806[s7; -|Sql sql;&]
807[s7; -|sql `* Select(ID, NAME, LASTNAME)&]
808[s7; -|      .From(PERSON)&]
809[s7; -|      .OrderBy(LASTNAME, NAME);&]
810[s7; -|ValueArray person;&]
811[s7; -|ValueMap vm;&]
812[s7; -|while(sql.Fetch(vm))&]
813[s7; -|-|person.Add(vm);&]
814[s7; `*/&]
815[s7; -|[* http(`"PERSON`", Select(SqlAll()).From(PERSON).OrderBy(LASTNAME,
816NAME))]&]
817[s7; -|.RenderResult(`"Skylark10/index`");&]
818[s7; `}&]
819[s7; &]
820[s7; SKYLARK(SubmitNew, `"create/submit:POST`")&]
821[s7; `{&]
822[s7; /`*&]
823[s7; -|SQL `* Insert(PERSON)(NAME, http`[`"name`"`])(LASTNAME, http`[`"lastname`"`]);&]
824[s7; `*/&]
825[s7; -|[* SQL `* http.Insert(PERSON);]&]
826[s7; -|http.Redirect(HomePage);&]
827[s7; `}&]
828[s7; &]
829[s7; SKYLARK(New, `"create`")&]
830[s7; `{&]
831[s7; -|http(`"ACTION`", SubmitNew)&]
832[s7; -|.RenderResult(`"Skylark10/Dialog`");&]
833[s7; `}&]
834[s7; &]
835[s7; SKYLARK(SubmitEdit, `"edit/submit/`*:POST`")&]
836[s7; `{&]
837[s7; /`*&]
838[s7; -|SQL `* Update(PERSON)&]
839[s7; -|-|-|(NAME, http`[`"name`"`])&]
840[s7; -|-|-|(LASTNAME, http`[`"lastname`"`])&]
841[s7; -|-|-|.Where(ID `=`= http.Int(0));&]
842[s7; -|;&]
843[s7; `*/&]
844[s7; -|[* SQL `* http.Update(PERSON).Where(ID `=`= http.Int(0));]&]
845[s7; -|http.Redirect(HomePage);&]
846[s7; `}&]
847[s7; &]
848[s7; SKYLARK(Edit, `"edit/`*`")&]
849[s7; `{&]
850[s7; -|int id `= http.Int(0);&]
851[s7; /`*&]
852[s7; -|Sql sql;&]
853[s7; -|sql `* Select(NAME, LASTNAME).From(PERSON).Where(ID `=`= id);&]
854[s7; -|if(!sql.Fetch()) `{&]
855[s7; -|-|http.Redirect(HomePage);&]
856[s7; -|-|return;&]
857[s7; -|`}&]
858[s7; -|http(`"NAME`", sql`[NAME`])(`"LASTNAME`", sql`[LASTNAME`]);&]
859[s7; `*/&]
860[s7; -|[* http]&]
861[s7; [* -|-|(Select(SqlAll()).From(PERSON).Where(ID `=`= id))]&]
862[s7; -|-|(`"ID`", id)&]
863[s7; -|-|(`"ACTION`", SubmitEdit, id)&]
864[s7; -|.RenderResult(`"Skylark10/Dialog`");&]
865[s7; `}&]
866[s7; &]
867[s7; struct MyApp : SkylarkApp `{&]
868[s7; -|virtual void WorkThread();&]
869[s7; &]
870[s7; -|MyApp() `{&]
871[s7; -|-|root `= `"myapp`";&]
872[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
873[s7; -|#ifdef `_DEBUG&]
874[s7; -|-|prefork `= 0;&]
875[s7; -|-|use`_caching `= false;&]
876[s7; -|#endif&]
877[s7; -|`}&]
878[s7; `};&]
879[s7; &]
880[s7; void InitModel()&]
881[s7; `{&]
882[s7; #ifdef `_DEBUG&]
883[s7; -|SqlSchema sch(SQLITE3);&]
884[s7; -|All`_Tables(sch);&]
885[s7; -|SqlPerformScript(sch.Upgrade());&]
886[s7; -|SqlPerformScript(sch.Attributes());&]
887[s7; -|sch.SaveNormal();&]
888[s7; #endif&]
889[s7; `}&]
890[s7; &]
891[s7; void OpenSQL(Sqlite3Session`& sqlite3)&]
892[s7; `{&]
893[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&]
894[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&]
895[s7; -|-|Exit(1);&]
896[s7; -|`}&]
897[s7; #ifdef `_DEBUG&]
898[s7; -|sqlite3.LogErrors();&]
899[s7; -|sqlite3.SetTrace();&]
900[s7; #endif&]
901[s7; -|SQL `= sqlite3;&]
902[s7; `}&]
903[s7; &]
904[s7; void MyApp`::WorkThread()&]
905[s7; `{&]
906[s7; -|Sqlite3Session sqlite3;&]
907[s7; -|OpenSQL(sqlite3);&]
908[s7; -|RunThread();&]
909[s7; `}&]
910[s7; &]
911[s7; void InitDB()&]
912[s7; `{&]
913[s7; -|Sqlite3Session sqlsession;&]
914[s7; -|OpenSQL(sqlsession);&]
915[s7; -|SqlSchema sch(SQLITE3);&]
916[s7; -|All`_Tables(sch);&]
917[s7; -|SqlPerformScript(sch.Upgrade());&]
918[s7; -|SqlPerformScript(sch.Attributes());&]
919[s7; `}&]
920[s7; &]
921[s7; CONSOLE`_APP`_MAIN&]
922[s7; `{&]
923[s7; #ifdef `_DEBUG&]
924[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
925[s7; -|Ini`::skylark`_log `= true;&]
926[s7; #endif&]
927[s7; &]
928[s7; -|InitDB();&]
929[s7; &]
930[s7; -|MyApp().Run();-|&]
931[s7; `}&]
932[s7; &]
933[s5; [* Skylark10/index.witz:]&]
934[s7; &]
935[s7; <!DOCTYPE html>&]
936[s7; <html>&]
937[s7; <body>&]
938[s7; <table border`=`"1`">&]
939[s7; <tr>&]
940[s7;   <th>No.</th>&]
941[s7;   <th>First Name</th>&]
942[s7;   <th>Last Name</th>&]
943[s7; </tr>&]
944[s7; `$for(i in PERSON)&]
945[s7; -|<tr>&]
946[s7; -|  <td>`$(i.`_index `+ 1).</td>&]
947[s7; -|  <td>`$i.NAME</td>&]
948[s7; -|  <td>`$i.LASTNAME</td>&]
949[s7; -|  <td><a href`=`$Edit(i.ID)>Edit</a></td>&]
950[s7; -|</tr>-|&]
951[s7; `$/&]
952[s7; </table>&]
953[s7; <a href`=`$New>Create new</a>&]
954[s7; </body>&]
955[s7; </html>&]
956[s7; &]
957[s5; [* Skylark10/dialog.witz:]&]
958[s7; &]
959[s7; <!DOCTYPE html>&]
960[s7; <html>&]
961[s7; <body>&]
962[s7; <FORM action`=`$ACTION method`=`"post`" accept`-charset`=`"utf`-8`"
963enctype`=`"multipart/form`-data`">&]
964[s7; `$post`_identity()&]
965[s7; <P>&]
966[s7;     First name: <INPUT type`=`"text`" name`=`"name`" value`=`"`$NAME`"><BR>&]
967[s7;     Last name: <INPUT type`=`"text`" name`=`"lastname`" value`=`"`$LASTNAME`"><BR
968>&]
969[s7;     <INPUT type`=`"submit`" value`=`"Send`" name`=`"OK`"/><BR>&]
970[s7; </P>&]
971[s7; </FORM>&]
972[s7; </table>&]
973[s7; </body>&]
974[s7; </html>&]
975[s7; &]
976[s5; [* Skylark10/myapp.sch:]&]
977[s7; &]
978[s7; TABLE`_(PERSON)&]
979[s7; -|INT`_    (ID)      PRIMARY`_KEY AUTO`_INCREMENT&]
980[s7; -|STRING`_ (NAME, 200)&]
981[s7; -|STRING`_ (LASTNAME, 200)&]
982[s7; END`_TABLE&]
983[s7; &]
984[s5; &]
985[s3;:11: 11. Language support&]
986[s5; Skylark language support is based on session variables:&]
987[s5; `"[@5 .`_`_lang`_`_]`" contains integer with U`+`+ language identifier&]
988[s5; `"[@5 .language]`" contains the string form of this identifier
989(e.g. `"cs`-cz`")&]
990[s5; [@5 Http`::SetLanguage ]method sets both variables and also SetLanguage
991(U`+`+ Core function to switch U`+`+ language). When loading
992.witz templates, Skylark first searches for templates for specific
993language `- language can be a part of template name, e.g. [%-*@5 index.][%-*_@5 cs`-c
994z][%-*@5 .witz], only if not found, `'generic`' version (like
995[%-*@5 index.witz]) is used. Also, after loading the session, U`+`+
996Core SetLanguage is invoked with current `".`_`_lang`_`_`" setting,
997that way U`+`+ i18n system can be used in Skylark handlers.&]
998[s7; &]
999[s7; #include <Skylark/Skylark.h>&]
1000[s7; #include <plugin/sqlite3/Sqlite3.h>&]
1001[s7; &]
1002[s7; using namespace Upp;&]
1003[s7; &]
1004[s7; SKYLARK(HomePage, `"`")&]
1005[s7; `{&]
1006[s7; -|http(`"VAR`", [* t`_(`"Aborted by user.`")])&]
1007[s7; -|    .RenderResult(`"Skylark11/index`");&]
1008[s7; `}&]
1009[s7; &]
1010[s7; SKYLARK([* SetLanguage], `"setlanguage/`*`")&]
1011[s7; `{&]
1012[s7; -|int lang `= LNGFromText(http`[0`]);&]
1013[s7; -|if(lang)&]
1014[s7; -|-|[* http.SetLanguage(lang);]&]
1015[s7; -|http.Redirect(HomePage);&]
1016[s7; `}&]
1017[s7; &]
1018[s7; struct MyApp : SkylarkApp `{&]
1019[s7; -|MyApp() `{&]
1020[s7; -|-|root `= `"myapp`";&]
1021[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
1022[s7; -|#ifdef `_DEBUG&]
1023[s7; -|-|prefork `= 0;&]
1024[s7; -|-|use`_caching `= false;&]
1025[s7; -|#endif&]
1026[s7; -|`}&]
1027[s7; `};&]
1028[s7; &]
1029[s7; CONSOLE`_APP`_MAIN&]
1030[s7; `{&]
1031[s7; #ifdef `_DEBUG&]
1032[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
1033[s7; -|Ini`::skylark`_log `= true;&]
1034[s7; #endif&]
1035[s7; -|MyApp().Run();-|&]
1036[s7; `}&]
1037[s7; &]
1038[s5; [* Skylark11/lang.witz:]&]
1039[s7; &]
1040[s7; <a href`=`$[* SetLanguage](`"en`-us`")>EN</a>&]
1041[s7; <a href`=`$[* SetLanguage](`"cs`-cz`")>CZ</a>&]
1042[s7; <br>&]
1043[s7; Current language: [* `$.language]<br>&]
1044[s7; &]
1045[s5; [* Skylark11/index.witz:]&]
1046[s7; &]
1047[s7; <!DOCTYPE html>&]
1048[s7; <html>&]
1049[s7; <body>&]
1050[s7; #include lang&]
1051[s7; English version<br>&]
1052[s7; Variable value: `$VAR&]
1053[s7; </body>&]
1054[s7; </html>&]
1055[s7; &]
1056[s5;%- [* Skylark11/index.][*_ cs`-cz][* .witz:]&]
1057[s7; &]
1058[s7; <!DOCTYPE html>&]
1059[s7; <html>&]
1060[s7; <body>&]
1061[s7; #include lang&]
1062[s7; Česká verze<br>&]
1063[s7; Hodnota proměnné: `$VAR&]
1064[s7; </body>&]
1065[s7; </html>&]
1066[s7; &]
1067[s5; &]
1068[s3;:12: 12. Packs&]
1069[s5; Sometimes it is useful to parametrize a group of handlers so
1070that they can be used in different contexts. For example, the
1071Create/Edit/Delete operations in chapter 10 share many elements
1072(e.g. table name or dialog) and by parametrizing them, it would
1073be possible to use them with different table. Such thing is possible
1074by creating `"pack`" class. Such class has to be derived from
1075[@5 SkylarkPack ]base class, must have [@5 CLASSNAME ]typedef and
1076must have [@5 Use ]method.&]
1077[s5; Pack handlers are normal methods of such class and are registred
1078in [@5 Use ]method by [@5 SKYLARK`_METHOD ]macro. To reference other
1079method of the same class (e.g. for Redirect or as witz parameter),
1080use [@5 THISLINK ]macro.&]
1081[s5; The instance of pack is created with [@5 SKYLARK`_USE]. This effectively
1082creates a global variable of pack class type and register handlers
1083for this instance, combining variable name and method name as
1084name of handler and provided urls to get the handler url. [@5 SKYLARK`_USE
1085]has `"body`" that gets executed during registration phase and
1086can be used to setup parameters of pack instance.&]
1087[s5; Url links to pack methods are then expressed as [/@5 instance:method][@5
1088]in witz templates and as LINK(instance, method) in C`+`+ code.&]
1089[s5; Note also that HandlerId is a special simple type able to store
1090either [@5 LINK ]or [@5 THISLINK ]originated reference to pack method
1091or pointer to handler function.&]
1092[s7; .....&]
1093[s7; &]
1094[s7; struct CreateEditDelete : [* SkylarkPack ]`{&]
1095[s7; -|[* HandlerId   ]back;&]
1096[s7; -|SqlId       table;&]
1097[s7; -|SqlId       key;&]
1098[s7; -|SqlSet      columns;&]
1099[s7; -|String      dialog;&]
1100[s7; &]
1101[s7; -|void Create(Http`& http);&]
1102[s7; -|void SubmitCreate(Http`& http);&]
1103[s7; -|void Edit(Http`& http);&]
1104[s7; -|void SubmitEdit(Http`& http);&]
1105[s7; -|void Delete(Http`& http);&]
1106[s7; &]
1107[s7; -|typedef CreateEditDelete CLASSNAME;&]
1108[s7; &]
1109[s7; -|void Use();&]
1110[s7; -|&]
1111[s7; -|CreateEditDelete() `{ key `= SqlId(`"ID`"); columns `= SqlSet(SqlAll());
1112`}&]
1113[s7; `};&]
1114[s7; &]
1115[s5; [* Skylark12/Cud.cpp:]&]
1116[s7; &]
1117[s7; #include `"Skylark12.h`"&]
1118[s7; &]
1119[s7; void CreateEditDelete`::Create(Http`& http)&]
1120[s7; `{&]
1121[s7; -|http(`"ACTION`", [* THISLINK](SubmitCreate))&]
1122[s7; -|.RenderResult(dialog);&]
1123[s7; `}&]
1124[s7; &]
1125[s7; void CreateEditDelete`::SubmitCreate(Http`& http)&]
1126[s7; `{&]
1127[s7; -|SQL `* http.Insert(table);&]
1128[s7; -|http.Redirect(back);&]
1129[s7; `}&]
1130[s7; &]
1131[s7; void CreateEditDelete`::Edit(Http`& http)&]
1132[s7; `{&]
1133[s7; -|int id `= http.Int(0);&]
1134[s7; -|http&]
1135[s7; -|-|(Select(columns).From(table).Where(key `=`= id))&]
1136[s7; -|-|(`"ID`", id)&]
1137[s7; -|-|(`"ACTION`", [* THISLINK](SubmitEdit), id)&]
1138[s7; -|.RenderResult(dialog);&]
1139[s7; `}&]
1140[s7; &]
1141[s7; void CreateEditDelete`::SubmitEdit(Http`& http)&]
1142[s7; `{&]
1143[s7; -|SQL `* http.Update(table).Where(key `=`= http.Int(0));&]
1144[s7; -|http.Redirect(back);&]
1145[s7; `}&]
1146[s7; &]
1147[s7; void CreateEditDelete`::Delete(Http`& http)&]
1148[s7; `{&]
1149[s7; -|SQL `* SqlDelete(table).Where(key `=`= atoi(http`[0`]));&]
1150[s7; -|http.Redirect(back);&]
1151[s7; `}&]
1152[s7; &]
1153[s7; void CreateEditDelete`::[* Use]()&]
1154[s7; `{&]
1155[s7; -|[* SKYLARK`_METHOD](Create, `"create`");&]
1156[s7; -|[* SKYLARK`_METHOD](SubmitCreate, `"create`_submit:POST`");&]
1157[s7; -|[* SKYLARK`_METHOD](Edit, `"edit/`*`");&]
1158[s7; -|[* SKYLARK`_METHOD](SubmitEdit, `"submit`_edit/`*:POST`");&]
1159[s7; -|[* SKYLARK`_METHOD](Delete, `"delete/`*`");&]
1160[s7; `}&]
1161[s7; &]
1162[s5; [* Skylark12/Handlers.icpp:]&]
1163[s7; &]
1164[s7; #include `"Skylark12.h`"&]
1165[s7; &]
1166[s7; void HomePage(Http`&);&]
1167[s7; &]
1168[s7; [* SKYLARK`_USE](CreateEditDelete, Person, `"person`")&]
1169[s7; `{&]
1170[s7; -|Person.back `= HomePage;&]
1171[s7; -|Person.table `= PERSON;&]
1172[s7; -|Person.dialog `= `"Skylark12/dialog`";&]
1173[s7; `}&]
1174[s7; &]
1175[s7; SKYLARK(HomePage, `"`")&]
1176[s7; `{&]
1177[s7; -|http(`"PERSON`", Select(ID, FIRSTNAME, LASTNAME, EMAIL).From(PERSON)&]
1178[s7; -|               .OrderBy(LASTNAME, FIRSTNAME))&]
1179[s7; -|(`"CREATE`", [* LINK](Person, Create))&]
1180[s7; -|.RenderResult(`"Skylark12/index`");&]
1181[s7; `}&]
1182[s7; &]
1183[s7; SKYLARK(CatchAll, `"`*`*`")&]
1184[s7; `{&]
1185[s7; -|http.Redirect(HomePage);&]
1186[s7; `}&]
1187[s7; &]
1188[s5; [* Skylark12/index.witz:]&]
1189[s7; &]
1190[s7; #include Skylark/Base&]
1191[s7; &]
1192[s7; #define BODY&]
1193[s7; &]
1194[s7; <table border`=`"1`" id`=`"persons`">&]
1195[s7; <tr>&]
1196[s7;   <th>First Name</th>&]
1197[s7;   <th>Last Name</th>&]
1198[s7;   <th>Email</th>&]
1199[s7; </tr>&]
1200[s7; `$for(i in PERSON)&]
1201[s7; -|<tr>&]
1202[s7; -|  <td>`$i.FIRSTNAME</td>&]
1203[s7; -|  <td>`$i.LASTNAME</td>&]
1204[s7; -|  <td>`$i.EMAIL</td>&]
1205[s7; -|  <td>&]
1206[s7; -|  -|<a href`=`$[* Person:Edit](i.ID)>Edit</a>&]
1207[s7; -|  -|<a href`=`$[* Person:Delete](i.ID)>Delete</a>&]
1208[s7; -|  </td>&]
1209[s7; -|</tr>-|&]
1210[s7; `$/&]
1211[s7; </table>&]
1212[s7; &]
1213[s7; <p/>&]
1214[s7; <a href`=`$[* Person:Create]>Insert new person</a>&]
1215[s7; <a href`=`$[* CREATE]>Insert new person using LINK</a>&]
1216[s7; &]
1217[s5; &]
1218[s3; Recommended tutorials:&]
1219[s5; If you want to learn more, we have several tutorials that you
1220can find useful:&]
1221[s5;l160;i150;O0; [^topic`:`/`/Sql`/srcdoc`/tutorial`$en`-us^ SQL tutorial]
1222`- everything you should know to work efficiency with databases
1223within U`+`+ framework.]]