From 1cfbbce15eb843a40ee3def4eb28b3fc7beff234 Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Tue, 4 May 2021 15:00:50 +0200 Subject: [PATCH 1/5] Add a template render controller to mod_ginger_spa. (Dev053) --- modules/mod_ginger_spa/README.md | 37 +++++++++- .../controller_ginger_spa_template.erl | 69 +++++++++++++++++++ modules/mod_ginger_spa/dispatch/dispatch | 5 +- .../mod_ginger_spa/templates/public/test.tpl | 2 + 4 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl create mode 100644 modules/mod_ginger_spa/templates/public/test.tpl diff --git a/modules/mod_ginger_spa/README.md b/modules/mod_ginger_spa/README.md index 923f5ca06..448d65257 100644 --- a/modules/mod_ginger_spa/README.md +++ b/modules/mod_ginger_spa/README.md @@ -1,7 +1,42 @@ mod_ginger_spa -======================== +============== Default template and dispatch rules for a single page app. _Only routes for which a Ginger resource with the same path exists will be served by the `page.tpl` template. Other routes should be manually added to the sites dispatch rules._ + + +Template rendering +------------------ + +The `controller_ginger_spa_template` renders templates and returns the result as plain text. + +Pass the template name in the path, example: + + https://example.com/render-template/public/test.tpl + +This renders the template `"public/test.tpl"`. + +Pass arguments via the query string: + + https://example.com/render-template/public/test.tpl?id=1 + +Or in a POST body as `application/x-www-form-urlencoded` or `multipart/form-data`. + +Use the argument `is_cat_include=1` to render the template using a *catinclude*. +Any `id` argument is always added as `id` to the template arguments (after mapping using `m_rsc:rid/2`), in +this way the `id` is always available and usable for the optional catinclude. + +Security +........ + +As in Zotonic 0.x template models are not access checked there is a security using the name of the template. + +Templates starting with `public/...` can be rendered by anonymous users. + +Templates starting with `member/...` can be rendered by authenticated users. + +Administrators are allowed to render all templates. + + diff --git a/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl new file mode 100644 index 000000000..3a4e97890 --- /dev/null +++ b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl @@ -0,0 +1,69 @@ +%% @copyright Driebit BV 2021 +%% @doc Controller to render templates. Post arguments via the query arguments. + +-module(controller_ginger_spa_template). + +-export([init/1, service_available/2, charsets_provided/2, content_types_provided/2]). +-export([allowed_methods/2, resource_exists/2]). +-export([process_post/2, provide_content/2]). + +-include_lib("controller_webmachine_helper.hrl"). +-include_lib("include/zotonic.hrl"). + +init(DispatchArgs) -> {ok, DispatchArgs}. + +service_available(ReqData, DispatchArgs) when is_list(DispatchArgs) -> + Context1 = z_context:new_request(ReqData, DispatchArgs, ?MODULE), + ?WM_REPLY(true, Context1). + +allowed_methods(ReqData, Context) -> + {[ 'POST', 'GET', 'HEAD' ], ReqData, Context}. + +charsets_provided(ReqData, Context) -> + {[{"utf-8", fun(X) -> X end}], ReqData, Context}. + +content_types_provided(ReqData, Context) -> + {[{"text/plain", provide_content}], ReqData, Context}. + +resource_exists(ReqData, Context) -> + Context1 = ?WM_REQ(ReqData, Context), + Context2 = z_context:ensure_all(Context1), + ?WM_REPLY(true, Context2). + +process_post(ReqData, Context) -> + Context1 = ?WM_REQ(ReqData, Context), + Result = render(Context1), + ReqData1 = wrq:set_resp_body(Result, ReqData), + {{halt, 200}, ReqData1, Context}. + +provide_content(ReqData, Context) -> + Context1 = ?WM_REQ(ReqData, Context), + Result = render(Context1), + ?WM_REPLY(Result, Context1). + +render(Context) -> + Template = z_context:get_q("*", Context), + case is_viewable(Template, Context) of + true -> + Vars = [ + {is_spa_render, true}, + {id, m_rsc:rid(z_context:get_q("id", Context), Context)} + ], + Template1 = case is_cat_include(Context) of + true -> {cat, Template}; + false -> Template + end, + {Data, _} = z_template:render_to_iolist(Template1, Vars, Context), + iolist_to_binary(Data); + false -> + lager:info("[~p] Denied render of template \"~s\"", [ z_context:site(Context), Template ]), + <<>> + end. + +is_cat_include(Context) -> + z_convert:to_bool(z_context:get_q("is_cat_include", Context)). + +is_viewable("/" ++ _, _Context) -> false; +is_viewable("public/" ++ _, _Context) -> true; +is_viewable("member/" ++ _, Context) -> z_auth:is_auth(Context); +is_viewable(T, Context) when is_list(T) -> z_acl:is_admin(Context). diff --git a/modules/mod_ginger_spa/dispatch/dispatch b/modules/mod_ginger_spa/dispatch/dispatch index 29349bd5b..0ea5a2f65 100644 --- a/modules/mod_ginger_spa/dispatch/dispatch +++ b/modules/mod_ginger_spa/dispatch/dispatch @@ -1,3 +1,6 @@ [{home, [], controller_template, [ {template, "page.tpl"}, {id, home} ]}, - {search, ["search"], controller_template, [ {template, "page.tpl"} ]} + {search, ["search"], controller_template, [ {template, "page.tpl"} ]}, + + % Render a template the path is the template, pass arguments via the query post or get + {spa_render, ["render-template", '*'], controller_ginger_spa_template, []} ]. diff --git a/modules/mod_ginger_spa/templates/public/test.tpl b/modules/mod_ginger_spa/templates/public/test.tpl new file mode 100644 index 000000000..f9e9006c2 --- /dev/null +++ b/modules/mod_ginger_spa/templates/public/test.tpl @@ -0,0 +1,2 @@ +{% debug %} +{% print q.v %} From 1485c114ec434c1fe6f07189e9a1f291f8d13f36 Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Tue, 4 May 2021 15:01:16 +0200 Subject: [PATCH 2/5] Support the zotonic environment in ginger_environment. --- .../support/ginger_environment.erl | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/mod_ginger_base/support/ginger_environment.erl b/modules/mod_ginger_base/support/ginger_environment.erl index 748776715..53f96b452 100644 --- a/modules/mod_ginger_base/support/ginger_environment.erl +++ b/modules/mod_ginger_base/support/ginger_environment.erl @@ -14,13 +14,20 @@ %% @doc Get current environment -spec get(#context{}) -> dev|acc|prod. get(Context) -> - case is_dev(Context) of - true -> dev; - false -> - case is_acc(Context) of - true -> acc; - false -> prod - end + case z_config:get(environment) of + production -> + case is_dev(Context) of + true -> dev; + false -> + case is_acc(Context) of + true -> acc; + false -> prod + end + end; + acceptance -> + acc; + _ -> + dev end. is_dev(Context) -> From 52ccb92eab40f612df2233edaf4533c462a8f26d Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Thu, 6 May 2021 14:29:13 +0200 Subject: [PATCH 3/5] Rename 'is_cat_include' to 'catinclude'. This to use the same arg as in other template routines. --- modules/mod_ginger_spa/README.md | 2 +- .../controllers/controller_ginger_spa_template.erl | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/mod_ginger_spa/README.md b/modules/mod_ginger_spa/README.md index 448d65257..e01bf1abc 100644 --- a/modules/mod_ginger_spa/README.md +++ b/modules/mod_ginger_spa/README.md @@ -24,7 +24,7 @@ Pass arguments via the query string: Or in a POST body as `application/x-www-form-urlencoded` or `multipart/form-data`. -Use the argument `is_cat_include=1` to render the template using a *catinclude*. +Use the argument `catinclude=1` to render the template using a *catinclude*. Any `id` argument is always added as `id` to the template arguments (after mapping using `m_rsc:rid/2`), in this way the `id` is always available and usable for the optional catinclude. diff --git a/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl index 3a4e97890..31d25481e 100644 --- a/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl +++ b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl @@ -49,7 +49,7 @@ render(Context) -> {is_spa_render, true}, {id, m_rsc:rid(z_context:get_q("id", Context), Context)} ], - Template1 = case is_cat_include(Context) of + Template1 = case is_catinclude(Context) of true -> {cat, Template}; false -> Template end, @@ -60,8 +60,8 @@ render(Context) -> <<>> end. -is_cat_include(Context) -> - z_convert:to_bool(z_context:get_q("is_cat_include", Context)). +is_catinclude(Context) -> + z_convert:to_bool(z_context:get_q("catinclude", Context)). is_viewable("/" ++ _, _Context) -> false; is_viewable("public/" ++ _, _Context) -> true; From 30c98301c8d7408a31e4732d2881c91d3c0b02a8 Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Tue, 11 May 2021 17:00:41 +0200 Subject: [PATCH 4/5] Make allow spa template renders explicit via dispatch rules. --- .../controller_ginger_spa_template.erl | 32 +++++++++++++------ modules/mod_ginger_spa/dispatch/dispatch | 2 +- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl index 31d25481e..b1cb25233 100644 --- a/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl +++ b/modules/mod_ginger_spa/controllers/controller_ginger_spa_template.erl @@ -42,9 +42,8 @@ provide_content(ReqData, Context) -> ?WM_REPLY(Result, Context1). render(Context) -> - Template = z_context:get_q("*", Context), - case is_viewable(Template, Context) of - true -> + case template(Context) of + {ok, Template} -> Vars = [ {is_spa_render, true}, {id, m_rsc:rid(z_context:get_q("id", Context), Context)} @@ -55,15 +54,30 @@ render(Context) -> end, {Data, _} = z_template:render_to_iolist(Template1, Vars, Context), iolist_to_binary(Data); - false -> - lager:info("[~p] Denied render of template \"~s\"", [ z_context:site(Context), Template ]), + {error, _} -> + lager:info("[~p] Denied render of template \"~s\"", [ z_context:site(Context), m_req:get(path, Context) ]), <<>> end. is_catinclude(Context) -> z_convert:to_bool(z_context:get_q("catinclude", Context)). -is_viewable("/" ++ _, _Context) -> false; -is_viewable("public/" ++ _, _Context) -> true; -is_viewable("member/" ++ _, Context) -> z_auth:is_auth(Context); -is_viewable(T, Context) when is_list(T) -> z_acl:is_admin(Context). +template(Context) -> + case z_context:get(template, Context) of + undefined -> + case path(Context) of + "api/render-template/" ++ Template -> + {ok, drop_slash(Template)}; + _ -> + {error, enoent} + end; + Template -> + {ok, Template} + end. + +drop_slash("/" ++ Path) -> drop_slash(Path); +drop_slash(Path) -> Path. + +path(Context) -> + DispatchPath = z_context:get_q("zotonic_dispatch_path", Context), + lists:flatten( z_utils:combine($/, DispatchPath) ). diff --git a/modules/mod_ginger_spa/dispatch/dispatch b/modules/mod_ginger_spa/dispatch/dispatch index 0ea5a2f65..532322267 100644 --- a/modules/mod_ginger_spa/dispatch/dispatch +++ b/modules/mod_ginger_spa/dispatch/dispatch @@ -2,5 +2,5 @@ {search, ["search"], controller_template, [ {template, "page.tpl"} ]}, % Render a template the path is the template, pass arguments via the query post or get - {spa_render, ["render-template", '*'], controller_ginger_spa_template, []} + {spa_render, ["api", "render-template", "public", '*' ], controller_ginger_spa_template, []} ]. From a8e284092b52ff9a000f19f7a8b129e9f11bc8db Mon Sep 17 00:00:00 2001 From: Marc Worrell Date: Tue, 11 May 2021 17:02:23 +0200 Subject: [PATCH 5/5] Make m_rdf_export more robust against missing resources. --- modules/mod_ginger_rdf/models/m_rdf_export.erl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/mod_ginger_rdf/models/m_rdf_export.erl b/modules/mod_ginger_rdf/models/m_rdf_export.erl index c83227d92..9adfaa7ce 100644 --- a/modules/mod_ginger_rdf/models/m_rdf_export.erl +++ b/modules/mod_ginger_rdf/models/m_rdf_export.erl @@ -24,7 +24,10 @@ to_rdf(Id, Context) -> %% - media. -spec to_rdf(m_rsc:resource(), [module()], z:context()) -> m_rdf:rdf_resource(). to_rdf(Id, Ontologies, Context) -> - Properties = m_rsc:get_visible(Id, Context), + Properties = case m_rsc:get_visible(Id, Context) of + undefined -> []; + Props -> Props + end, Edges = m_edge:get_edges(Id, Context), Types = types(proplists:get_value(category_id, Properties), Context), Triples = lists:flatten(