PageRenderTime 48ms CodeModel.GetById 18ms app.highlight 23ms RepoModel.GetById 2ms app.codeStats 0ms

/modules/mod_menu/mod_menu.erl

https://code.google.com/p/zotonic/
Erlang | 197 lines | 126 code | 33 blank | 38 comment | 0 complexity | 24f2afa40630466c29841f7205275694 MD5 | raw file
  1%% @author Marc Worrell <marc@worrell.nl>
  2%% @copyright 2009 Marc Worrell
  3%% Date: 2009-07-12
  4%% @doc Menu module.  Supports menus in Zotonic. Adds admin interface to define the menu.
  5
  6%% Copyright 2009 Marc Worrell
  7%%
  8%% Licensed under the Apache License, Version 2.0 (the "License");
  9%% you may not use this file except in compliance with the License.
 10%% You may obtain a copy of the License at
 11%% 
 12%%     http://www.apache.org/licenses/LICENSE-2.0
 13%% 
 14%% Unless required by applicable law or agreed to in writing, software
 15%% distributed under the License is distributed on an "AS IS" BASIS,
 16%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 17%% See the License for the specific language governing permissions and
 18%% limitations under the License.
 19
 20-module(mod_menu).
 21-author("Marc Worrell <marc@worrell.nl>").
 22
 23-mod_title("Menus").
 24-mod_description("Menus in Zotonic, adds amdin interface to define the menu.").
 25
 26-include("zotonic.hrl").
 27
 28%% interface functions
 29-export([
 30    init/1,
 31    datamodel/1,
 32    get_menu/1,
 33    get_menu/2,
 34    set_menu/3,
 35    observe_menu_get_rsc_ids/2,
 36    observe_menu_save/2,
 37    test/0,
 38    menu_flat/2
 39]).
 40
 41%% @doc The datamodel for the menu routines.
 42datamodel(Context) ->
 43    [
 44     {categories,
 45      [
 46       {menu, categorization,
 47        [{title, <<"Page menu">>}]
 48       }
 49      ]
 50     },
 51     {resources,
 52      case z_install_defaultdata:default_menu(m_site:get(skeleton, Context)) of
 53          undefined ->
 54              [];
 55          Menu ->
 56              [
 57               {main_menu,
 58                menu,
 59                [{title, <<"Main menu">>},
 60                 {menu, Menu}
 61                ]
 62               }
 63              ]
 64      end
 65     }
 66    ].
 67
 68%% @doc Initializes the module (after the datamodel is installed).
 69init(Context) ->
 70    case m_config:get(menu, menu_default, Context) of
 71        undefined -> ok;
 72        Props -> 
 73            %% upgrade from previous menu
 74            OldMenu = proplists:get_value(menu, Props, []),
 75            ?zInfo("Upgrading old menu structure", Context),
 76            set_menu(OldMenu, Context),
 77            m_config:delete(menu, menu_default, Context)
 78    end.
 79
 80
 81
 82%% @doc Notifier handler to get all menu ids for the default menu.
 83observe_menu_get_rsc_ids(menu_get_rsc_ids, Context) ->
 84    Menu = get_menu(Context),
 85    {ok, menu_ids(Menu, [])}.
 86
 87    menu_ids([], Acc) ->
 88        Acc;
 89    menu_ids([{Id,SubMenu}|T], Acc) ->
 90        Acc1 = menu_ids(SubMenu, [Id|Acc]),
 91        menu_ids(T, Acc1);
 92    menu_ids([H|T], Acc) ->
 93        menu_ids(T, [H|Acc]).
 94
 95
 96%% @doc Observer the 'menu_save' notification
 97observe_menu_save({menu_save, MenuId, Menu}, Context) ->
 98    set_menu(MenuId, Menu, Context).
 99
100
101%% @doc Fetch the default menu. Performs validation/visibility checking on the menu items.
102%% @spec get_menu(Context) -> list()
103get_menu(Context) ->
104    get_menu(m_rsc:rid(main_menu, Context), Context).
105
106%% @doc Fetch a menu structure from a rsc. Performs validation/visibility checking on the menu items.
107%% @spec get_menu(Id, Context) -> list()
108get_menu(Id, Context) ->
109    case m_rsc:p(Id, menu, Context) of
110        undefined -> [];
111        <<>> -> [];
112        Menu -> remove_invisible(validate(Menu, []), [], Context)
113    end.
114
115	validate([], Acc) ->
116		lists:reverse(Acc);
117	validate([{_M,_L} = M|Ms], Acc) ->
118		validate(Ms, [M|Acc]);
119	validate([M|Ms], Acc) ->
120		validate(Ms, [{M,[]}|Acc]).
121
122
123%% Remove invisible menu items
124remove_invisible([], Acc, _Context) ->
125    lists:reverse(Acc);
126remove_invisible([{Id,Sub}|Rest], Acc, Context) ->
127    case m_rsc:is_visible(Id, Context) of
128        true ->  remove_invisible(Rest, [{Id,remove_invisible(Sub, [], Context)} | Acc], Context);
129        false -> remove_invisible(Rest, Acc, Context)
130    end;
131remove_invisible([Id|Rest], Acc, Context) ->
132    case m_rsc:is_visible(Id, Context) of
133        true ->  remove_invisible(Rest, [Id | Acc], Context);
134        false -> remove_invisible(Rest, Acc, Context)
135    end.
136
137
138%% @doc Save the default menu.
139set_menu(Menu, Context) ->
140    Id = m_rsc:rid(main_menu, Context),
141    set_menu(Id, Menu, Context).
142
143
144%% @doc Save the current menu.
145set_menu(Id, Menu, Context) ->
146    m_rsc:update(Id, [{menu, Menu}], Context).
147
148
149
150menu_flat(undefined, _Context) ->
151    [];
152menu_flat(<<>>, _Context) ->
153    [];
154menu_flat(X, Context) ->
155    menu_flat(X, [1], [], Context).
156
157menu_flat([], _Path, Acc, _Context) ->
158    lists:reverse(Acc);
159menu_flat([ {MenuId, []} | Rest], [Idx|PR], Acc, Context ) ->
160
161    [ {m_rsc:rid(MenuId, Context), [Idx|PR], undefined} ] 
162        ++ menu_flat(Rest, [Idx+1|PR], [], Context)
163        ++  Acc;
164menu_flat([ {MenuId, Children} | Rest], [Idx|PR], Acc, Context ) ->
165
166    [ {m_rsc:rid(MenuId, Context), [Idx|PR], down} ] 
167        ++ menu_flat(Children, [1,Idx|PR], [], Context) 
168        ++ [{undefined, undefined, up}]
169        ++ menu_flat(Rest, [Idx+1|PR], [], Context)
170        ++  Acc;
171menu_flat([ MenuId | Rest ], P, A, C) when is_integer(MenuId) ->
172    %% oldschool notation fallback
173    menu_flat([{MenuId, []} | Rest], P, A, C).
174
175
176
177%% @doc test function
178%%  111  [1]
179%%  - 44   [1,1]
180%%  - - 555  [1,1,1]
181%%  - - 666  [1,1,2]
182%%  222  [2]
183%%  - 333  [2,1]
184test() ->
185
186    [
187     {111, [1], down },
188     {444, [1,1], down},
189     {555, [1,1,1], undefined},
190     {666, [2,1,1], undefined},
191     {undefined, undefined, up},
192     {undefined, undefined, up},
193     {222, [2], down},
194     {333, [1,2], undefined},
195     {undefined, undefined, up}
196    ]
197        = menu_flat([{111, [{444, [{555, []}, {666, []} ]}]}, {222, [{333, []}]}], x).