PageRenderTime 35ms CodeModel.GetById 16ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/mod_admin_category/mod_admin_category.erl

http://github.com/zotonic/zotonic
Erlang | 197 lines | 152 code | 25 blank | 20 comment | 1 complexity | 6f859feda3b1b06cbb7fde4a3dcba046 MD5 | raw file
  1%% @author Marc Worrell <marc@worrell.nl>
  2%% @copyright 2009-2015 Marc Worrell
  3%% @doc Module for editing and managing categories.
  4
  5%% Copyright 2009-2015 Marc Worrell
  6%%
  7%% Licensed under the Apache License, Version 2.0 (the "License");
  8%% you may not use this file except in compliance with the License.
  9%% You may obtain a copy of the License at
 10%% 
 11%%     http://www.apache.org/licenses/LICENSE-2.0
 12%% 
 13%% Unless required by applicable law or agreed to in writing, software
 14%% distributed under the License is distributed on an "AS IS" BASIS,
 15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16%% See the License for the specific language governing permissions and
 17%% limitations under the License.
 18
 19-module(mod_admin_category).
 20-author("Marc Worrell <marc@worrell.nl>").
 21
 22-mod_title("Admin category support").
 23-mod_description("Support editing and changing the category hierarchy.").
 24-mod_prio(600).
 25-mod_depends([admin, menu]).
 26-mod_provides([]).
 27
 28-export([
 29    event/2,
 30    observe_category_hierarchy_save/2,
 31    observe_admin_menu/3,
 32    observe_rsc_delete/2
 33]).
 34
 35-include_lib("zotonic.hrl").
 36-include_lib("modules/mod_admin/include/admin_menu.hrl").
 37
 38
 39event(#submit{message={delete_move, Args}}, Context) ->
 40    ToCatId = z_convert:to_integer(z_context:get_q_validated("category_id", Context)),
 41    {id, Id} = proplists:lookup(id, Args),
 42    Ids = [ Id | m_hierarchy:children('$category', Id, Context) ],
 43    case deletable(Ids, Context) andalso z_acl:rsc_editable(ToCatId, Context) of
 44        true ->
 45            Context1 = z_context:prune_for_async(Context),
 46            spawn(fun() ->
 47                    cat_move_and_delete(Ids, ToCatId, Context1)
 48                  end),
 49            z_render:wire({dialog_close, []}, Context);
 50        false ->
 51            z_render:growl(?__("Sorry, you are not allowed to delete this.", Context), Context)
 52    end;
 53event(#postback{message={delete_all, Args}}, Context) ->
 54    {id, Id} = proplists:lookup(id, Args),
 55    IfEmpty = proplists:get_value(if_empty, Args, false),
 56    Ids = [ Id | m_hierarchy:children('$category', Id, Context) ],
 57    case not IfEmpty orelse not m_category:is_used(Id, Context) of
 58        true ->
 59            case deletable(Ids, Context)  of
 60                true ->
 61                    Context1 = z_context:prune_for_async(Context),
 62                    spawn(fun() ->
 63                            cat_delete(Ids, Context1)
 64                          end),
 65                    z_render:wire({dialog_close, []}, Context);
 66                false ->
 67                    z_render:growl(?__("Sorry, you are not allowed to delete this.", Context), Context)
 68            end;
 69        false ->
 70            z_render:wire({alert, [{message, ?__("Delete is canceled, there are pages in the category.", Context)}]}, Context)
 71    end.
 72
 73cat_delete(Ids, Context) ->
 74    z_session_page:add_script(z_render:wire({mask, [{message, ?__("Deleting...", Context)}]}, Context)),
 75    RscIds = in_categories(Ids, Context),
 76    case delete_all(RscIds, 0, length(RscIds), Context) of
 77        ok ->
 78            lists:foreach(fun(Id) ->
 79                             m_rsc:delete(Id, Context)
 80                          end,
 81                          Ids),
 82            z_session_page:add_script(z_render:wire({unmask, []}, Context));
 83        {error, _} ->
 84            Context1 = z_render:wire([
 85                    {unmask, []},
 86                    {alert, [{message, ?__("Not all resources could be deleted.", Context)}]}
 87                ],
 88                Context),
 89            z_session_page:add_script(Context1)
 90
 91    end.
 92
 93cat_move_and_delete(Ids, ToGroupId, Context) ->
 94    z_session_page:add_script(z_render:wire({mask, [{message, ?__("Deleting...", Context)}]}, Context)),
 95    RscIds = in_categories(Ids, Context),
 96    ok = move_all(RscIds, ToGroupId, 0, length(RscIds), Context),
 97    lists:foreach(fun(Id) ->
 98                     m_rsc:delete(Id, Context)
 99                  end,
100                  Ids),
101    z_session_page:add_script(z_render:wire({unmask, []}, Context)),
102    ok.
103
104in_categories(Ids, Context) when is_list(Ids) ->
105    RscIds = z_db:q("select id from rsc where category_id in (SELECT(unnest($1::int[])))", [Ids], Context, 60000),
106    [ RscId || {RscId} <- RscIds ].
107
108delete_all([], _N, _Total, _Context) ->
109    ok;
110delete_all([{Id}|Ids], N, Total, Context) ->
111    case catch m_rsc:delete(Id, Context) of
112        ok ->
113            maybe_progress(N, N+1, Total, Context),
114            delete_all(Ids, N+1, Total, Context);
115        Error ->
116            {error, Error}
117    end.
118
119move_all([], _ToCatId, _N, _Total, _Context) ->
120    ok;
121move_all([{Id}|Ids], ToCatId, N, Total, Context) ->
122    m_rsc_update:update(Id, [{category_id, ToCatId}], z_acl:sudo(Context)),
123    maybe_progress(N, N+1, Total, Context),
124    move_all(Ids, ToCatId, N+1, Total, Context).
125
126maybe_progress(_N1, _N2, 0, _Context) ->
127    ok;
128maybe_progress(N1, N2, Total, Context) ->
129    z_pivot_rsc:pivot_delay(Context),
130    PerStep = Total / 100,
131    S1 = round(N1 / PerStep),
132    S2 = round(N2 / PerStep),
133    case S1 of
134        S2 -> ok;
135        _ -> z_session_page:add_script(z_render:wire({mask_progress, [{percent,S2}]}, Context))
136    end.
137
138deletable(Ids, Context) ->
139    lists:all(fun(Id) -> z_acl:rsc_deletable(Id, Context) end, Ids).
140
141
142observe_category_hierarchy_save(#category_hierarchy_save{tree=New}, Context) ->
143    case z_acl:is_allowed(insert, category, Context) of
144        true ->
145            case m_hierarchy:menu('$category', Context) of
146                New -> 
147                    ok;
148                Old ->
149                    % Check if any ids are added or deleted
150                    NewIds = lists:sort(ids(New, [])),
151                    OldIds = lists:sort(ids(Old, [])),
152                    Deleted = OldIds -- NewIds,
153                    % Inserted = NewIds -- OldIds,
154
155                    % Delete all ids not in the new category tree
156                    lists:map(fun(Id) ->
157                                  ok = m_category:delete(Id, undefined, Context)
158                              end,
159                              Deleted),
160
161                    _ = m_hierarchy:save_nocheck('$category', New, Context),
162                    m_category:flush(Context),
163                    ok
164            end;
165        false ->
166            undefined
167    end.
168
169%% @doc Do not allow a category to be removed iff there are resources within that category
170observe_rsc_delete(#rsc_delete{id=Id, is_a=IsA}, Context) ->
171    case lists:member(category, IsA) of
172        true ->
173            case m_category:is_used(Id, Context) of
174                true -> throw({error, is_used});
175                false -> ok
176            end;
177        false ->
178            ok
179    end.
180
181observe_admin_menu(admin_menu, Acc, Context) ->
182    [
183     #menu_item{id=admin_categories,
184                parent=admin_structure,
185                label=?__("Categories", Context),
186                url={admin_category_sorter},
187                visiblecheck={acl, insert, category}}
188     
189     |Acc].
190
191
192ids([], Acc) ->
193    Acc;
194ids([{Id,Sub}|Rest], Acc) ->
195    Acc1 = ids(Sub, Acc),
196    ids(Rest, [Id|Acc1]).
197