diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl
index 7d5ae36c9..de2333558 100644
--- a/src/ejabberd_update.erl
+++ b/src/ejabberd_update.erl
@@ -28,13 +28,15 @@
-author('alexey@process-one.net').
%% API
--export([update/0, update_info/0]).
+-export([update/0, update/1, update_info/0]).
-include("ejabberd.hrl").
%%====================================================================
%% API
%%====================================================================
+
+%% Update all the modified modules
update() ->
case update_info() of
{ok, Dir, _UpdatedBeams, _Script, LowLevelScript, _Check} ->
@@ -48,48 +50,91 @@ update() ->
{error, Reason}
end.
-update_info() ->
- Dir = filename:dirname(code:which(ejabberd)),
- case file:list_dir(Dir) of
- {ok, Files} ->
- Beams = [list_to_atom(filename:rootname(FN)) ||
- FN <- Files, lists:suffix(".beam", FN)],
- UpdatedBeams =
- lists:filter(
- fun(Module) ->
- {ok, {Module, NewVsn}} =
- beam_lib:version(code:which(Module)),
- case code:is_loaded(Module) of
- {file, _} ->
- Attrs = Module:module_info(attributes),
- {value, {vsn, CurVsn}} =
- lists:keysearch(vsn, 1, Attrs),
- NewVsn /= CurVsn;
- false ->
- false
- end
- end, Beams),
- ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]),
- Script = make_script(UpdatedBeams),
- ?INFO_MSG("script: ~p~n", [Script]),
- LowLevelScript = make_low_level_script(UpdatedBeams, Script),
- ?INFO_MSG("low level script: ~p~n", [LowLevelScript]),
- Check =
- release_handler_1:check_script(
- LowLevelScript,
+%% Update only the specified modules
+update(ModulesToUpdate) ->
+ case update_info() of
+ {ok, Dir, UpdatedBeamsAll, _Script, _LowLevelScript, _Check} ->
+ UpdatedBeamsNow =
+ [A || A <- UpdatedBeamsAll, B <- ModulesToUpdate, A == B],
+ {_, LowLevelScript, _} = build_script(Dir, UpdatedBeamsNow),
+ Eval =
+ release_handler_1:eval_script(
+ LowLevelScript, [],
[{ejabberd, "", filename:join(Dir, "..")}]),
- ?INFO_MSG("check: ~p~n", [Check]),
- {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check};
+ ?INFO_MSG("eval: ~p~n", [Eval]),
+ Eval;
{error, Reason} ->
{error, Reason}
end.
+%% Get information about the modified modules
+update_info() ->
+ Dir = filename:dirname(code:which(ejabberd)),
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ update_info(Dir, Files);
+ {error, Reason} ->
+ {error, Reason}
+ end.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-%% From systools.hrl
+update_info(Dir, Files) ->
+ Beams = lists:sort(get_beams(Files)),
+ UpdatedBeams = get_updated_beams(Beams),
+ ?INFO_MSG("beam files: ~p~n", [UpdatedBeams]),
+ {Script, LowLevelScript, Check} = build_script(Dir, UpdatedBeams),
+ {ok, Dir, UpdatedBeams, Script, LowLevelScript, Check}.
+
+get_beams(Files) ->
+ [list_to_atom(filename:rootname(FN))
+ || FN <- Files, lists:suffix(".beam", FN)].
+
+%% Return only the beams that have different version
+get_updated_beams(Beams) ->
+ lists:filter(
+ fun(Module) ->
+ NewVsn = get_new_version(Module),
+ case code:is_loaded(Module) of
+ {file, _} ->
+ CurVsn = get_current_version(Module),
+ (NewVsn /= CurVsn
+ andalso NewVsn /= unknown_version);
+ false ->
+ false
+ end
+ end, Beams).
+
+get_new_version(Module) ->
+ Path = code:which(Module),
+ VersionRes = beam_lib:version(Path),
+ case VersionRes of
+ {ok, {Module, NewVsn}} -> NewVsn;
+ %% If a m1.erl has -module("m2"):
+ _ -> unknown_version
+ end.
+
+get_current_version(Module) ->
+ Attrs = Module:module_info(attributes),
+ {value, {vsn, CurVsn}} = lists:keysearch(vsn, 1, Attrs),
+ CurVsn.
+
+%% @spec(Dir::string(), UpdatedBeams::[atom()]) -> {Script,LowLevelScript,Check}
+build_script(Dir, UpdatedBeams) ->
+ Script = make_script(UpdatedBeams),
+ ?INFO_MSG("script: ~p~n", [Script]),
+ LowLevelScript = make_low_level_script(UpdatedBeams, Script),
+ ?INFO_MSG("low level script: ~p~n", [LowLevelScript]),
+ Check =
+ release_handler_1:check_script(
+ LowLevelScript,
+ [{ejabberd, "", filename:join(Dir, "..")}]),
+ ?INFO_MSG("check: ~p~n", [Check]),
+ {Script, LowLevelScript, Check}.
+
+%% Copied from Erlang/OTP file: lib/sasl/src/systools.hrl
-record(application,
{name, %% Name of the application, atom().
type = permanent, %% Application start type, atom().
diff --git a/src/web/ejabberd_web_admin.erl b/src/web/ejabberd_web_admin.erl
index 484c9b393..77b69b933 100644
--- a/src/web/ejabberd_web_admin.erl
+++ b/src/web/ejabberd_web_admin.erl
@@ -39,6 +39,12 @@
-include("ejabberd_http.hrl").
-include("ejabberd_web_admin.hrl").
+-define(INPUTATTRS(Type, Name, Value, Attrs),
+ ?XA("input", Attrs ++
+ [?XMLATTR('type', Type),
+ ?XMLATTR('name', Name),
+ ?XMLATTR('value', Value)])).
+
process(["doc", LocalFile], _Request) ->
DocPath = case os:getenv("EJABBERD_DOC_PATH") of
@@ -149,6 +155,12 @@ make_xhtml(Els, Host, Node, Lang) ->
#xmlel{ns = ?NS_XHTML, name = 'meta', attrs = [
?XMLATTR('http-equiv', <<"Content-Type">>),
?XMLATTR('content', <<"text/html; charset=utf-8">>)]},
+ #xmlel{ns = ?NS_XHTML, name = 'script',
+ %% This children is to ensure exmpp puts:
+ children = [?C(".")],
+ attrs = [
+ ?XMLATTR('src', Base ++ "additions.js"),
+ ?XMLATTR('type', <<"text/javascript">>)]},
#xmlel{ns = ?NS_XHTML, name = 'link', attrs = [
?XMLATTR('href', Base ++ "favicon.ico"),
?XMLATTR('type', <<"image/x-icon">>),
@@ -190,6 +202,24 @@ get_base_path(Host, cluster) -> "/admin/server/" ++ Host ++ "/";
get_base_path(global, Node) -> "/admin/node/" ++ atom_to_list(Node) ++ "/";
get_base_path(Host, Node) -> "/admin/server/" ++ Host ++ "/node/" ++ atom_to_list(Node) ++ "/".
+additions_js() ->
+"
+function selectAll() {
+ for(i=0;i
Base = get_base_path(Host, cluster),
"
@@ -525,6 +555,10 @@ h3 {
padding-left: 10px;
}
+#content ul.noliststyle > li {
+ list-style-type: none;
+}
+
#content li.big {
font-size: 10pt;
}
@@ -646,6 +680,9 @@ process_admin(_Host, #request{path = ["logo.png"]}) ->
process_admin(_Host, #request{path = ["logo-fill.png"]}) ->
{200, [{"Content-Type", "image/png"}, last_modified(), cache_control_public()], logo_fill()};
+process_admin(_Host, #request{path = ["additions.js"]}) ->
+ {200, [{"Content-Type", "text/javascript"}, last_modified(), cache_control_public()], additions_js()};
+
process_admin(Host,
#request{path = ["acls-raw"],
q = Query,
@@ -1953,9 +1990,32 @@ get_node(global, Node, ["update"], Query, Lang) ->
[] ->
?CT("None");
_ ->
- ?XE('ul',
- [?LI([?C(atom_to_list(Beam))]) ||
- Beam <- UpdatedBeams])
+ BeamsLis =
+ lists:map(
+ fun(Beam) ->
+ BeamString = atom_to_list(Beam),
+ ?LI([
+ ?INPUT("checkbox", "selected", BeamString),
+ %% If we want checkboxes selected by default:
+ %%?XA("input", [{"checked", ""},
+ %% {"type", "checkbox"},
+ %% {"name", "selected"},
+ %% {"value", BeamString}]),
+ ?C(BeamString)])
+ end,
+ UpdatedBeams),
+ SelectButtons =
+ [?BR,
+ ?INPUTATTRS(<<"button">>, <<"selectall">>,
+ <<"Select All">>,
+ [?XMLATTR('onClick', <<"selectAll()">>)]),
+ ?C(" "),
+ ?INPUTATTRS(<<"button">>, <<"unselectall">>,
+ <<"Unselect All">>,
+ [?XMLATTR('onClick', <<"unSelectAll()">>)])],
+ %%?XE("ul", BeamsLis)
+ ?XAE('ul', [?XMLATTR('class', <<"noliststyle">>)],
+ BeamsLis ++ SelectButtons)
end,
FmtScript = ?XC('pre', io_lib:format("~p", [Script])),
FmtLowLevelScript = ?XC('pre', io_lib:format("~p", [LowLevelScript])),
@@ -1972,6 +2032,7 @@ get_node(global, Node, ["update"], Query, Lang) ->
?XCT('h3', "Update script"), FmtScript,
?XCT('h3', "Low level update script"), FmtLowLevelScript,
?XCT('h3', "Script check"), ?XC("pre", atom_to_list(Check)),
+ ?BR,
?INPUTT("submit", "update", "Update")
])
];
@@ -2294,7 +2355,9 @@ node_modules_parse_query(Host, Node, Modules, Query) ->
node_update_parse_query(Node, Query) ->
case lists:keysearch("update", 1, Query) of
{value, _} ->
- case rpc:call(Node, ejabberd_update, update, []) of
+ ModulesToUpdateStrings = proplists:get_all_values("selected",Query),
+ ModulesToUpdate = [list_to_atom(M) || M <- ModulesToUpdateStrings],
+ case rpc:call(Node, ejabberd_update, update, [ModulesToUpdate]) of
{ok, _} ->
ok;
{error, Error} ->