mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-30 16:36:29 +01:00
src/web/ejabberd_web_admin.erl, src/web/ejabberd_web_admin.hrl:
Many exmpp related fixes. Fix a bug when displaying users lists, if we can't access the 'offline_msg' mnesia table (using mod_offline_odbc). This fix should be temporal, we should find a better way to manage this situation. src/web/ejabberd_http.erl: Language must be in binary() format. src/translate.erl: Remove a debug call to io:format/2. src/ejabberd_sm.erl, src/mod_configure.erl, src/mod_disco.erl: ejabberd_sm:get_user_resources/2 returns resources as binary(). src/ejabberd_sm.erl: Bugfix in get_user_info/3. SVN Revision: 1886
This commit is contained in:
parent
ea62092d6e
commit
7c10f3422a
15
ChangeLog
15
ChangeLog
@ -7,6 +7,21 @@
|
|||||||
|
|
||||||
* src/ejabberd_c2s.erl: Remove comment for an already done TODO.
|
* src/ejabberd_c2s.erl: Remove comment for an already done TODO.
|
||||||
|
|
||||||
|
* src/web/ejabberd_web_admin.erl, src/web/ejabberd_web_admin.hrl:
|
||||||
|
Many exmpp related fixes. Fix a bug when displaying users lists,
|
||||||
|
if we can't access the 'offline_msg' mnesia table (using mod_offline_odbc).
|
||||||
|
This fix should be temporal, we should find a better way to manage this
|
||||||
|
situation. The webadmin is usable again.
|
||||||
|
|
||||||
|
* src/web/ejabberd_http.erl: Language must be in binary() format.
|
||||||
|
|
||||||
|
* src/translate.erl: Remove a debug call to io:format/2.
|
||||||
|
|
||||||
|
* src/ejabberd_sm.erl, src/mod_configure.erl, src/mod_disco.erl:
|
||||||
|
ejabberd_sm:get_user_resources/2 returns resources as binary().
|
||||||
|
|
||||||
|
* src/ejabberd_sm.erl: Bugfix in get_user_info/3.
|
||||||
|
|
||||||
2009-02-13 Christophe Romain <christophe.romain@process-one.net>
|
2009-02-13 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/ejabberd_auth.erl: prevent from calling
|
* src/ejabberd_auth.erl: prevent from calling
|
||||||
|
@ -154,7 +154,7 @@ get_user_resources(User, Server)
|
|||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
[];
|
[];
|
||||||
Ss ->
|
Ss ->
|
||||||
[binary_to_list(element(3, S#session.usr)) || S <- clean_session_list(Ss)]
|
[element(3, S#session.usr) || S <- clean_session_list(Ss)]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_user_ip(JID) when ?IS_JID(JID) ->
|
get_user_ip(JID) when ?IS_JID(JID) ->
|
||||||
@ -176,9 +176,9 @@ get_user_info(User, Server, Resource)
|
|||||||
LUser = exmpp_stringprep:nodeprep(User),
|
LUser = exmpp_stringprep:nodeprep(User),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
LResource = exmpp_stringprep:resourceprep(Resource),
|
LResource = exmpp_stringprep:resourceprep(Resource),
|
||||||
USR = {list_to_binary(LUser),
|
USR = {LUser,
|
||||||
list_to_binary(LServer),
|
LServer,
|
||||||
list_to_binary(LResource)},
|
LResource},
|
||||||
case mnesia:dirty_index_read(session, USR, #session.usr) of
|
case mnesia:dirty_index_read(session, USR, #session.usr) of
|
||||||
[] ->
|
[] ->
|
||||||
offline;
|
offline;
|
||||||
|
@ -287,7 +287,7 @@ get_sm_items(Acc, From, To, Node, Lang) ->
|
|||||||
{allow, ""} ->
|
{allow, ""} ->
|
||||||
Nodes = [?NODEJID(To, "Configuration", <<"config">>),
|
Nodes = [?NODEJID(To, "Configuration", <<"config">>),
|
||||||
?NODEJID(To, "User Management", <<"user">>)],
|
?NODEJID(To, "User Management", <<"user">>)],
|
||||||
{result, Items ++ Nodes ++ get_user_resources(To)};
|
{result, Items ++ Nodes ++ [binary_to_list(R) || R <- get_user_resources(To)]};
|
||||||
{allow, "config"} ->
|
{allow, "config"} ->
|
||||||
{result, []};
|
{result, []};
|
||||||
{_, "config"} ->
|
{_, "config"} ->
|
||||||
|
@ -324,7 +324,7 @@ get_sm_items(Acc, From, To, <<>>, _Lang) ->
|
|||||||
empty -> []
|
empty -> []
|
||||||
end,
|
end,
|
||||||
Items1 = case {LFrom, LSFrom} of
|
Items1 = case {LFrom, LSFrom} of
|
||||||
{LTo, LSTo} -> get_user_resources(To);
|
{LTo, LSTo} -> [binary_to_list(R) || R <- get_user_resources(To)];
|
||||||
_ -> []
|
_ -> []
|
||||||
end,
|
end,
|
||||||
{result, Items ++ Items1};
|
{result, Items ++ Items1};
|
||||||
|
@ -105,7 +105,6 @@ load_file(Lang, File) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
translate(Lang, Msg) ->
|
translate(Lang, Msg) ->
|
||||||
io:format("translate(~p, ~p) ~n",[Lang, Msg]),
|
|
||||||
LLang = ascii_tolower(Lang),
|
LLang = ascii_tolower(Lang),
|
||||||
case ets:lookup(translations, {LLang, Msg}) of
|
case ets:lookup(translations, {LLang, Msg}) of
|
||||||
[{_, Trans}] ->
|
[{_, Trans}] ->
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
request_auth,
|
request_auth,
|
||||||
request_keepalive,
|
request_keepalive,
|
||||||
request_content_length,
|
request_content_length,
|
||||||
request_lang = "en",
|
request_lang = <<"en">>,
|
||||||
%% XXX bard: request handlers are configured in
|
%% XXX bard: request handlers are configured in
|
||||||
%% ejabberd.cfg under the HTTP service. For example,
|
%% ejabberd.cfg under the HTTP service. For example,
|
||||||
%% to have the module test_web handle requests with
|
%% to have the module test_web handle requests with
|
||||||
@ -548,9 +548,9 @@ make_text_output(State, Status, Headers, Data) when is_binary(Data) ->
|
|||||||
parse_lang(Langs) ->
|
parse_lang(Langs) ->
|
||||||
case string:tokens(Langs, ",; ") of
|
case string:tokens(Langs, ",; ") of
|
||||||
[First | _] ->
|
[First | _] ->
|
||||||
First;
|
list_to_binary(First);
|
||||||
[] ->
|
[] ->
|
||||||
"en"
|
<<"en">>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
% Code below is taken (with some modifications) from the yaws webserver, which
|
% Code below is taken (with some modifications) from the yaws webserver, which
|
||||||
|
@ -975,7 +975,7 @@ process_admin(Host, #request{lang = Lang} = Request) ->
|
|||||||
global -> {webadmin_page_main, [Request]};
|
global -> {webadmin_page_main, [Request]};
|
||||||
Host -> {webadmin_page_host, [Host, Request]}
|
Host -> {webadmin_page_host, [Host, Request]}
|
||||||
end,
|
end,
|
||||||
case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
|
case ejabberd_hooks:run_fold(Hook, list_to_binary(Host), [], Opts) of
|
||||||
[] -> setelement(1, make_xhtml([?XC('h1', "Not Found")], Host, Lang), 404);
|
[] -> setelement(1, make_xhtml([?XC('h1', "Not Found")], Host, Lang), 404);
|
||||||
Res -> make_xhtml(Res, Host, Lang)
|
Res -> make_xhtml(Res, Host, Lang)
|
||||||
end.
|
end.
|
||||||
@ -1291,7 +1291,7 @@ list_vhosts(Lang) ->
|
|||||||
lists:map(
|
lists:map(
|
||||||
fun(Host) ->
|
fun(Host) ->
|
||||||
OnlineUsers =
|
OnlineUsers =
|
||||||
length(ejabberd_sm:get_vh_session_list(Host)),
|
length(ejabberd_sm:get_vh_session_list(list_to_binary(Host))),
|
||||||
RegisteredUsers =
|
RegisteredUsers =
|
||||||
ejabberd_auth:get_vh_registered_users_number(Host),
|
ejabberd_auth:get_vh_registered_users_number(Host),
|
||||||
?XE('tr',
|
?XE('tr',
|
||||||
@ -1400,13 +1400,20 @@ list_given_users(Users, Prefix, Lang, URLFunc) ->
|
|||||||
?XE('tbody',
|
?XE('tbody',
|
||||||
lists:map(
|
lists:map(
|
||||||
fun(_SU = {Server, User}) ->
|
fun(_SU = {Server, User}) ->
|
||||||
US = {User, Server},
|
ServerB = list_to_binary(Server),
|
||||||
|
UserB = list_to_binary(User),
|
||||||
|
US = {UserB, ServerB},
|
||||||
|
FQueueLen = try
|
||||||
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
|
QueueLen = length(mnesia:dirty_read({offline_msg, US})),
|
||||||
FQueueLen = [?AC(URLFunc({users_queue, Prefix,
|
[?AC(URLFunc({users_queue, Prefix,
|
||||||
User, Server}),
|
User, Server}),
|
||||||
integer_to_list(QueueLen))],
|
integer_to_list(QueueLen))]
|
||||||
|
catch
|
||||||
|
_:_ -> [#xmlcdata{cdata = <<"Can't access the offline messages storage.">>}]
|
||||||
|
end,
|
||||||
|
|
||||||
FLast =
|
FLast =
|
||||||
case ejabberd_sm:get_user_resources(User, Server) of
|
case ejabberd_sm:get_user_resources(UserB, ServerB) of
|
||||||
[] ->
|
[] ->
|
||||||
case mnesia:dirty_read({last_activity, US}) of
|
case mnesia:dirty_read({last_activity, US}) of
|
||||||
[] ->
|
[] ->
|
||||||
@ -1464,7 +1471,7 @@ get_stats(global, Lang) ->
|
|||||||
])];
|
])];
|
||||||
|
|
||||||
get_stats(Host, Lang) ->
|
get_stats(Host, Lang) ->
|
||||||
OnlineUsers = length(ejabberd_sm:get_vh_session_list(Host)),
|
OnlineUsers = length(ejabberd_sm:get_vh_session_list(list_to_binary(Host))),
|
||||||
RegisteredUsers = ejabberd_auth:get_vh_registered_users_number(Host),
|
RegisteredUsers = ejabberd_auth:get_vh_registered_users_number(Host),
|
||||||
[?XAE('table', [],
|
[?XAE('table', [],
|
||||||
[?XE('tbody',
|
[?XE('tbody',
|
||||||
@ -1477,20 +1484,22 @@ get_stats(Host, Lang) ->
|
|||||||
|
|
||||||
|
|
||||||
list_online_users(Host, _Lang) ->
|
list_online_users(Host, _Lang) ->
|
||||||
Users = [{S, U} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Host)],
|
Users = [{S, U} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(list_to_binary(Host))],
|
||||||
SUsers = lists:usort(Users),
|
SUsers = lists:usort(Users),
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun({_S, U} = SU) ->
|
fun({_S, U} = SU) ->
|
||||||
[?AC("../user/" ++ ejabberd_http:url_encode(U) ++ "/",
|
[?AC("../user/" ++ ejabberd_http:url_encode(binary_to_list(U)) ++ "/",
|
||||||
su_to_list(SU)),
|
su_to_list(SU)),
|
||||||
?BR]
|
?BR]
|
||||||
end, SUsers).
|
end, SUsers).
|
||||||
|
|
||||||
user_info(User, Server, Query, Lang) ->
|
user_info(User, Server, Query, Lang) ->
|
||||||
|
UserB = list_to_binary(User),
|
||||||
|
ServerB = list_to_binary(Server),
|
||||||
LServer = exmpp_stringprep:nameprep(Server),
|
LServer = exmpp_stringprep:nameprep(Server),
|
||||||
US = {exmpp_stringprep:nodeprep(User), LServer},
|
US = {exmpp_stringprep:nodeprep(User), LServer},
|
||||||
Res = user_parse_query(User, Server, Query),
|
Res = user_parse_query(User, Server, Query),
|
||||||
Resources = ejabberd_sm:get_user_resources(User, Server),
|
Resources = ejabberd_sm:get_user_resources(UserB, ServerB),
|
||||||
FResources =
|
FResources =
|
||||||
case Resources of
|
case Resources of
|
||||||
[] ->
|
[] ->
|
||||||
@ -1499,7 +1508,7 @@ user_info(User, Server, Query, Lang) ->
|
|||||||
[?XE('ul',
|
[?XE('ul',
|
||||||
lists:map(fun(R) ->
|
lists:map(fun(R) ->
|
||||||
FIP = case ejabberd_sm:get_user_info(
|
FIP = case ejabberd_sm:get_user_info(
|
||||||
User, Server, R) of
|
UserB, ServerB, R) of
|
||||||
offline ->
|
offline ->
|
||||||
"";
|
"";
|
||||||
[{node, Node}, {conn, Conn}, {ip, {IP, Port}}] ->
|
[{node, Node}, {conn, Conn}, {ip, {IP, Port}}] ->
|
||||||
@ -1518,13 +1527,13 @@ user_info(User, Server, Query, Lang) ->
|
|||||||
++ "#" ++ atom_to_list(Node)
|
++ "#" ++ atom_to_list(Node)
|
||||||
++ ")"
|
++ ")"
|
||||||
end,
|
end,
|
||||||
?LI([?C(R ++ FIP)])
|
?LI([?C(binary_to_list(R) ++ FIP)])
|
||||||
end, lists:sort(Resources)))]
|
end, lists:sort(Resources)))]
|
||||||
end,
|
end,
|
||||||
Password = ejabberd_auth:get_password_s(User, Server),
|
Password = ejabberd_auth:get_password_s(User, Server),
|
||||||
FPassword = [?INPUT("password", "password", Password), ?C(" "),
|
FPassword = [?INPUT("password", "password", Password), ?C(" "),
|
||||||
?INPUTT("submit", "chpassword", "Change Password")],
|
?INPUTT("submit", "chpassword", "Change Password")],
|
||||||
UserItems = ejabberd_hooks:run_fold(webadmin_user, LServer, [],
|
UserItems = ejabberd_hooks:run_fold(webadmin_user, list_to_binary(LServer), [],
|
||||||
[User, Server, Lang]),
|
[User, Server, Lang]),
|
||||||
[?XC('h1', ?T("User ") ++ us_to_list(US))] ++
|
[?XC('h1', ?T("User ") ++ us_to_list(US))] ++
|
||||||
case Res of
|
case Res of
|
||||||
@ -1562,7 +1571,7 @@ user_parse_query1("removeuser", User, Server, _Query) ->
|
|||||||
ejabberd_auth:remove_user(User, Server),
|
ejabberd_auth:remove_user(User, Server),
|
||||||
ok;
|
ok;
|
||||||
user_parse_query1(Action, User, Server, Query) ->
|
user_parse_query1(Action, User, Server, Query) ->
|
||||||
case ejabberd_hooks:run_fold(webadmin_user_parse_query, Server, [], [Action, User, Server, Query]) of
|
case ejabberd_hooks:run_fold(webadmin_user_parse_query, list_to_binary(Server), [], [Action, User, Server, Query]) of
|
||||||
[] -> nothing;
|
[] -> nothing;
|
||||||
Res -> Res
|
Res -> Res
|
||||||
end.
|
end.
|
||||||
@ -1972,7 +1981,7 @@ get_node(Host, Node, NPath, Query, Lang) ->
|
|||||||
global -> {webadmin_page_node, [Node, NPath, Query, Lang]};
|
global -> {webadmin_page_node, [Node, NPath, Query, Lang]};
|
||||||
Host -> {webadmin_page_hostnode, [Host, Node, NPath, Query, Lang]}
|
Host -> {webadmin_page_hostnode, [Host, Node, NPath, Query, Lang]}
|
||||||
end,
|
end,
|
||||||
case ejabberd_hooks:run_fold(Hook, Host, [], Opts) of
|
case ejabberd_hooks:run_fold(Hook, list_to_binary(Host), [], Opts) of
|
||||||
[] -> [?XC('h1', "Not Found")];
|
[] -> [?XC('h1', "Not Found")];
|
||||||
Res -> Res
|
Res -> Res
|
||||||
end.
|
end.
|
||||||
@ -2135,7 +2144,7 @@ node_ports_to_xhtml(Ports, Lang) ->
|
|||||||
?XE('td', [?INPUTS("text", "ipnew", "0.0.0.0", "15")]),
|
?XE('td', [?INPUTS("text", "ipnew", "0.0.0.0", "15")]),
|
||||||
?XE('td', [?INPUTS("text", "modulenew", "", "15")]),
|
?XE('td', [?INPUTS("text", "modulenew", "", "15")]),
|
||||||
?XE('td', [?TEXTAREA("optsnew", "2", "35", "[]")]),
|
?XE('td', [?TEXTAREA("optsnew", "2", "35", "[]")]),
|
||||||
?XAE('td', [{"colspan", "2"}],
|
?XAE('td', [?XMLATTR("colspan", "2")],
|
||||||
[?INPUTT("submit", "addnew", "Add New")])
|
[?INPUTT("submit", "addnew", "Add New")])
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
@ -2237,7 +2246,7 @@ node_modules_to_xhtml(Modules, Lang) ->
|
|||||||
[?XE('tr',
|
[?XE('tr',
|
||||||
[?XE('td', [?INPUT("text", "modulenew", "")]),
|
[?XE('td', [?INPUT("text", "modulenew", "")]),
|
||||||
?XE('td', [?TEXTAREA("optsnew", "2", "40", "[]")]),
|
?XE('td', [?TEXTAREA("optsnew", "2", "40", "[]")]),
|
||||||
?XAE('td', [{"colspan", "2"}],
|
?XAE('td', [?XMLATTR("colspan", "2")],
|
||||||
[?INPUTT("submit", "start", "Start")])
|
[?INPUTT("submit", "start", "Start")])
|
||||||
]
|
]
|
||||||
)]
|
)]
|
||||||
@ -2399,9 +2408,9 @@ make_server_menu(HostMenu, NodeMenu, Lang) ->
|
|||||||
|
|
||||||
|
|
||||||
get_menu_items_hook({hostnode, Host}, Lang) ->
|
get_menu_items_hook({hostnode, Host}, Lang) ->
|
||||||
ejabberd_hooks:run_fold(webadmin_menu_hostnode, Host, [], [Host, Lang]);
|
ejabberd_hooks:run_fold(webadmin_menu_hostnode, list_to_binary(Host), [], [Host, Lang]);
|
||||||
get_menu_items_hook({host, Host}, Lang) ->
|
get_menu_items_hook({host, Host}, Lang) ->
|
||||||
ejabberd_hooks:run_fold(webadmin_menu_host, Host, [], [Host, Lang]);
|
ejabberd_hooks:run_fold(webadmin_menu_host, list_to_binary(Host), [], [Host, Lang]);
|
||||||
get_menu_items_hook(node, Lang) ->
|
get_menu_items_hook(node, Lang) ->
|
||||||
ejabberd_hooks:run_fold(webadmin_menu_node, [], [Lang]);
|
ejabberd_hooks:run_fold(webadmin_menu_node, [], [Lang]);
|
||||||
get_menu_items_hook(server, Lang) ->
|
get_menu_items_hook(server, Lang) ->
|
||||||
|
@ -34,42 +34,42 @@
|
|||||||
-define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
|
-define(XACT(Name, Attrs, Text), ?XAC(Name, Attrs, ?T(Text))).
|
||||||
|
|
||||||
-define(LI(Els), ?XE('li', Els)).
|
-define(LI(Els), ?XE('li', Els)).
|
||||||
-define(A(URL, Els), ?XAE('a', [#xmlattr{name = 'href', value = URL}], Els)).
|
-define(A(URL, Els), ?XAE('a', [exmpp_xml:attribute('href', URL)], Els)).
|
||||||
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
-define(AC(URL, Text), ?A(URL, [?C(Text)])).
|
||||||
-define(ACT(URL, Text), ?AC(URL, ?T(Text))).
|
-define(ACT(URL, Text), ?AC(URL, ?T(Text))).
|
||||||
-define(P, ?X('p')).
|
-define(P, ?X('p')).
|
||||||
-define(BR, ?X('br')).
|
-define(BR, ?X('br')).
|
||||||
-define(INPUT(Type, Name, Value),
|
-define(INPUT(Type, Name, Value),
|
||||||
?XA('input', [#xmlattr{name = 'type', value = Type},
|
?XA('input', [exmpp_xml:attribute('type', Type),
|
||||||
#xmlattr{name = 'name', value = Name},
|
exmpp_xml:attribute('name', Name),
|
||||||
#xmlattr{name = 'value', value = Value}])).
|
exmpp_xml:attribute('value', Value)])).
|
||||||
|
|
||||||
-define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
|
-define(INPUTT(Type, Name, Value), ?INPUT(Type, Name, ?T(Value))).
|
||||||
-define(INPUTS(Type, Name, Value, Size),
|
-define(INPUTS(Type, Name, Value, Size),
|
||||||
?XA('input', [#xmlattr{name = 'type', value = Type},
|
?XA('input', [exmpp_xml:attribute('type', Type),
|
||||||
#xmlattr{name = 'name', value = Name},
|
exmpp_xml:attribute('name', Name),
|
||||||
#xmlattr{name = 'value', value = Value},
|
exmpp_xml:attribute('value', Value),
|
||||||
#xmlattr{name = 'size', value = Size}])).
|
exmpp_xml:attribute('size', Size)])).
|
||||||
-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
|
-define(INPUTST(Type, Name, Value, Size), ?INPUT(Type, Name, ?T(Value), Size)).
|
||||||
-define(ACLINPUT(Text), ?XE('td', [?INPUT("text", "value" ++ ID, Text)])).
|
-define(ACLINPUT(Text), ?XE('td', [?INPUT("text", "value" ++ ID, Text)])).
|
||||||
|
|
||||||
-define(TEXTAREA(Name, Rows, Cols, Value),
|
-define(TEXTAREA(Name, Rows, Cols, Value),
|
||||||
?XAC('textarea', [#xmlattr{name = 'name', value = list_to_binary(Name)},
|
?XAC('textarea', [exmpp_xml:attribute('name', list_to_binary(Name)),
|
||||||
#xmlattr{name = 'rows', value = list_to_binary(Rows)},
|
exmpp_xml:attribute('rows', list_to_binary(Rows)),
|
||||||
#xmlattr{name = 'cols', value = list_to_binary(Cols)}],
|
exmpp_xml:attribute('cols', list_to_binary(Cols))],
|
||||||
Value)).
|
Value)).
|
||||||
|
|
||||||
%% Build an xmlelement for result
|
%% Build an xmlelement for result
|
||||||
-define(XRES(Text), ?XAC('p', [#xmlattr{name = 'class', value = <<"result">>}], Text)).
|
-define(XRES(Text), ?XAC('p', [exmpp_xml:attribute('class', <<"result">>)], Text)).
|
||||||
-define(XREST(Text), ?XRES(?T(Text))).
|
-define(XREST(Text), ?XRES(?T(Text))).
|
||||||
|
|
||||||
%% Guide Link
|
%% Guide Link
|
||||||
-define(GL(Ref, Title),
|
-define(GL(Ref, Title),
|
||||||
?XAE('div',
|
?XAE('div',
|
||||||
[#xmlattr{name = 'class', value = <<"guidelink">>}],
|
[exmpp_xml:attribute('class', <<"guidelink">>)],
|
||||||
[?XAE('a',
|
[?XAE('a',
|
||||||
[#xmlattr{name = 'href', value = list_to_binary("/admin/doc/guide.html#"++ Ref)},
|
[exmpp_xml:attribute('href', list_to_binary("/admin/doc/guide.html#"++ Ref)),
|
||||||
#xmlattr{name = 'target', value = <<"_blank">>}],
|
exmpp_xml:attribute('target', <<"_blank">>)],
|
||||||
[?C("[Guide: " ++ Title ++ "]")])
|
[?C("[Guide: " ++ Title ++ "]")])
|
||||||
])).
|
])).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user