%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2009-2010 Marc Worrell
%%
%% @doc Module for rendering and caching scomps. Scomps can be caching and
%% non caching, depending on the passed arguments and the results of the
%% scomp's varies/2 function.
%% Copyright 2009-2010 Marc Worrell
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(z_scomp).
-author("Marc Worrell <marc@worrell.nl>").
-export([render/4, render_all/4, render_optional/4]).
-include_lib("zotonic.hrl").
%% @spec render(ScompName, Args, Vars, Context) -> {ok, Context} | {ok, io_list} | {error, Reason}
%% @doc Render the names scomp, Args are the scomp arguments and Vars are the variables given to the template
render(ScompName, Args, Vars, Context) ->
case z_module_indexer:find(scomp, ScompName, Context) of
{ok, #module_index{erlang_module=ModuleName}} ->
ScompContext = z_context:prune_for_scomp(Context),
render_scomp_module(ModuleName, Args, Vars, ScompContext, Context);
{error, enoent} ->
%% No such scomp, as we can switch on/off functionality we do a quiet skip
?LOG_WARNING(#{
text => <<"Scomp not enabled">>,
in => zotonic_core,
result => error,
reason => enoent,
scomp => ScompName
}),
<<>>
end.
render_optional(ScompName, Args, Vars, Context) ->
case z_module_indexer:find(scomp, ScompName, Context) of
{ok, #module_index{erlang_module=ModuleName}} ->
ScompContext = z_context:prune_for_scomp(Context),
render_scomp_module(ModuleName, ['$optional'|Args], Vars, ScompContext, Context);
{error, enoent} ->
<<>>
end.
render_all(ScompName, Args, Vars, Context) ->
case z_module_indexer:find(scomp, ScompName, Context) of
{ok, #module_index{erlang_module=ModuleName}} ->
ScompContext = z_context:prune_for_scomp(Context),
render_scomp_module(ModuleName, [{'$all', true}|Args], Vars, ScompContext, Context);
{error, enoent} ->
<<>>
end.
render_scomp_module(ModuleName, Args, Vars, ScompContext, Context) ->
ScompContextWM = ScompContext#context{
cowreq = Context#context.cowreq,
cowenv = Context#context.cowenv
},
case vary(ModuleName, Args, ScompContext) of
nocache ->
case ModuleName:render(Args, Vars, ScompContextWM) of
{ok, Result} -> render_state(Result);
{error, Reason} -> throw({error, Reason})
end;
{CachKeyArgs, MaxAge, Varies} ->
Key = key(ModuleName, CachKeyArgs, ScompContextWM),
RenderFun = fun() ->
case ModuleName:render(Args, Vars, ScompContextWM) of
{ok, Result} -> render_state(Result);
{error, Reason} -> throw({error, Reason})
end
end,
z_depcache:memo(RenderFun, Key, MaxAge, Varies, Context)
end.
render_state(#context{} = Context) ->
z_context:get_render_state(Context);
render_state(MixedHtml) ->
MixedHtml.
%% @doc Create an unique key for the scomp and the visibility level it is rendered for
%% @spec key(atom(), proplist(), context()) -> term()
key(ScompName, EssentialParams, Context) ->
{ScompName, EssentialParams, z_acl:cache_key(Context), z_context:language(Context)}.
%% @doc Check how and if the scomp wants to be cached.
vary(ModuleName, Args, ScompContext) ->
case ModuleName:vary(Args, ScompContext) of
default ->
%% Scomp asks default behaviour, check the arguments for caching args
MaxAge = proplists:get_value(max_age, Args),
case z_convert:to_integer(MaxAge) of
undefined ->
nocache;
Max ->
Vary = proplists:get_all_values(vary, Args),
Args1 = proplists:delete(max_age, Args),
Args2 = proplists:delete(vary, Args1),
{Args2, Max, Vary}
end;
Other ->
Other
end.