mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 14:16:10 -06:00
1193 lines
No EOL
37 KiB
C++
1193 lines
No EOL
37 KiB
C++
topic "Tutorial";
|
|
[2 $$0,0#00000000000000000000000000000000:Default]
|
|
[l288;i1120;a17;O9;~~~.1408;2 $$1,0#10431211400427159095818037425705:param]
|
|
[a83;*R6 $$2,5#31310162474203024125188417583966:caption]
|
|
[H4;b83;*4 $$3,5#07864147445237544204411237157677:title]
|
|
[i288;O9;C2 $$4,6#40027414424643823182269349404212:item]
|
|
[b42;a42;2 $$5,5#45413000475342174754091244180557:text]
|
|
[l288;b17;a17;2 $$6,6#27521748481378242620020725143825:desc]
|
|
[l321;C@5;1 $$7,7#20902679421464641399138805415013:code]
|
|
[b2503;2 $$8,0#65142375456100023862071332075487:separator]
|
|
[*@(0.0.255)2 $$9,0#83433469410354161042741608181528:base]
|
|
[C2 $$10,0#37138531426314131251341829483380:class]
|
|
[l288;a17;*1 $$11,11#70004532496200323422659154056402:requirement]
|
|
[i417;b42;a42;O9;~~~.416;2 $$12,12#10566046415157235020018451313112:tparam]
|
|
[b167;C2 $$13,13#92430459443460461911108080531343:item1]
|
|
[i288;a42;O9;C2 $$14,14#77422149456609303542238260500223:item2]
|
|
[*@2$(0.128.128)2 $$15,15#34511555403152284025741354420178:NewsDate]
|
|
[l321;*C$7;2 $$16,16#03451589433145915344929335295360:result]
|
|
[l321;b83;a83;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line]
|
|
[l160;*C+117 $$18,5#88603949442205825958800053222425:package`-title]
|
|
[2 $$19,0#53580023442335529039900623488521:gap]
|
|
[C2 $$20,20#70211524482531209251820423858195:class`-nested]
|
|
[b50;2 $$21,21#03324558446220344731010354752573:Par]
|
|
[{_}%EN-US
|
|
[s2; Skylark Tutorial&]
|
|
[s0; &]
|
|
[s3; 1. SKYLARK handlers and path patterns&]
|
|
[s5; Skylark application is divided to `'handlers`' which provide
|
|
response to various HTTP requests based on url pattern:&]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK](HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http << `"<!DOCTYPE html><html><body>Hello world!</body></html>`";&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK](Param, `"`*/param`")&]
|
|
[s7; `{&]
|
|
[s7; -|http << `"<!DOCTYPE html><html><body>Parameter: `" << http`[0`]
|
|
<< `"</html></body>`";&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK](Params, `"params/`*`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|http << `"<!DOCTYPE html><html><body>Parameters: `";&]
|
|
[s7; -|for(int i `= 0; i < http.GetParamCount(); i`+`+)&]
|
|
[s7; -|-|http << http`[i`] << `" `";&]
|
|
[s7; -|http << `"</html></body>`";&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK](CatchAll, `"`*`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.[* Redirect](HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK](Favicon, `"/favicon.ico`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.ContentType(`"image/png`") << LoadFile(GetDataFile(`"favicon.png`"));&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|[* root `= `"myapp`";]&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|[* MyApp().Run();]-|&]
|
|
[s7; `}&]
|
|
[s5; Handlers are introduced by macro [@5 SKYLARK], first parameters
|
|
is the name of handler (macro creates a function that is named
|
|
this way and this id is also used in various places like page
|
|
templates) and path pattern which handler is supposed to respond
|
|
to.&]
|
|
[s5; Handler is represented by C`+`+ function with single parameter
|
|
Http`& [/ http], which is used for request processing. [@5 SKYLARK]
|
|
macro creates a header for this function and also registers it
|
|
with Skylark so that requestes matching the path pattern are
|
|
dispatched to this function. Important note: as [@5 SKYLARK] macro
|
|
uses global constructors to register handlers, handlers should
|
|
be put into .icpp files (unless they are in the same file with
|
|
`'main`').&]
|
|
[s5; Path pattern can contain parameter placeholders `'`*`' (see
|
|
[@5 Param] handler), which are then provided by [/ http] as parameters
|
|
(through operator`[`](int)). Path is always split by `'/`' characters
|
|
and each part must either be text or placeholder `- patterns
|
|
like `"something`*/to`*avoid`" are not allowed&]
|
|
[s5; Special wildcard `'`*`*`' represents any other number of placeholders
|
|
(see [@5 Params]); in special case it is useful to catch all unresolved
|
|
requests to website and [@5 Redirect] them to the application homepage
|
|
(see [@5 CatchAll]).&]
|
|
[s5; The priority of various handlers for particular request is determined
|
|
by number of text parts matched, which means that for request
|
|
`"foo/bar/32`" placeholder `"foo/bar/`*`" has priority over `"foo/`*/`*`".&]
|
|
[s5; Another concern is setting of root path for application, in
|
|
code above done by `"[* root ]`= `"[@3 myapp]`";`". This defines
|
|
the root path of application, in other words [@5 HomePage ]handler
|
|
will react to `"[@3 myapp]`" path, Param handler to e.g. `"[@3 myapp]/test/param`"
|
|
etc. In situation where this is not desirable, it is possible
|
|
to exclude root by adding `'/`' at the start of path, as demonstrated
|
|
by [@5 Favicon] handler.&]
|
|
[s5; [@5 MyApp().Run()] starts a HTTP server (and also SCGI server)
|
|
on default port 8001, so after starting application, you should
|
|
be able to access it from your browser by entering &]
|
|
[s5; 127.0.0.1:8001/myapp&]
|
|
[s5; We then recommend to try e.g.&]
|
|
[s0; 127.0.0.1:8001/myapp/anything/param&]
|
|
[s0; 127.0.0.1:8001/myapp/params/1/2/3&]
|
|
[s0; 127.0.0.1:8001/myapp/anythingelse&]
|
|
[s0; 127.0.0.1:8001&]
|
|
[s5; (last one should result in error `"Page not found`", as no matching
|
|
handler is present)&]
|
|
[s5; &]
|
|
[s3; 2. Witz templates&]
|
|
[s5; To decouple presentation layer from application logic, Skylark
|
|
features template language `'Witz`':&]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|ValueArray va;&]
|
|
[s7; -|va.Add(1);&]
|
|
[s7; -|va.Add(`"Hello`");&]
|
|
[s7; -|ValueMap m;&]
|
|
[s7; -|m.Add(`"key1`", `"first value`");&]
|
|
[s7; -|m.Add(`"key2`", `"second value`");&]
|
|
[s7; &]
|
|
[s7; -|[* http(`"MyValue`", `"some value`")]&]
|
|
[s7; [* -| (`"MyRawValue`", Raw(`"<b>raw <u>html</u></b>`"))]&]
|
|
[s7; [* -| (`"MyRawValue2`", `"<b>another raw <u>html</u></b>`")]&]
|
|
[s7; [* -| (`"MyArray`", va)]&]
|
|
[s7; [* -| (`"MyMap`", m)]&]
|
|
[s7; [* -| .RenderResult(`"Skylark02/index`");]&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|[* use`_caching ]`= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark02/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; MyValue: [* `$MyValue]<br>&]
|
|
[s7; MyRawValue: [* `$MyRawValue]<br>&]
|
|
[s7; MyRawValue2: [* `$raw(MyRawValue2)]<br>&]
|
|
[s7; <br>MyArray:<br>&]
|
|
[s7; [* `$for(i in MyArray)]&]
|
|
[s7; [* -|`$i.`_index. `$i<br>]&]
|
|
[s7; [* `$endfor]&]
|
|
[s7; <br>MyMap:<br>&]
|
|
[s7; [* `$for(i in MyMap)]&]
|
|
[s7; [* -|`$i.`_index. `$i.`_key: `$i<br>]&]
|
|
[s7; [* `$endfor]&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s5; Witz template language loosely ressembles JavaScript. It is
|
|
able to evaluate expression, supports `"if`" and looping through
|
|
arrays or maps with `"for`".&]
|
|
[s5; Witz templates are compiled at runtime. Settings configuration
|
|
variable [* use`_caching] to false makes them to compile each time
|
|
they are invoked (otherwise the compilation result is cached)
|
|
`- this is usefull while debugging, as templates can be adjusted
|
|
while application is running.&]
|
|
[s5; The search path for templates is set by another configuration
|
|
variable [* path]. Default value of [* path] is set to environment
|
|
variable `'UPP`_ASSEMBLY`_`_`', which is set by theide to current
|
|
assembly path `- which means that for debugging you do not need
|
|
to worry about [* path] as long as all templates are in U`+`+ packages
|
|
and you start the application from theide. Note that [* path] is
|
|
also used to search for static files.&]
|
|
[s5; String values are normally HTML escaped; if you need to pass
|
|
raw html code as parameter, you have to either use [@5 Raw ]C`+`+
|
|
function in application logic or [@5 raw] function in Witz.&]
|
|
[s5; &]
|
|
[s3; 3. Witz links to handlers&]
|
|
[s5; Skylark handlers are represented in witz templates as function
|
|
calls or simple variables with the same identifiers as the id
|
|
of handler; both yield `" quoted path that matches the handler
|
|
path pattern. If pattern contains parameter placeholder, it is
|
|
passed as argument of the function:&]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* HomePage], `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark03/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* Page2], `"page2`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark03/page2`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* Param], `"paramtest/`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"PARAM`", http`[0`]).RenderResult(`"Skylark03/param`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark03/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <a href`=[* `$Page2]>Link to page2</a><br>&]
|
|
[s7; `$for(i in `[`"param`_test`", 123, `"param`_test3`"`])&]
|
|
[s7; -|<a href`=[* `$Param(i)]>Param test: `$i</a><br>&]
|
|
[s7; `$endfor&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s5; [* Skylark03/page2.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <a href`=[* `$HomePage()]>Back to index</a><br>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s5; [* Skylark03/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <a href`=[* `$Page2()]>Link to page2</a><br>&]
|
|
[s7; `$for(i in `[`"param`_test`", 123, `"param`_test3`"`])&]
|
|
[s7; -|<a href`=[* `$Param(i)]>Param test: `$i</a><br>&]
|
|
[s7; `$endfor&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 4. Combining Witz templates using #define and #include&]
|
|
[s5; Witz templates can be parametrized using subblock [@5 #define]
|
|
[/@5 id] and [@5 #][/@5 id] insertion and combined from several files
|
|
using [@5 #include]:&]
|
|
[s5; [* Skylark04/base.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <title>#TITLE</title>&]
|
|
[s7; <body>&]
|
|
[s7; #BODY&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; #define TITLE Generic title&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark04/index.witz:]&]
|
|
[s7; #include Skylark04/base&]
|
|
[s7; &]
|
|
[s7; #define TITLE MyApp title&]
|
|
[s7; &]
|
|
[s7; #define BODY&]
|
|
[s7; This is MyApp body html!&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 5. FORM GET&]
|
|
[s5; Whenever Skylark founds `'?`' sign in the request path, it converts
|
|
attached values into http`'s [/ shared variable space], which is
|
|
accessible using [* operator`[`]].&]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark05/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* Submit], `"submit`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"[* RESULT]`", ToUpper((String)[* http`[`"id`"`]]))&]
|
|
[s7; -|-|.RenderResult(`"Skylark05/submit`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark05/index.witz]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <form action`=[* `$Submit] method`=`"[* get]`" accept`-charset`=`"utf`-8`"
|
|
enctype`=`"multipart/form`-data`">&]
|
|
[s7; <P>&]
|
|
[s7; <INPUT type`=`"text`" name`=`"[* id]`">&]
|
|
[s7; <INPUT type`=`"submit`" value`=`"Submit`">&]
|
|
[s7; </P>&]
|
|
[s7; </form>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark05/submit.witz]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; Result: [* `$RESULT]<br>&]
|
|
[s7; <a href`=`$HomePage>Back to homepage</a>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 6. FORM POST&]
|
|
[s5; If HTTP method is POST and content`-type field of header contains
|
|
standard values (either `"application/x`-www`-form`-urlencoded`"
|
|
or starts with `"multipart/`", Skylark parses the posted content
|
|
and assigns posted values into shared variable space.&]
|
|
[s5; This makes handling FORM POSTs similar to GET with one exception:
|
|
To avoid [^http`:`/`/en`.wikipedia`.org`/wiki`/Cross`-site`_request`_forgery^ CSRF]
|
|
attacks, it is mandatory to include hidden field into dialog
|
|
definition provided by function [@5 `$post`_identity()] `- Skylark
|
|
manages everything required using session subsystem.&]
|
|
[s5; Request method is also part of handler path pattern (GET is
|
|
default if not specified).&]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark06/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(Submit, `"submit:[* POST]`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"RESULT`", ToUpper((String)http`[`"id`"`]))&]
|
|
[s7; -|-|.RenderResult(`"Skylark06/submit`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark06/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <form action`=`$Submit method`=`"[* post]`" accept`-charset`=`"utf`-8`"
|
|
enctype`=`"multipart/form`-data`">&]
|
|
[s7; <P>&]
|
|
[s7; [* `$post`_identity()]&]
|
|
[s7; <INPUT type`=`"text`" name`=`"id`">&]
|
|
[s7; <INPUT type`=`"submit`" value`=`"Submit`">&]
|
|
[s7; </P>&]
|
|
[s7; </form>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark06/submit.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; Result: `$RESULT<br>&]
|
|
[s7; <a href`=`$HomePage>Back to homepage</a>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; Note: There also exists alternative POST`_RAW method marker in
|
|
SKYLARK handler definition `- such handler also reacts to POST
|
|
requests, but it avoids identity checks to prevent CSRF attacks.&]
|
|
[s5; &]
|
|
[s3; 7. Session variables&]
|
|
[s5; Http`::SessioSet method can be used to store `'session variables`'
|
|
that are persistent for specific browser across requests. Implementation
|
|
is based on cookie, session variables are stored either in filesystem
|
|
or in database (see Skylark configuration for details). Session
|
|
variables are reflected in shared variable space (means its values
|
|
are accessible using [* Http`::operator`[`]]) and are distinguished
|
|
with `'.`' as the first character.&]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark07/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(Submit, `"submit:POST`")&]
|
|
[s7; `{&]
|
|
[s7; -|Value h `= [* http`[`".LIST`"`];]&]
|
|
[s7; -|ValueArray va;&]
|
|
[s7; -|if(IsValueArray(h))&]
|
|
[s7; -|-|va `= h;&]
|
|
[s7; -|va.Add(http`[`"id`"`]);&]
|
|
[s7; -|[* http.SessionSet(`".LIST`", va);]&]
|
|
[s7; -|http.Redirect(HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark07/index.witz:]&]
|
|
[s7; &]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <form action`=`$Submit method`=`"post`" accept`-charset`=`"utf`-8`"
|
|
enctype`=`"multipart/form`-data`">&]
|
|
[s7; <P>&]
|
|
[s7; `$post`_identity()&]
|
|
[s7; <INPUT type`=`"text`" name`=`"id`">&]
|
|
[s7; <INPUT type`=`"submit`" value`=`"Add to list`">&]
|
|
[s7; </P>&]
|
|
[s7; </form>&]
|
|
[s7; List (`$count([* .LIST])):<br>&]
|
|
[s7; `$for(i in [* .LIST])&]
|
|
[s7; -|`"`$i`" &]
|
|
[s7; `$endfor&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 8. Ajax support&]
|
|
[s5; Skylark provides optional direct support for handling Ajax requests.
|
|
On client side, this support is implemented using tiny JavaScript
|
|
library `"skylark.js`".&]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.RenderResult(`"Skylark08/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* Add], `"add:POST`")&]
|
|
[s7; `{&]
|
|
[s7; -|String r `= AsString(Nvl(http.Int(`"number1`")) `+ Nvl(http.Int(`"number2`")));
|
|
&]
|
|
[s7; [* -|http.Ux(`"result`", `"The result is: `" `+ r)]&]
|
|
[s7; [* -| .UxSet(`"number1`", r);]&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark08/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <script type`=`"text/javascript`" src`=`"[* `$static/Skylark/skylark.js]`"></scrip
|
|
t>&]
|
|
[s7; <body>&]
|
|
[s7; [* `$js`_identity()]&]
|
|
[s7; <div id`=`"[* numbers]`">&]
|
|
[s7; <INPUT type`=`"text`" id`=`"[* number1]`">&]
|
|
[s7; <INPUT type`=`"text`" id`=`"[* number2]`">&]
|
|
[s7; <INPUT type`=`"button`" value`=`"Add`" onclick`=`'[* UxPost(`$Add,
|
|
`"numbers`")]`'><br>&]
|
|
[s7; </div>&]
|
|
[s7; <div id`=`"result`"/>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [*@5 `$static] variable is set by Skylark to the [@5 static`_dir
|
|
]configuration parameter, if missing it is set to [/ root]/static,
|
|
which makes Skylark to handle requests for static files (we expect
|
|
that for production environment, serving static files will be
|
|
off`-loaded to webserver). JavaScript function [*@5 UxPost ]recursively
|
|
scans through html elements provided as second, third etc.. parameters
|
|
and gathers all ids and values of input elements, packs them
|
|
into POST format and sends to Skylark handler specified as first
|
|
parameter. In this case it basically means that is sends [@5 number1]
|
|
and [@5 number2] values to server handler [@5 Add]. Note the use
|
|
of [*@5 `$js`_identity()] call `- this plays the same role in prevention
|
|
of [^http`:`/`/en`.wikipedia`.org`/wiki`/Cross`-site`_request`_forgery^ CSRF]
|
|
attacks as [@5 `$post`_identity] for FORM POSTs (alternative [*@5 UxGet]
|
|
function uses GET requests and does not require [@5 `$js`_identity]).
|
|
Also note the use of single quotes `' for onclick handler `-
|
|
this is because links to handlers are expanded as double`-quoted
|
|
strings.&]
|
|
[s5; The response to [@5 UxPost] or [@5 UxGet] requests is Skylark arbitrary
|
|
format that can be used to directly alter current web page. [*@5 Ux]
|
|
replaces innerHTML of matching (by id) element, [*@5 UxSet] sets
|
|
the value attribute of matching element. Not shown here, [*@5 UxRender]
|
|
is similar to Ux, but renders the text from witz template and
|
|
finally [*@5 UxRun] can be used to run any JavaScript code in the
|
|
client.&]
|
|
[s5; &]
|
|
[s3; 9. Connecting SQL database&]
|
|
[s5; Skylark naturally uses U`+`+ SQL support when dealing with persistent
|
|
storage, using `"session per`-thread`" mode. Because of this,
|
|
database session needs to be connected when starting each thread
|
|
`- this is done by overloading [@5 WorkThread] virtual method of
|
|
SkylarkApp:&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; #include <plugin/sqlite3/Sqlite3.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; #define MODEL <Skylark09/myapp.sch>&]
|
|
[s7; #define SCHEMADIALECT <plugin/sqlite3/Sqlite3Schema.h>&]
|
|
[s7; #include <Sql/sch`_header.h>&]
|
|
[s7; #include <Sql/sch`_schema.h>&]
|
|
[s7; #include <Sql/sch`_source.h>&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|Sql sql;&]
|
|
[s7; -|sql `* Select(ID, NAME, LASTNAME)&]
|
|
[s7; -| .From(PERSON)&]
|
|
[s7; -| .OrderBy(LASTNAME, NAME);&]
|
|
[s7; -|ValueArray person;&]
|
|
[s7; -|ValueMap vm;&]
|
|
[s7; -|while(sql.Fetch(vm))&]
|
|
[s7; -|-|person.Add(vm);&]
|
|
[s7; -|http(`"PERSON`", person)&]
|
|
[s7; -| .RenderResult(`"Skylark09/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|[* virtual void WorkThread();]&]
|
|
[s7; &]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; void InitModel()&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|SqlSchema sch(SQLITE3);&]
|
|
[s7; -|All`_Tables(sch);&]
|
|
[s7; -|SqlPerformScript(sch.Upgrade());&]
|
|
[s7; -|SqlPerformScript(sch.Attributes());&]
|
|
[s7; -|sch.SaveNormal();&]
|
|
[s7; #endif&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void OpenSQL(Sqlite3Session`& sqlite3)&]
|
|
[s7; `{&]
|
|
[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&]
|
|
[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&]
|
|
[s7; -|-|Exit(1);&]
|
|
[s7; -|`}&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|sqlite3.LogErrors();&]
|
|
[s7; -|sqlite3.SetTrace();&]
|
|
[s7; #endif&]
|
|
[s7; -|SQL `= sqlite3;&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; [* void MyApp`::WorkThread()]&]
|
|
[s7; [* `{]&]
|
|
[s7; [* -|Sqlite3Session sqlite3;]&]
|
|
[s7; [* -|OpenSQL(sqlite3);]&]
|
|
[s7; [* -|RunThread();]&]
|
|
[s7; [* `}]&]
|
|
[s7; &]
|
|
[s7; void InitDB()&]
|
|
[s7; `{&]
|
|
[s7; -|Sqlite3Session sqlsession;&]
|
|
[s7; -|OpenSQL(sqlsession);&]
|
|
[s7; -|SqlSchema sch(SQLITE3);&]
|
|
[s7; -|All`_Tables(sch);&]
|
|
[s7; -|SqlPerformScript(sch.Upgrade());&]
|
|
[s7; -|SqlPerformScript(sch.Attributes());&]
|
|
[s7; &]
|
|
[s7; -|SQL `* Insert(PERSON)(NAME,`"Joe`")(LASTNAME,`"Smith`");&]
|
|
[s7; -|SQL `* Insert(PERSON)(NAME,`"Mike`")(LASTNAME,`"Carpenter`");&]
|
|
[s7; -|SQL `* Insert(PERSON)(NAME,`"Jon`")(LASTNAME,`"Goober`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|DeleteFile(ConfigFile(`"db`")); // for this example, always
|
|
create a new DB&]
|
|
[s7; -|InitDB();&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark09/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <table border`=`"1`">&]
|
|
[s7; <tr>&]
|
|
[s7; <th>No.</th>&]
|
|
[s7; <th>First Name</th>&]
|
|
[s7; <th>Last Name</th>&]
|
|
[s7; </tr>&]
|
|
[s7; `$for(i in PERSON)&]
|
|
[s7; -|<tr>&]
|
|
[s7; -| <td>`$i.`_index.</td>&]
|
|
[s7; -| <td>`$i.NAME</td>&]
|
|
[s7; -| <td>`$i.LASTNAME</td>&]
|
|
[s7; -|</tr>-|&]
|
|
[s7; `$/&]
|
|
[s7; </table>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark09/myapp.sch:]&]
|
|
[s7; TABLE`_(PERSON)&]
|
|
[s7; -|INT`_ (ID) PRIMARY`_KEY AUTO`_INCREMENT&]
|
|
[s7; -|STRING`_ (NAME, 200)&]
|
|
[s7; -|STRING`_ (LASTNAME, 200)&]
|
|
[s7; END`_TABLE&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 10. Advanced SQL&]
|
|
[s5; Http class contains some advanced convenience support for dealing
|
|
with SQL databases:&]
|
|
[s5;i150;O0; It is possible to fill result set of SqlSelect into
|
|
witz variable as demonstrated by [@5 HomePage ]handler&]
|
|
[s5;i150;O0; Http`::Insert inserts a row to database, reading values
|
|
of columns based on known (from .sch file) names of columns from
|
|
equally named variables of shared variable space (which come
|
|
from POST or GET request), as demonstrated by [@5 SubmitNew].&]
|
|
[s5;i150;O0; Http`::Update updates a row in database, again based
|
|
on names of columns and shared variable space, as demonstrated
|
|
by [@5 SubmitEdit].&]
|
|
[s5;i150;O0; It is also possible to add shared variables based on
|
|
single row select, names being then same as names of columns,
|
|
as demonstrated by [@5 Edit].&]
|
|
[s5; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; #include <plugin/sqlite3/Sqlite3.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; #define MODEL <Skylark09/myapp.sch>&]
|
|
[s7; #define SCHEMADIALECT <plugin/sqlite3/Sqlite3Schema.h>&]
|
|
[s7; #include <Sql/sch`_header.h>&]
|
|
[s7; #include <Sql/sch`_schema.h>&]
|
|
[s7; #include <Sql/sch`_source.h>&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; /`*&]
|
|
[s7; -|Sql sql;&]
|
|
[s7; -|sql `* Select(ID, NAME, LASTNAME)&]
|
|
[s7; -| .From(PERSON)&]
|
|
[s7; -| .OrderBy(LASTNAME, NAME);&]
|
|
[s7; -|ValueArray person;&]
|
|
[s7; -|ValueMap vm;&]
|
|
[s7; -|while(sql.Fetch(vm))&]
|
|
[s7; -|-|person.Add(vm);&]
|
|
[s7; `*/&]
|
|
[s7; -|[* http(`"PERSON`", Select(SqlAll()).From(PERSON).OrderBy(LASTNAME,
|
|
NAME))]&]
|
|
[s7; -|.RenderResult(`"Skylark10/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(SubmitNew, `"create/submit:POST`")&]
|
|
[s7; `{&]
|
|
[s7; /`*&]
|
|
[s7; -|SQL `* Insert(PERSON)(NAME, http`[`"name`"`])(LASTNAME, http`[`"lastname`"`]);&]
|
|
[s7; `*/&]
|
|
[s7; -|[* SQL `* http.Insert(PERSON);]&]
|
|
[s7; -|http.Redirect(HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(New, `"create`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"ACTION`", SubmitNew)&]
|
|
[s7; -|.RenderResult(`"Skylark10/Dialog`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(SubmitEdit, `"edit/submit/`*:POST`")&]
|
|
[s7; `{&]
|
|
[s7; /`*&]
|
|
[s7; -|SQL `* Update(PERSON)&]
|
|
[s7; -|-|-|(NAME, http`[`"name`"`])&]
|
|
[s7; -|-|-|(LASTNAME, http`[`"lastname`"`])&]
|
|
[s7; -|-|-|.Where(ID `=`= http.Int(0));&]
|
|
[s7; -|;&]
|
|
[s7; `*/&]
|
|
[s7; -|[* SQL `* http.Update(PERSON).Where(ID `=`= http.Int(0));]&]
|
|
[s7; -|http.Redirect(HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(Edit, `"edit/`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|int id `= http.Int(0);&]
|
|
[s7; /`*&]
|
|
[s7; -|Sql sql;&]
|
|
[s7; -|sql `* Select(NAME, LASTNAME).From(PERSON).Where(ID `=`= id);&]
|
|
[s7; -|if(!sql.Fetch()) `{&]
|
|
[s7; -|-|http.Redirect(HomePage);&]
|
|
[s7; -|-|return;&]
|
|
[s7; -|`}&]
|
|
[s7; -|http(`"NAME`", sql`[NAME`])(`"LASTNAME`", sql`[LASTNAME`]);&]
|
|
[s7; `*/&]
|
|
[s7; -|[* http]&]
|
|
[s7; [* -|-|(Select(SqlAll()).From(PERSON).Where(ID `=`= id))]&]
|
|
[s7; -|-|(`"ID`", id)&]
|
|
[s7; -|-|(`"ACTION`", SubmitEdit, id)&]
|
|
[s7; -|.RenderResult(`"Skylark10/Dialog`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|virtual void WorkThread();&]
|
|
[s7; &]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; void InitModel()&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|SqlSchema sch(SQLITE3);&]
|
|
[s7; -|All`_Tables(sch);&]
|
|
[s7; -|SqlPerformScript(sch.Upgrade());&]
|
|
[s7; -|SqlPerformScript(sch.Attributes());&]
|
|
[s7; -|sch.SaveNormal();&]
|
|
[s7; #endif&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void OpenSQL(Sqlite3Session`& sqlite3)&]
|
|
[s7; `{&]
|
|
[s7; -|if(!sqlite3.Open(ConfigFile(`"db`"))) `{&]
|
|
[s7; -|-|LOG(`"Can`'t create or open database file`\n`");&]
|
|
[s7; -|-|Exit(1);&]
|
|
[s7; -|`}&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|sqlite3.LogErrors();&]
|
|
[s7; -|sqlite3.SetTrace();&]
|
|
[s7; #endif&]
|
|
[s7; -|SQL `= sqlite3;&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void MyApp`::WorkThread()&]
|
|
[s7; `{&]
|
|
[s7; -|Sqlite3Session sqlite3;&]
|
|
[s7; -|OpenSQL(sqlite3);&]
|
|
[s7; -|RunThread();&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void InitDB()&]
|
|
[s7; `{&]
|
|
[s7; -|Sqlite3Session sqlsession;&]
|
|
[s7; -|OpenSQL(sqlsession);&]
|
|
[s7; -|SqlSchema sch(SQLITE3);&]
|
|
[s7; -|All`_Tables(sch);&]
|
|
[s7; -|SqlPerformScript(sch.Upgrade());&]
|
|
[s7; -|SqlPerformScript(sch.Attributes());&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; &]
|
|
[s7; -|InitDB();&]
|
|
[s7; &]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark10/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <table border`=`"1`">&]
|
|
[s7; <tr>&]
|
|
[s7; <th>No.</th>&]
|
|
[s7; <th>First Name</th>&]
|
|
[s7; <th>Last Name</th>&]
|
|
[s7; </tr>&]
|
|
[s7; `$for(i in PERSON)&]
|
|
[s7; -|<tr>&]
|
|
[s7; -| <td>`$(i.`_index `+ 1).</td>&]
|
|
[s7; -| <td>`$i.NAME</td>&]
|
|
[s7; -| <td>`$i.LASTNAME</td>&]
|
|
[s7; -| <td><a href`=`$Edit(i.ID)>Edit</a></td>&]
|
|
[s7; -|</tr>-|&]
|
|
[s7; `$/&]
|
|
[s7; </table>&]
|
|
[s7; <a href`=`$New>Create new</a>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark10/dialog.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; <FORM action`=`$ACTION method`=`"post`" accept`-charset`=`"utf`-8`"
|
|
enctype`=`"multipart/form`-data`">&]
|
|
[s7; `$post`_identity()&]
|
|
[s7; <P>&]
|
|
[s7; First name: <INPUT type`=`"text`" name`=`"name`" value`=`"`$NAME`"><BR>&]
|
|
[s7; Last name: <INPUT type`=`"text`" name`=`"lastname`" value`=`"`$LASTNAME`"><BR
|
|
>&]
|
|
[s7; <INPUT type`=`"submit`" value`=`"Send`" name`=`"OK`"/><BR>&]
|
|
[s7; </P>&]
|
|
[s7; </FORM>&]
|
|
[s7; </table>&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark10/myapp.sch:]&]
|
|
[s7; TABLE`_(PERSON)&]
|
|
[s7; -|INT`_ (ID) PRIMARY`_KEY AUTO`_INCREMENT&]
|
|
[s7; -|STRING`_ (NAME, 200)&]
|
|
[s7; -|STRING`_ (LASTNAME, 200)&]
|
|
[s7; END`_TABLE&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 11. Language support&]
|
|
[s5; Skylark language support is based on session variables:&]
|
|
[s5; `"[@5 .`_`_lang`_`_]`" contains integer with U`+`+ language identifier&]
|
|
[s5; `"[@5 .language]`" contains the string form of this identifier
|
|
(e.g. `"cs`-cz`")&]
|
|
[s5; [@5 Http`::SetLanguage ]method sets both variables and also SetLanguage
|
|
(U`+`+ Core function to switch U`+`+ language). When loading
|
|
.witz templates, Skylark first searches for templates for specific
|
|
language `- language can be a part of template name, e.g. [%-*@5 index.][%-*_@5 cs`-c
|
|
z][%-*@5 .witz], only if not found, `'generic`' version (like
|
|
[%-*@5 index.witz]) is used. Also, after loading the session, U`+`+
|
|
Core SetLanguage is invoked with current `".`_`_lang`_`_`" setting,
|
|
that way U`+`+ i18n system can be used in Skylark handlers.&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s7; #include <Skylark/Skylark.h>&]
|
|
[s7; #include <plugin/sqlite3/Sqlite3.h>&]
|
|
[s7; &]
|
|
[s7; using namespace Upp;&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"VAR`", [* t`_(`"Aborted by user.`")])&]
|
|
[s7; -| .RenderResult(`"Skylark11/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK([* SetLanguage], `"setlanguage/`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|int lang `= LNGFromText(http`[0`]);&]
|
|
[s7; -|if(lang)&]
|
|
[s7; -|-|[* http.SetLanguage(lang);]&]
|
|
[s7; -|http.Redirect(HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; struct MyApp : SkylarkApp `{&]
|
|
[s7; -|MyApp() `{&]
|
|
[s7; -|-|root `= `"myapp`";&]
|
|
[s7; -|-|threads `= 1; // Sqlite3 does not like threads...&]
|
|
[s7; -|#ifdef `_DEBUG&]
|
|
[s7; -|-|prefork `= 0;&]
|
|
[s7; -|-|use`_caching `= false;&]
|
|
[s7; -|#endif&]
|
|
[s7; -|`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; CONSOLE`_APP`_MAIN&]
|
|
[s7; `{&]
|
|
[s7; #ifdef `_DEBUG&]
|
|
[s7; -|StdLogSetup(LOG`_FILE`|LOG`_COUT);&]
|
|
[s7; -|Ini`::skylark`_log `= true;&]
|
|
[s7; #endif&]
|
|
[s7; -|MyApp().Run();-|&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark11/lang.witz:]&]
|
|
[s7; <a href`=`$[* SetLanguage](`"en`-us`")>EN</a>&]
|
|
[s7; <a href`=`$[* SetLanguage](`"cs`-cz`")>CZ</a>&]
|
|
[s7; <br>&]
|
|
[s7; Current language: [* `$.language]<br>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark11/index.witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; #include lang&]
|
|
[s7; English version<br>&]
|
|
[s7; Variable value: `$VAR&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5;%- [* Skylark11/index.][*_ cs`-cz][* .witz:]&]
|
|
[s7; <!DOCTYPE html>&]
|
|
[s7; <html>&]
|
|
[s7; <body>&]
|
|
[s7; #include lang&]
|
|
[s7; Česká verze<br>&]
|
|
[s7; Hodnota proměnné: `$VAR&]
|
|
[s7; </body>&]
|
|
[s7; </html>&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s3; 12. Packs&]
|
|
[s5; Sometimes it is useful to parametrize a group of handlers so
|
|
that they can be used in different contexts. For example, the
|
|
Create/Edit/Delete operations in chapter 10 share many elements
|
|
(e.g. table name or dialog) and by parametrizing them, it would
|
|
be possible to use them with different table. Such thing is possible
|
|
by creating `"pack`" class. Such class has to be derived from
|
|
[@5 SkylarkPack ]base class, must have [@5 CLASSNAME ]typedef and
|
|
must have [@5 Use ]method.&]
|
|
[s5; Pack handlers are normal methods of such class and are registred
|
|
in [@5 Use ]method by [@5 SKYLARK`_METHOD ]macro. To reference other
|
|
method of the same class (e.g. for Redirect or as witz parameter),
|
|
use [@5 THISLINK ]macro.&]
|
|
[s5; The instance of pack is created with [@5 SKYLARK`_USE]. This effectively
|
|
creates a global variable of pack class type and register handlers
|
|
for this instance, combining variable name and method name as
|
|
name of handler and provided urls to get the handler url. [@5 SKYLARK`_USE
|
|
]has `"body`" that gets executed during registration phase and
|
|
can be used to setup parameters of pack instance.&]
|
|
[s5; Url links to pack methods are then expressed as [/@5 instance:method][@5
|
|
]in witz templates and as LINK(instance, method) in C`+`+ code.&]
|
|
[s5; Note also that HandlerId is a special simple type able to store
|
|
either [@5 LINK ]or [@5 THISLINK ]originated reference to pack method
|
|
or pointer to handler function.&]
|
|
[s7; .....&]
|
|
[s7; &]
|
|
[s7; struct CreateEditDelete : [* SkylarkPack ]`{&]
|
|
[s7; -|[* HandlerId ]back;&]
|
|
[s7; -|SqlId table;&]
|
|
[s7; -|SqlId key;&]
|
|
[s7; -|SqlSet columns;&]
|
|
[s7; -|String dialog;&]
|
|
[s7; &]
|
|
[s7; -|void Create(Http`& http);&]
|
|
[s7; -|void SubmitCreate(Http`& http);&]
|
|
[s7; -|void Edit(Http`& http);&]
|
|
[s7; -|void SubmitEdit(Http`& http);&]
|
|
[s7; -|void Delete(Http`& http);&]
|
|
[s7; &]
|
|
[s7; -|typedef CreateEditDelete CLASSNAME;&]
|
|
[s7; &]
|
|
[s7; -|void Use();&]
|
|
[s7; -|&]
|
|
[s7; -|CreateEditDelete() `{ key `= SqlId(`"ID`"); columns `= SqlSet(SqlAll());
|
|
`}&]
|
|
[s7; `};&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark12/Cud.cpp:]&]
|
|
[s7; &]
|
|
[s7; #include `"Skylark12.h`"&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::Create(Http`& http)&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"ACTION`", [* THISLINK](SubmitCreate))&]
|
|
[s7; -|.RenderResult(dialog);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::SubmitCreate(Http`& http)&]
|
|
[s7; `{&]
|
|
[s7; -|SQL `* http.Insert(table);&]
|
|
[s7; -|http.Redirect(back);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::Edit(Http`& http)&]
|
|
[s7; `{&]
|
|
[s7; -|int id `= http.Int(0);&]
|
|
[s7; -|http&]
|
|
[s7; -|-|(Select(columns).From(table).Where(key `=`= id))&]
|
|
[s7; -|-|(`"ID`", id)&]
|
|
[s7; -|-|(`"ACTION`", [* THISLINK](SubmitEdit), id)&]
|
|
[s7; -|.RenderResult(dialog);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::SubmitEdit(Http`& http)&]
|
|
[s7; `{&]
|
|
[s7; -|SQL `* http.Update(table).Where(key `=`= http.Int(0));&]
|
|
[s7; -|http.Redirect(back);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::Delete(Http`& http)&]
|
|
[s7; `{&]
|
|
[s7; -|SQL `* SqlDelete(table).Where(key `=`= atoi(http`[0`]));&]
|
|
[s7; -|http.Redirect(back);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; void CreateEditDelete`::[* Use]()&]
|
|
[s7; `{&]
|
|
[s7; -|[* SKYLARK`_METHOD](Create, `"create`");&]
|
|
[s7; -|[* SKYLARK`_METHOD](SubmitCreate, `"create`_submit:POST`");&]
|
|
[s7; -|[* SKYLARK`_METHOD](Edit, `"edit/`*`");&]
|
|
[s7; -|[* SKYLARK`_METHOD](SubmitEdit, `"submit`_edit/`*:POST`");&]
|
|
[s7; -|[* SKYLARK`_METHOD](Delete, `"delete/`*`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark12/Handlers.icpp:]&]
|
|
[s7; #include `"Skylark12.h`"&]
|
|
[s7; &]
|
|
[s7; void HomePage(Http`&);&]
|
|
[s7; &]
|
|
[s7; [* SKYLARK`_USE](CreateEditDelete, Person, `"person`")&]
|
|
[s7; `{&]
|
|
[s7; -|Person.back `= HomePage;&]
|
|
[s7; -|Person.table `= PERSON;&]
|
|
[s7; -|Person.dialog `= `"Skylark12/dialog`";&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(HomePage, `"`")&]
|
|
[s7; `{&]
|
|
[s7; -|http(`"PERSON`", Select(ID, FIRSTNAME, LASTNAME, EMAIL).From(PERSON)&]
|
|
[s7; -| .OrderBy(LASTNAME, FIRSTNAME))&]
|
|
[s7; -|(`"CREATE`", [* LINK](Person, Create))&]
|
|
[s7; -|.RenderResult(`"Skylark12/index`");&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; SKYLARK(CatchAll, `"`*`*`")&]
|
|
[s7; `{&]
|
|
[s7; -|http.Redirect(HomePage);&]
|
|
[s7; `}&]
|
|
[s7; &]
|
|
[s7; &]
|
|
[s5; [* Skylark12/index.witz:]&]
|
|
[s7; &]
|
|
[s7; #include Skylark/Base&]
|
|
[s7; &]
|
|
[s7; #define BODY&]
|
|
[s7; &]
|
|
[s7; <table border`=`"1`" id`=`"persons`">&]
|
|
[s7; <tr>&]
|
|
[s7; <th>First Name</th>&]
|
|
[s7; <th>Last Name</th>&]
|
|
[s7; <th>Email</th>&]
|
|
[s7; </tr>&]
|
|
[s7; `$for(i in PERSON)&]
|
|
[s7; -|<tr>&]
|
|
[s7; -| <td>`$i.FIRSTNAME</td>&]
|
|
[s7; -| <td>`$i.LASTNAME</td>&]
|
|
[s7; -| <td>`$i.EMAIL</td>&]
|
|
[s7; -| <td>&]
|
|
[s7; -| -|<a href`=`$[* Person:Edit](i.ID)>Edit</a>&]
|
|
[s7; -| -|<a href`=`$[* Person:Delete](i.ID)>Delete</a>&]
|
|
[s7; -| </td>&]
|
|
[s7; -|</tr>-|&]
|
|
[s7; `$/&]
|
|
[s7; </table>&]
|
|
[s7; &]
|
|
[s7; <p/>&]
|
|
[s7; <a href`=`$[* Person:Create]>Insert new person</a>&]
|
|
[s7; <a href`=`$[* CREATE]>Insert new person using LINK</a>&]
|
|
[s7; &]
|
|
[s5;* ]] |