Current section

Files

Jump to
zotonic_mod_admin_frontend src mod_admin_frontend.erl
Raw

src/mod_admin_frontend.erl

%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2013-2026 Marc Worrell
%% @doc Administrative interface version for use via a web site's frontend.
%% @end
%% Copyright 2013-2026 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(mod_admin_frontend).
-moduledoc("
Adds editing of resources, menu-trees and collections for non-admin users.
With many sites it is needed to let “normal” users edit content. For those users the `/admin` interface is
overwhelming and also gives too many options that only administrators should be using.
For this use-case the mod_admin_frontend is created. It reuses some templates and parts of the normal admin, but in
such a way that only the most needed options and fields are present. It also enables editing a menu-tree or collection,
side-by-side with the content in the menu-tree.
Interface and dispatching
-------------------------
The interface is a one-page interface which uses postbacks to update the currently edited resource and forms. The
editing history is maintained by using hashes in the url.
The URL of the edit page is generated by the dispatch rule `admin_frontend_edit`:
```erlang
[
{admin_frontend_edit, [\"edit\"], controller_page, [{acl, is_auth}, {template, \"page_admin_frontend_edit.tpl\"}]},
{admin_frontend_edit, [\"edit\", id], controller_page, [{acl_action, edit}, {template, {cat, \"page_admin_frontend_edit.tpl\"}}]}
].
```
The template uses the `menu_rsc` filter to find the contextual menu for the resource being edited. Per default the
`menu_rsc` filter will fallback to the resource with the name `main_menu`. Hook into the `#menu_rsc{}` notification to
change this behavior.
To edit a specific resource in the context of a menu that is non-standard for the resource, use the following code:
```erlang
<a href=\"{% url admin_frontend_edit id=my_menu %}#edit_id={{ id }}\">Click to edit {{ id.title }}</a>
```
Customizing and templates
-------------------------
The `mod_admin_frontend` makes heavy use of `catinclude` to find the correct templates.
The column with the menu is rendered using `{% catinclude \"_admin_menu_menu_view.tpl\" tree_id ... %}`. The
resource-column with the main editing form is rendered using `{% catinclude \"_admin_frontend_edit.tpl\" id ... %}`.
This column is loaded lazily via a postback.
### Customizing the menu column
Extra content can be added above or below the menu view by overruling the blocks `above_menu` and `below_menu`.
### Customizing the main edit column
The main edit template `_admin_frontend_edit.tpl` provides the main edit-form, javascript initializations and fields for
basic publication status editing.
The main edit block `edit_blocks` is defined as follows and can be overruled for specific categories:
```django
{% block edit_blocks %}
{% catinclude \"_admin_edit_basics.tpl\" id is_editable=is_editable languages=languages %}
{% if id.category_id.feature_show_address %}
{% catinclude \"_admin_edit_content_address.tpl\" id is_editable=is_editable languages=languages %}
{% endif %}
{% all catinclude \"_admin_edit_content.tpl\" id is_editable=is_editable languages=languages %}
{% if id.is_a.media or id.medium %}
{% include \"_admin_edit_content_media.tpl\" %}
{% endif %}
{% catinclude \"_admin_edit_body.tpl\" id is_editable=is_editable languages=languages %}
{% catinclude \"_admin_edit_blocks.tpl\" id is_editable=is_editable languages=languages %}
{% catinclude \"_admin_edit_depiction.tpl\" id is_editable=is_editable languages=languages %}
{% endblock %}
```
If any content needs to be added on top of the page, between the publication checkbox and the main edit fields, then
overrule the block `meta_data_after`.
If you click on the cog icon on the right, then a meta data panel is shown (for access-control options and language
settings). This panel can be extended with extra tabs using the blocks `meta_tabs` and `meta_panels`.
See also
[mod_admin](/id/doc_module_mod_admin), [menu_rsc](/id/doc_template_filter_filter_menu_rsc)
Accepted Events
---------------
Delegate callbacks:
- `event/2` with `postback` messages: `admin_menu_edit`.
- `event/2` with `postback_notify` messages: `<<\"admin-menu-edit\">>`.
").
-author("Marc Worrell <marc@worrell.nl>").
-mod_title("Admin Frontend").
-mod_description("Edit pages on a web site; subset of admin module for Bootstrap based web sites.").
-mod_depends([mod_admin, mod_mqtt]).
-mod_prio(500).
-export([
event/2
]).
-include_lib("zotonic_core/include/zotonic.hrl").
event(#postback{message={admin_menu_edit, Args}}, Context) ->
maybe_load_edit_panel(Args, Context);
event(#postback_notify{message= <<"admin-menu-edit">>}, Context) ->
maybe_load_edit_panel([], Context);
event(#sort{} = S, Context) ->
controller_admin_edit:event(S, Context).
maybe_load_edit_panel(Args, Context) ->
case m_rsc:rid(z_context:get_q(<<"id">>, Context, proplists:get_value(id, Args)), Context) of
undefined ->
maybe_load_edit_cat(Args, Context);
Id ->
Vars = [
{id, Id},
{tab, z_context:get_q(<<"tab">>, Context)},
{tree_id, m_rsc:rid(z_context:get_q(<<"tree_id">>, Context), Context)}
],
Context1 = z_render:update("editcol", #render{template={cat, "_admin_frontend_edit.tpl"}, vars=Vars}, Context),
z_render:add_script(<<"setTimeout(function() { z_editor_init(); }, 100);">>, Context1)
end.
maybe_load_edit_cat(Args, Context) ->
case m_rsc:rid(z_context:get_q(<<"cat">>, Context, proplists:get_value(cat, Args)), Context) of
undefined ->
Context;
CatId ->
Vars = [
{id, undefined},
{cat, CatId},
{tab, z_context:get_q(<<"tab">>, Context)},
{tree_id, m_rsc:rid(z_context:get_q(<<"tree_id">>, Context), Context)}
],
Context1 = z_render:update("editcol", #render{template={cat, "_admin_frontend_edit.tpl", m_category:is_a(CatId, Context)}, vars=Vars}, Context),
z_render:add_script(<<"setTimeout(function() { z_editor_init(); }, 100);">>, Context1)
end.