From: Hans U. N. <hu...@n-...> - 2009-03-05 13:48:59
|
This should catch all occurences of the word "shoping" outside of ChangeLogs or NEWS files and replace it with the correctly spelled "shopping". --- Makefile | 2 +- www/TAB.inc | 2 +- www/shopingcart/Makefile | 15 -- www/shopingcart/buy.yaws | 11 - www/shopingcart/index.yaws | 11 - www/shopingcart/junk.jpg | Bin 5989 -> 0 bytes www/shopingcart/loginpost.yaws | 7 - www/shopingcart/logout.yaws | 12 - www/shopingcart/shopcart.erl | 434 ----------------------------------- www/shopingcart/shopcart_form.yaws | 11 - www/shopingcart/source.html | 98 -------- www/shopingcart/style.css | 57 ----- www/shoppingcart/Makefile | 15 ++ www/shoppingcart/buy.yaws | 11 + www/shoppingcart/index.yaws | 11 + www/shoppingcart/junk.jpg | Bin 0 -> 5989 bytes www/shoppingcart/loginpost.yaws | 7 + www/shoppingcart/logout.yaws | 12 + www/shoppingcart/shopcart.erl | 434 +++++++++++++++++++++++++++++++++++ www/shoppingcart/shopcart_form.yaws | 11 + www/shoppingcart/source.html | 98 ++++++++ www/shoppingcart/style.css | 57 +++++ 22 files changed, 658 insertions(+), 658 deletions(-) delete mode 100644 www/shopingcart/Makefile delete mode 100644 www/shopingcart/buy.yaws delete mode 100644 www/shopingcart/index.yaws delete mode 100644 www/shopingcart/junk.jpg delete mode 100644 www/shopingcart/loginpost.yaws delete mode 100644 www/shopingcart/logout.yaws delete mode 100644 www/shopingcart/shopcart.erl delete mode 100644 www/shopingcart/shopcart_form.yaws delete mode 100644 www/shopingcart/source.html delete mode 100644 www/shopingcart/style.css create mode 100644 www/shoppingcart/Makefile create mode 100644 www/shoppingcart/buy.yaws create mode 100644 www/shoppingcart/index.yaws create mode 100644 www/shoppingcart/junk.jpg create mode 100644 www/shoppingcart/loginpost.yaws create mode 100644 www/shoppingcart/logout.yaws create mode 100644 www/shoppingcart/shopcart.erl create mode 100644 www/shoppingcart/shopcart_form.yaws create mode 100644 www/shoppingcart/source.html create mode 100644 www/shoppingcart/style.css diff --git a/Makefile b/Makefile index c836c5e..83f47cd 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -SUBDIRS = c_src src man www/shopingcart www/code doc scripts +SUBDIRS = c_src src man www/shoppingcart www/code doc scripts include ./include.mk include ./vsn.mk diff --git a/www/TAB.inc b/www/TAB.inc index 96eb090..55466c6 100644 --- a/www/TAB.inc +++ b/www/TAB.inc @@ -48,7 +48,7 @@ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" <div class="%%session%%"> <a href="/session.yaws">Cookie sessions</a> </div> <div class="%%appmods%%"> <a href="/appmods.yaws">Appmods</a> </div> <div class="%%stream%%"> <a href="/stream.yaws">Streaming data</a> </div> -<a href="/shopingcart/index.yaws">Tiny shopping cart</a> +<a href="/shoppingcart/index.yaws">Tiny shopping cart</a> <div class="%%embed%%"> <a href="/embed.yaws">Embedding Yaws</a></div> <div class="%%json_intro%%"> <a href="/json_intro.yaws">AJAX/Json RPC</a></div> <div class="%%haxe_intro%%"> <a href="/haxe_intro.yaws">haXe remoting</a></div> diff --git a/www/shopingcart/Makefile b/www/shopingcart/Makefile deleted file mode 100644 index da9baf6..0000000 --- a/www/shopingcart/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -include ../../include.mk - -all: shopcart.beam - cp shopcart.beam ../../examples/ebin -debug: - -install: all - cp shopcart.beam $(DESTDIR)$(VARDIR)/yaws/ebin - -clean: - $(RM) shopcart.beam - $(RM) ../../examples/ebin/shopcart.beam - -shopcart.beam: shopcart.erl - $(ERLC) shopcart.erl diff --git a/www/shopingcart/buy.yaws b/www/shopingcart/buy.yaws deleted file mode 100644 index 6cc2d47..0000000 --- a/www/shopingcart/buy.yaws +++ /dev/null @@ -1,11 +0,0 @@ -<erl> - -out(A) -> - case shopcart:top(A) of - ok -> - shopcart:buy(A); - X -> - X - end. - -</erl> diff --git a/www/shopingcart/index.yaws b/www/shopingcart/index.yaws deleted file mode 100644 index 811ea8a..0000000 --- a/www/shopingcart/index.yaws +++ /dev/null @@ -1,11 +0,0 @@ -<erl> - -out(A) -> - case shopcart:top(A) of - ok -> - shopcart:index(A); - X -> - X - end. - -</erl> diff --git a/www/shopingcart/junk.jpg b/www/shopingcart/junk.jpg deleted file mode 100644 index d1380d4a3a458459f5ba93e8cfeb5de60c83e835..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5989 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∨Zd+o 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%0A@O zgc`b`li4Ws5NhNH%S<3YY-{Ka(NCJSXUKQ2J>TlrG;38YlKv=Qtch226`k3VK%nN> z#+Ld@p}8HBVTn4g$ZN*n+MT`se06CRjt;|#vaLecf0d+ifM^eRZ+Oxzb`NCq4a>hi z<LKQ*Y=OZ~D22Zp=#@$s)>$<~7e!Yzv>;aecPhpf_?q?493(8vzY4|2t9$fx|L%`T zp<eRV-C^$|XsJ9ipEWRGoc%1POH4*(yd}I7(^nU&lv5s(|5=HL$w`xd8h@FlR#cuO zGJgDpPF0D)`of0g?BRLTp4;*0q^wJ%v>CW2VQ}FM75K=IO>N$vf;P{r{x@|Yx4OMk etnHb+lI+LZ^ujn4%9??}^y`10g2(k?_J07uI9G%K diff --git a/www/shopingcart/loginpost.yaws b/www/shopingcart/loginpost.yaws deleted file mode 100644 index c4ea0e9..0000000 --- a/www/shopingcart/loginpost.yaws +++ /dev/null @@ -1,7 +0,0 @@ - -<erl> - -out(A) -> - shopcart:loginpost(A). - -</erl> diff --git a/www/shopingcart/logout.yaws b/www/shopingcart/logout.yaws deleted file mode 100644 index 0ac2f53..0000000 --- a/www/shopingcart/logout.yaws +++ /dev/null @@ -1,12 +0,0 @@ -<erl> - - -out(A) -> - case shopcart:top(A) of - ok -> - shopcart:logout(A); - X -> - X - end. - -</erl> diff --git a/www/shopingcart/shopcart.erl b/www/shopingcart/shopcart.erl deleted file mode 100644 index f19a7f6..0000000 --- a/www/shopingcart/shopcart.erl +++ /dev/null @@ -1,434 +0,0 @@ - -%% a small shoppingcart example which tries to show -%% a variety of tricks and tacticts to display a -%% shopingcart style page with server side state. - - --module(shopcart). --author('kl...@hy...'). - --compile(export_all). --include("../../include/yaws_api.hrl"). --include_lib("kernel/include/inet.hrl"). - - -%% this is the opaque structure we pass to the -%% yaws cookie session server - --record(sess, { - user, - passwd, - addr, - items = []}). - - -%% this function extracts the session from the cookie -check_cookie(A) -> - H = A#arg.headers, - case yaws_api:find_cookie_val("ssid", H#headers.cookie) of - Val when Val /= [] -> - case yaws_api:cookieval_to_opaque(Val) of - {ok, Sess} -> - {ok, Sess, Val}; - {error, {has_session, Sess}} -> - {ok, Sess}; - Else -> - Else - end; - [] -> - {error, nocookie} - end. - - -%% this function is calle first in all out yaws files, -%% it will autologin users that are not logged in -top(A) -> - case check_cookie(A) of - {ok, _Session, _Cookie} -> - ok; - {error, Reason} -> - login(A) - end. - - - -%% generate a css head the title of the page set dynamically -css_head(PageTitle) -> - Z = - [<<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"> -<html> - -<head> - <meta name=\"keywords\" content=\"Nortel Extranet VPN\"> - <title>">>, - PageTitle, - <<"</title> - <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"> - <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"> -</head> - -<body bgcolor=\"linen\"> - -">> - ], - {html, Z}. - - - -%% the little status field in the upper left corner -head_status(User) -> - T = - {ehtml, - {table, [], - {tr, [], - [{td, [{width, "30%"}], - {table, [ {border, "1"}, {bgcolor, beige},{bordercolor, black}], - [{tr, [], {td, [], pb("User: ~s", [User])}} - ]} - }, - {td, [{align, right}], {img, [{src, "junk.jpg"} - ]}} - ] - } - } - }. - - -%% bold paragraph according to style.css -pb(Fmt, Args) -> - {p, [{class, pb}], io_lib:format(Fmt, Args)}. - - -%% toprow of buttons to push -toprow() -> - {ehtml, - {table, [{cellspacing, "4"}, - {bgcolor, tan}, - {width, "100%"} - ], - [ - {tr, [], - [{td, [], {a, [{href, "buy.yaws"}] , {p, [{class, toprow}], "Buy"}}}, - {td, [], {a, [{href, "logout.yaws"}], {p, [{class, toprow}], "Logout"}}}, - {td, [], {a, [{href, "source.html"}], {p, [{class, toprow}], "The Source"}}}, - {td, [{width, "70%"}], ""} - ]} - ] - } - }. - - - -%% kinda hackish since we us ehtml -bot() -> - {html, "</body> \n </html> \n"}. - - - -%% This function displays the login page -login(A) -> - CSS = css_head("Shopcart"), - Head = head_status("Not lgged in"), - Top = toprow(), - Login = - {ehtml, - [{h2, [], "Shopcart login"}, - {form, [{method, get}, - {action, "loginpost.yaws"}], - [ - {p, [], "Username"}, - {input, [{name, user}, - {type, text}, - {value, "Joe Junk shopper"}, - {size, "48"}]}, - - - {p, [], "Password"}, - {input, [{name, password}, - {type, text}, - {value, "xyz123"}, - {size, "48"}]}, - - {input, [{type, submit}, - {value, "Login"}]}, - - {input, [{name, url}, - {type, hidden}, - {value, xpath((A#arg.req)#http_request.path, A)}]} - ] - } - ]}, - [CSS, Head, Top, Login, bot(), break]. - - - - -logout(A) -> - {ok, Sess, Cookie} = check_cookie(A), - yaws_api:delete_cookie_session(Cookie), - {ehtml, {h3, [], "Yo, "}}. - - - - -%% This is the function that gets invoked when the -%% user has attempted to login -%% The trick used here is to pass the original URL in a hidden -%% field into this function, if the login is successful, we redirect -%% to the original URL. - -loginpost(A) -> - case {yaws_api:queryvar(A, "user"), - yaws_api:queryvar(A, "url"), - yaws_api:queryvar(A, "password")} of - - {{ok, User}, - {ok, Url}, - {ok, Pwd}} -> - - %% here's the place to validate the user - %% we allow all users, - io:format("User ~p logged in ~n", [User]), - Sess = #sess{user = User, - passwd = Pwd}, - Cookie = yaws_api:new_cookie_session(Sess), - [yaws_api:redirect(Url), - yaws_api:setcookie("ssid",Cookie)]; - _ -> - login(A) - end. - -xpath({abs_path, P}, _A) -> - P. - -%% this is the function that gets the form when the user -%% hits "update Cart" - -formupdate(A) -> - {ok, Sess, Cookie} = check_cookie(A), - J = junk(), - Items = Sess#sess.items, - L = yaws_api:parse_post(A), - I2 = handle_l(L, Items), - Sess2 = Sess#sess{items = I2}, - yaws_api:replace_cookie_session(Cookie, Sess2), - {redirect, "index.yaws"}. %% force browser to reload - -handle_l([], Items) -> - Items; -handle_l([{Str, Num} | Tail], Items) -> - case catch list_to_integer(Num) of - Int when integer(Int) -> - handle_l(Tail, [{Str, Int} | lists:keydelete(Str,1, Items)]); - _ -> - handle_l(Tail, Items) - end. - - -ip(A) -> - S = A#arg.clisock, - case inet:peername(S) of - {ok, {Ip, Port}} -> - case inet:gethostbyaddr(Ip) of - {ok, HE} -> - io_lib:format("~s/~s", [fmtip(Ip), HE#hostent.h_name]); - Err -> - io_lib:format("~s", [fmtip(Ip)]) - end; - _ -> - [] - end. - -fmtip({A,B,C,D}) -> - io_lib:format("~w.~w.~w.~w", [A,B,C,D]). - - -%% generate the final "you have bought page ... " -buy(A) -> - {ok, Sess, Cookie} = check_cookie(A), - Css = css_head("Shopcart"), - Head = head_status(Sess#sess.user), - Top = toprow(), - BROWS = b_rows(Sess#sess.items), - Res = - if - length (BROWS) > 0 -> - {ehtml, - [{h4, [], "Congratulations, you have bought"}, - {table, [],BROWS}, - {hr}, - {p , [{class, toprow}], - io_lib:format( - "Items are at this very moment being shipped to the" - " residens of the computer with IP: ~s~n", [ip(A)])} - ] - }; - true -> - {ehtml, - [{h4, [], "Congratulations, you have bought nothing"}]} - end, - - - [Css, Head, Top, Res, bot()]. - - -b_rows(Items) -> - J = junk(), - Desc = {tr,[], - [ - {th, [], pb("Description",[])}, - {th, [], pb("Quantity",[])}, - {th, [], pb("Sum ",[])}]}, - - [Desc | b_rows(Items, J, 0, [])]. - -b_rows([{Desc, Num}|Tail], Junk, Ack, TRS) when Num > 0 -> - {value, {_, Price}} = lists:keysearch(Desc, 1, Junk), - A = Num * Price, - TR = {tr, [], - [{td, [], Desc}, - {td, [], io_lib:format("~w", [Num])}, - {td, [], io_lib:format("~w", [A])} - ]}, - b_rows(Tail, Junk, A+Ack, [TR|TRS]); - -b_rows([{Desc, Num}|Tail], Junk, Ack, TRS) when Num == 0 -> - b_rows(Tail, Junk, Ack, TRS); - -b_rows([], _, Ack, TRS) when Ack > 0 -> - Tax = round(0.27 * Ack), - Empty = {td, [], []}, - TaxRow = {tr, [], - [ - {td, [], pb("Swedish VAT tax 27% ",[])}, - Empty, - {td, [], pb("~w", [Tax])} - ]}, - Total = {tr, [], - [ - {td, [], pb("Total ",[])}, - Empty, - {td, [], pb("~w", [Ack + Tax])} - ]}, - - lists:reverse([Total, TaxRow | TRS]); -b_rows(_, _,_,_) -> - []. - - - - -%% this is the main function which displays -%% the shopcart ..... -%% the entire shopcart is one big form which gets posted -%% when the user updates the shopcart -index(A) -> - {ok, Sess, Cookie} = check_cookie(A), - io:format("Inside index: ~p~n", [Sess#sess.items]), - Css = css_head("Shopcart"), - Head = head_status(Sess#sess.user), - Top = toprow(), - Cart = - {ehtml, - {form, - [{name, form}, - {method,post}, - {action, "shopcart_form.yaws"}], - [ - {table, [{bgcolor, linen}, {border, "2"}], - rows(Sess#sess.items)}, - - {input, [{type, submit}, {value, "Update Cart"}]} - ] - } - }, - - [Css, Head, Top, Cart, bot()]. - - -%% this function gets a list of -%% {JunkString, Num} and displays the current shopcart - -rows(Items) -> - Junk = junk(), - First = {tr, [], - [{th, [], pb("Num Items", [])}, - {th, [], pb("Item description", [])}, - {th, [], pb("Price SEK ",[])} - ]}, - - L = lists:map( - fun({Desc, Price}) -> - {tr, [], - [{td, [], - {input, [{type, text}, - {width, "4"}, - {value, jval(Desc, Items)}, - {name, Desc}]}}, - {td, [], {p, [], Desc}}, - {td, [], pb("~w ", [Price])} - ]} - end, Junk), - - Total = total(Items, Junk, 0), - Tax = round(0.27 * Total), - T = [{tr, [], - [{td, [], pb("Sum accumulated",[])}, - {td, [{colspan, "2"}], pb("~w SEK", [Total])} - ] - }, - {tr, [], - [ - {td, [], pb("Swedish VAT tax 27 % :-)",[])}, - {td, [{colspan, "2"}], pb("~w SEK", [Tax])} - ] - }, - - {tr, [], - [ - {td, [], pb("Total",[])}, - {td, [{colspan, "2"}], pb("~w SEK", [Total + Tax])} - ] - } - ], - - Rows = [First | L] ++ T. - - - - - -%% The Items are picked up by the -%% formupdate function and set accordingly in the opaque state -%% this function recalculates the sum total - -total([{Str, Num} | Tail], Junk, Ack) -> - {value, {Str, Price}} = lists:keysearch(Str, 1, Junk), - total(Tail, Junk, (Num * Price) + Ack); -total([], _,Ack) -> - Ack. - - -%% We need to set the value in each input field -jval(Str, Items) -> - case lists:keysearch(Str, 1, Items) of - {value, {_, Num}} when integer(Num) -> - integer_to_list(Num); - false -> - "0" - end. - - -%% the store database :-) -%% {Description, Price} tuples -junk() -> - [{"Toothbrush in rainbow colours", 18}, - {"Twinset of extra soft towels", 66}, - {"Hangover pill - guaranteed to work", 88}, - {"Worlk-out kit that fits under your bed", 1900}, - {"100 pack of headache pills", 7}, - {"Free subscription to MS update packs", 999}, - {"Toilet cleaner", 1111}, - {"Body lotion 4 litres", 888}, - {"Yello, a lifetime supply", 99}]. - diff --git a/www/shopingcart/shopcart_form.yaws b/www/shopingcart/shopcart_form.yaws deleted file mode 100644 index 16be937..0000000 --- a/www/shopingcart/shopcart_form.yaws +++ /dev/null @@ -1,11 +0,0 @@ -<erl> - -out(A) -> - case shopcart:top(A) of - ok -> - shopcart:formupdate(A); - X -> - X - end. - -</erl> diff --git a/www/shopingcart/source.html b/www/shopingcart/source.html deleted file mode 100644 index 0baffa7..0000000 --- a/www/shopingcart/source.html +++ /dev/null @@ -1,98 +0,0 @@ -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> -<html> - -<head> - <meta name="keywords" content="Yaws"> - <title>Yaws</title> - <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> - <link rel="stylesheet" type="text/css" href="style.css"> -</head> - -<html> - -<h2> The source for the shopping cart </h2> - -<p class=pp>The shoppingcart contains a number of tricks and "preferred" ways to - code different typical solutions to servers side state applications. - -The source itself resides in the www/shopingcart directory in the -source code tree. A <a href="shopcart.erl">link to the source itself</a> to view from -the browser. - -<h3> The first trick, the <tt>break</tt> statement </h3> - -<p class="pp"> -The source of all yaws pages (including example index.yaws) -look very similar in this application, specifically - -<div style="background: rgb(211, 211, 211)"> -<pre> -out(A) -> - case shopcart:top(A) of - ok -> - shopcart:index(A); - X -> - X - end. -</pre> -</div> - -<p class=pp>All code, including the head containing the link -to the stylesheet is dynamically -generated. The first function in all code snippets in yaws files is always -a call to <tt>shopcart:top(A)</tt> -<p class=pp>The <tt>top/1</tt> function will check the cookie and if a cookie -which is associated to an active session is found, the request is granted, -otherwise the login page is displayed by the <tt>shopcart:login(A)</tt> function. -<p class=pp>The last item displayed by the login function is the atom <tt>break</tt>. When the -yaws server sees the <tt>break</tt>, it will not process any more data from the -yaws page being requested. Thus, we can have the login call at the top of a yaws -page and still have subsequent html/yaws code on the same page. - - -<h3>Redirect to the requested page</h3> - -<p class=pp>Since the <tt>login(A)</tt> function is supplied with the -Arg of the requested page, the login function -will set the path of the requested page in a hidden field in the login form. -The code that processes the login form will then issue a redirect to the -value of this hidden field if the login in successful. - - -<h3>Ehtml</h3> -<p class=pp>The use of the ehtml output syntax makes it more convenient to dynamically -generate structured content: - -<ul> -<li><p class=pp> -It is easier to get the structure right, -<li><p class=pp>It -is easier to inject the dynamically generated parts in the structure, -<li><p class=pp>It is <em>much </em> more beautiful. -</ul> - -<p class=pp>See the <a href="shopcart.erl">source</a> for a number of "proofs" for this. - -<h3>The use of the <b>yaws_session_server </h3> -<p class=pp>The yaws_session_server is designed to facilitate server side -state applications. The yaws application writer is supplied -with the following capabilities and services: - -<ol> -<li><p class=pp>Truly random cookie generation. Each cookie can -subsequently be used to uniquely identify a session. - -<li><p class=pp>Maintenance of an opaque application state in an ets table. -This state is readily available to the yaws page by means of the cookie. - -<li><p class=pp>Old idle sessions are garbage collected. - -</li> -</ol> - - - -</body> -</html> - - diff --git a/www/shopingcart/style.css b/www/shopingcart/style.css deleted file mode 100644 index ee48b66..0000000 --- a/www/shopingcart/style.css +++ /dev/null @@ -1,57 +0,0 @@ -<style TYPE="text/css"> - - body { - font-family: Verdana, Arial, Helvetica, sans-serif; - color: black; - } - - div.links { - background: green; - } - - - H1, H2, H4, H5 { - text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-weight: bold; - font-variant: small-caps - } - - - P { text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-weight: normal; - font-size: small; - } - - .toprow { - text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-weight: bold; - } - - .pb { - text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-weight: bold; - font-size: small; - } - - - .pp { - text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-size: medium - } - - - A { text-decoration: none; - font-family: Verdana, Arial, Helvetica, sans-serif; - font-weight: normal; - color: green; - } - A:link { color: #0000f0 } /* unvisited link */ - A:active { color: lime } /* active links */ - A:hover { background: brown } - -</style> diff --git a/www/shoppingcart/Makefile b/www/shoppingcart/Makefile new file mode 100644 index 0000000..da9baf6 --- /dev/null +++ b/www/shoppingcart/Makefile @@ -0,0 +1,15 @@ +include ../../include.mk + +all: shopcart.beam + cp shopcart.beam ../../examples/ebin +debug: + +install: all + cp shopcart.beam $(DESTDIR)$(VARDIR)/yaws/ebin + +clean: + $(RM) shopcart.beam + $(RM) ../../examples/ebin/shopcart.beam + +shopcart.beam: shopcart.erl + $(ERLC) shopcart.erl diff --git a/www/shoppingcart/buy.yaws b/www/shoppingcart/buy.yaws new file mode 100644 index 0000000..6cc2d47 --- /dev/null +++ b/www/shoppingcart/buy.yaws @@ -0,0 +1,11 @@ +<erl> + +out(A) -> + case shopcart:top(A) of + ok -> + shopcart:buy(A); + X -> + X + end. + +</erl> diff --git a/www/shoppingcart/index.yaws b/www/shoppingcart/index.yaws new file mode 100644 index 0000000..811ea8a --- /dev/null +++ b/www/shoppingcart/index.yaws @@ -0,0 +1,11 @@ +<erl> + +out(A) -> + case shopcart:top(A) of + ok -> + shopcart:index(A); + X -> + X + end. + +</erl> diff --git a/www/shoppingcart/junk.jpg b/www/shoppingcart/junk.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1380d4a3a458459f5ba93e8cfeb5de60c83e835 GIT binary patch literal 5989 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∨Zd+o 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%0A@O zgc`b`li4Ws5NhNH%S<3YY-{Ka(NCJSXUKQ2J>TlrG;38YlKv=Qtch226`k3VK%nN> z#+Ld@p}8HBVTn4g$ZN*n+MT`se06CRjt;|#vaLecf0d+ifM^eRZ+Oxzb`NCq4a>hi z<LKQ*Y=OZ~D22Zp=#@$s)>$<~7e!Yzv>;aecPhpf_?q?493(8vzY4|2t9$fx|L%`T zp<eRV-C^$|XsJ9ipEWRGoc%1POH4*(yd}I7(^nU&lv5s(|5=HL$w`xd8h@FlR#cuO zGJgDpPF0D)`of0g?BRLTp4;*0q^wJ%v>CW2VQ}FM75K=IO>N$vf;P{r{x@|Yx4OMk etnHb+lI+LZ^ujn4%9??}^y`10g2(k?_J07uI9G%K literal 0 HcmV?d00001 diff --git a/www/shoppingcart/loginpost.yaws b/www/shoppingcart/loginpost.yaws new file mode 100644 index 0000000..c4ea0e9 --- /dev/null +++ b/www/shoppingcart/loginpost.yaws @@ -0,0 +1,7 @@ + +<erl> + +out(A) -> + shopcart:loginpost(A). + +</erl> diff --git a/www/shoppingcart/logout.yaws b/www/shoppingcart/logout.yaws new file mode 100644 index 0000000..0ac2f53 --- /dev/null +++ b/www/shoppingcart/logout.yaws @@ -0,0 +1,12 @@ +<erl> + + +out(A) -> + case shopcart:top(A) of + ok -> + shopcart:logout(A); + X -> + X + end. + +</erl> diff --git a/www/shoppingcart/shopcart.erl b/www/shoppingcart/shopcart.erl new file mode 100644 index 0000000..95471b4 --- /dev/null +++ b/www/shoppingcart/shopcart.erl @@ -0,0 +1,434 @@ + +%% a small shoppingcart example which tries to show +%% a variety of tricks and tacticts to display a +%% shoppingcart style page with server side state. + + +-module(shopcart). +-author('kl...@hy...'). + +-compile(export_all). +-include("../../include/yaws_api.hrl"). +-include_lib("kernel/include/inet.hrl"). + + +%% this is the opaque structure we pass to the +%% yaws cookie session server + +-record(sess, { + user, + passwd, + addr, + items = []}). + + +%% this function extracts the session from the cookie +check_cookie(A) -> + H = A#arg.headers, + case yaws_api:find_cookie_val("ssid", H#headers.cookie) of + Val when Val /= [] -> + case yaws_api:cookieval_to_opaque(Val) of + {ok, Sess} -> + {ok, Sess, Val}; + {error, {has_session, Sess}} -> + {ok, Sess}; + Else -> + Else + end; + [] -> + {error, nocookie} + end. + + +%% this function is calle first in all out yaws files, +%% it will autologin users that are not logged in +top(A) -> + case check_cookie(A) of + {ok, _Session, _Cookie} -> + ok; + {error, Reason} -> + login(A) + end. + + + +%% generate a css head the title of the page set dynamically +css_head(PageTitle) -> + Z = + [<<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"> +<html> + +<head> + <meta name=\"keywords\" content=\"Nortel Extranet VPN\"> + <title>">>, + PageTitle, + <<"</title> + <meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"> + <link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\"> +</head> + +<body bgcolor=\"linen\"> + +">> + ], + {html, Z}. + + + +%% the little status field in the upper left corner +head_status(User) -> + T = + {ehtml, + {table, [], + {tr, [], + [{td, [{width, "30%"}], + {table, [ {border, "1"}, {bgcolor, beige},{bordercolor, black}], + [{tr, [], {td, [], pb("User: ~s", [User])}} + ]} + }, + {td, [{align, right}], {img, [{src, "junk.jpg"} + ]}} + ] + } + } + }. + + +%% bold paragraph according to style.css +pb(Fmt, Args) -> + {p, [{class, pb}], io_lib:format(Fmt, Args)}. + + +%% toprow of buttons to push +toprow() -> + {ehtml, + {table, [{cellspacing, "4"}, + {bgcolor, tan}, + {width, "100%"} + ], + [ + {tr, [], + [{td, [], {a, [{href, "buy.yaws"}] , {p, [{class, toprow}], "Buy"}}}, + {td, [], {a, [{href, "logout.yaws"}], {p, [{class, toprow}], "Logout"}}}, + {td, [], {a, [{href, "source.html"}], {p, [{class, toprow}], "The Source"}}}, + {td, [{width, "70%"}], ""} + ]} + ] + } + }. + + + +%% kinda hackish since we us ehtml +bot() -> + {html, "</body> \n </html> \n"}. + + + +%% This function displays the login page +login(A) -> + CSS = css_head("Shopcart"), + Head = head_status("Not lgged in"), + Top = toprow(), + Login = + {ehtml, + [{h2, [], "Shopcart login"}, + {form, [{method, get}, + {action, "loginpost.yaws"}], + [ + {p, [], "Username"}, + {input, [{name, user}, + {type, text}, + {value, "Joe Junk shopper"}, + {size, "48"}]}, + + + {p, [], "Password"}, + {input, [{name, password}, + {type, text}, + {value, "xyz123"}, + {size, "48"}]}, + + {input, [{type, submit}, + {value, "Login"}]}, + + {input, [{name, url}, + {type, hidden}, + {value, xpath((A#arg.req)#http_request.path, A)}]} + ] + } + ]}, + [CSS, Head, Top, Login, bot(), break]. + + + + +logout(A) -> + {ok, Sess, Cookie} = check_cookie(A), + yaws_api:delete_cookie_session(Cookie), + {ehtml, {h3, [], "Yo, "}}. + + + + +%% This is the function that gets invoked when the +%% user has attempted to login +%% The trick used here is to pass the original URL in a hidden +%% field into this function, if the login is successful, we redirect +%% to the original URL. + +loginpost(A) -> + case {yaws_api:queryvar(A, "user"), + yaws_api:queryvar(A, "url"), + yaws_api:queryvar(A, "password")} of + + {{ok, User}, + {ok, Url}, + {ok, Pwd}} -> + + %% here's the place to validate the user + %% we allow all users, + io:format("User ~p logged in ~n", [User]), + Sess = #sess{user = User, + passwd = Pwd}, + Cookie = yaws_api:new_cookie_session(Sess), + [yaws_api:redirect(Url), + yaws_api:setcookie("ssid",Cookie)]; + _ -> + login(A) + end. + +xpath({abs_path, P}, _A) -> + P. + +%% this is the function that gets the form when the user +%% hits "update Cart" + +formupdate(A) -> + {ok, Sess, Cookie} = check_cookie(A), + J = junk(), + Items = Sess#sess.items, + L = yaws_api:parse_post(A), + I2 = handle_l(L, Items), + Sess2 = Sess#sess{items = I2}, + yaws_api:replace_cookie_session(Cookie, Sess2), + {redirect, "index.yaws"}. %% force browser to reload + +handle_l([], Items) -> + Items; +handle_l([{Str, Num} | Tail], Items) -> + case catch list_to_integer(Num) of + Int when integer(Int) -> + handle_l(Tail, [{Str, Int} | lists:keydelete(Str,1, Items)]); + _ -> + handle_l(Tail, Items) + end. + + +ip(A) -> + S = A#arg.clisock, + case inet:peername(S) of + {ok, {Ip, Port}} -> + case inet:gethostbyaddr(Ip) of + {ok, HE} -> + io_lib:format("~s/~s", [fmtip(Ip), HE#hostent.h_name]); + Err -> + io_lib:format("~s", [fmtip(Ip)]) + end; + _ -> + [] + end. + +fmtip({A,B,C,D}) -> + io_lib:format("~w.~w.~w.~w", [A,B,C,D]). + + +%% generate the final "you have bought page ... " +buy(A) -> + {ok, Sess, Cookie} = check_cookie(A), + Css = css_head("Shopcart"), + Head = head_status(Sess#sess.user), + Top = toprow(), + BROWS = b_rows(Sess#sess.items), + Res = + if + length (BROWS) > 0 -> + {ehtml, + [{h4, [], "Congratulations, you have bought"}, + {table, [],BROWS}, + {hr}, + {p , [{class, toprow}], + io_lib:format( + "Items are at this very moment being shipped to the" + " residens of the computer with IP: ~s~n", [ip(A)])} + ] + }; + true -> + {ehtml, + [{h4, [], "Congratulations, you have bought nothing"}]} + end, + + + [Css, Head, Top, Res, bot()]. + + +b_rows(Items) -> + J = junk(), + Desc = {tr,[], + [ + {th, [], pb("Description",[])}, + {th, [], pb("Quantity",[])}, + {th, [], pb("Sum ",[])}]}, + + [Desc | b_rows(Items, J, 0, [])]. + +b_rows([{Desc, Num}|Tail], Junk, Ack, TRS) when Num > 0 -> + {value, {_, Price}} = lists:keysearch(Desc, 1, Junk), + A = Num * Price, + TR = {tr, [], + [{td, [], Desc}, + {td, [], io_lib:format("~w", [Num])}, + {td, [], io_lib:format("~w", [A])} + ]}, + b_rows(Tail, Junk, A+Ack, [TR|TRS]); + +b_rows([{Desc, Num}|Tail], Junk, Ack, TRS) when Num == 0 -> + b_rows(Tail, Junk, Ack, TRS); + +b_rows([], _, Ack, TRS) when Ack > 0 -> + Tax = round(0.27 * Ack), + Empty = {td, [], []}, + TaxRow = {tr, [], + [ + {td, [], pb("Swedish VAT tax 27% ",[])}, + Empty, + {td, [], pb("~w", [Tax])} + ]}, + Total = {tr, [], + [ + {td, [], pb("Total ",[])}, + Empty, + {td, [], pb("~w", [Ack + Tax])} + ]}, + + lists:reverse([Total, TaxRow | TRS]); +b_rows(_, _,_,_) -> + []. + + + + +%% this is the main function which displays +%% the shopcart ..... +%% the entire shopcart is one big form which gets posted +%% when the user updates the shopcart +index(A) -> + {ok, Sess, Cookie} = check_cookie(A), + io:format("Inside index: ~p~n", [Sess#sess.items]), + Css = css_head("Shopcart"), + Head = head_status(Sess#sess.user), + Top = toprow(), + Cart = + {ehtml, + {form, + [{name, form}, + {method,post}, + {action, "shopcart_form.yaws"}], + [ + {table, [{bgcolor, linen}, {border, "2"}], + rows(Sess#sess.items)}, + + {input, [{type, submit}, {value, "Update Cart"}]} + ] + } + }, + + [Css, Head, Top, Cart, bot()]. + + +%% this function gets a list of +%% {JunkString, Num} and displays the current shopcart + +rows(Items) -> + Junk = junk(), + First = {tr, [], + [{th, [], pb("Num Items", [])}, + {th, [], pb("Item description", [])}, + {th, [], pb("Price SEK ",[])} + ]}, + + L = lists:map( + fun({Desc, Price}) -> + {tr, [], + [{td, [], + {input, [{type, text}, + {width, "4"}, + {value, jval(Desc, Items)}, + {name, Desc}]}}, + {td, [], {p, [], Desc}}, + {td, [], pb("~w ", [Price])} + ]} + end, Junk), + + Total = total(Items, Junk, 0), + Tax = round(0.27 * Total), + T = [{tr, [], + [{td, [], pb("Sum accumulated",[])}, + {td, [{colspan, "2"}], pb("~w SEK", [Total])} + ] + }, + {tr, [], + [ + {td, [], pb("Swedish VAT tax 27 % :-)",[])}, + {td, [{colspan, "2"}], pb("~w SEK", [Tax])} + ] + }, + + {tr, [], + [ + {td, [], pb("Total",[])}, + {td, [{colspan, "2"}], pb("~w SEK", [Total + Tax])} + ] + } + ], + + Rows = [First | L] ++ T. + + + + + +%% The Items are picked up by the +%% formupdate function and set accordingly in the opaque state +%% this function recalculates the sum total + +total([{Str, Num} | Tail], Junk, Ack) -> + {value, {Str, Price}} = lists:keysearch(Str, 1, Junk), + total(Tail, Junk, (Num * Price) + Ack); +total([], _,Ack) -> + Ack. + + +%% We need to set the value in each input field +jval(Str, Items) -> + case lists:keysearch(Str, 1, Items) of + {value, {_, Num}} when integer(Num) -> + integer_to_list(Num); + false -> + "0" + end. + + +%% the store database :-) +%% {Description, Price} tuples +junk() -> + [{"Toothbrush in rainbow colours", 18}, + {"Twinset of extra soft towels", 66}, + {"Hangover pill - guaranteed to work", 88}, + {"Worlk-out kit that fits under your bed", 1900}, + {"100 pack of headache pills", 7}, + {"Free subscription to MS update packs", 999}, + {"Toilet cleaner", 1111}, + {"Body lotion 4 litres", 888}, + {"Yello, a lifetime supply", 99}]. + diff --git a/www/shoppingcart/shopcart_form.yaws b/www/shoppingcart/shopcart_form.yaws new file mode 100644 index 0000000..16be937 --- /dev/null +++ b/www/shoppingcart/shopcart_form.yaws @@ -0,0 +1,11 @@ +<erl> + +out(A) -> + case shopcart:top(A) of + ok -> + shopcart:formupdate(A); + X -> + X + end. + +</erl> diff --git a/www/shoppingcart/source.html b/www/shoppingcart/source.html new file mode 100644 index 0000000..4716241 --- /dev/null +++ b/www/shoppingcart/source.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> + +<head> + <meta name="keywords" content="Yaws"> + <title>Yaws</title> + <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> + <link rel="stylesheet" type="text/css" href="style.css"> +</head> + +<html> + +<h2> The source for the shopping cart </h2> + +<p class=pp>The shoppingcart contains a number of tricks and "preferred" ways to + code different typical solutions to servers side state applications. + +The source itself resides in the www/shoppingcart directory in the +source code tree. A <a href="shopcart.erl">link to the source itself</a> to view from +the browser. + +<h3> The first trick, the <tt>break</tt> statement </h3> + +<p class="pp"> +The source of all yaws pages (including example index.yaws) +look very similar in this application, specifically + +<div style="background: rgb(211, 211, 211)"> +<pre> +out(A) -> + case shopcart:top(A) of + ok -> + shopcart:index(A); + X -> + X + end. +</pre> +</div> + +<p class=pp>All code, including the head containing the link +to the stylesheet is dynamically +generated. The first function in all code snippets in yaws files is always +a call to <tt>shopcart:top(A)</tt> +<p class=pp>The <tt>top/1</tt> function will check the cookie and if a cookie +which is associated to an active session is found, the request is granted, +otherwise the login page is displayed by the <tt>shopcart:login(A)</tt> function. +<p class=pp>The last item displayed by the login function is the atom <tt>break</tt>. When the +yaws server sees the <tt>break</tt>, it will not process any more data from the +yaws page being requested. Thus, we can have the login call at the top of a yaws +page and still have subsequent html/yaws code on the same page. + + +<h3>Redirect to the requested page</h3> + +<p class=pp>Since the <tt>login(A)</tt> function is supplied with the +Arg of the requested page, the login function +will set the path of the requested page in a hidden field in the login form. +The code that processes the login form will then issue a redirect to the +value of this hidden field if the login in successful. + + +<h3>Ehtml</h3> +<p class=pp>The use of the ehtml output syntax makes it more convenient to dynamically +generate structured content: + +<ul> +<li><p class=pp> +It is easier to get the structure right, +<li><p class=pp>It +is easier to inject the dynamically generated parts in the structure, +<li><p class=pp>It is <em>much </em> more beautiful. +</ul> + +<p class=pp>See the <a href="shopcart.erl">source</a> for a number of "proofs" for this. + +<h3>The use of the <b>yaws_session_server </h3> +<p class=pp>The yaws_session_server is designed to facilitate server side +state applications. The yaws application writer is supplied +with the following capabilities and services: + +<ol> +<li><p class=pp>Truly random cookie generation. Each cookie can +subsequently be used to uniquely identify a session. + +<li><p class=pp>Maintenance of an opaque application state in an ets table. +This state is readily available to the yaws page by means of the cookie. + +<li><p class=pp>Old idle sessions are garbage collected. + +</li> +</ol> + + + +</body> +</html> + + diff --git a/www/shoppingcart/style.css b/www/shoppingcart/style.css new file mode 100644 index 0000000..ee48b66 --- /dev/null +++ b/www/shoppingcart/style.css @@ -0,0 +1,57 @@ +<style TYPE="text/css"> + + body { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: black; + } + + div.links { + background: green; + } + + + H1, H2, H4, H5 { + text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: bold; + font-variant: small-caps + } + + + P { text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: normal; + font-size: small; + } + + .toprow { + text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: bold; + } + + .pb { + text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: bold; + font-size: small; + } + + + .pp { + text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: medium + } + + + A { text-decoration: none; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-weight: normal; + color: green; + } + A:link { color: #0000f0 } /* unvisited link */ + A:active { color: lime } /* active links */ + A:hover { background: brown } + +</style> -- 1.6.1.3 |