mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-13 15:16:49 +02:00
Generate ejabberd.yml.5 man page from source code directly
Several documentation callbacks (doc/0 and mod_doc/0) are implemented and `ejabberdctl man` command is added to generate a man page. Note that the command requires a2x to be installed (which is a part of asciidoc package).
This commit is contained in:
parent
c40d8fe11b
commit
97da380acd
@ -388,7 +388,11 @@ get_commands_spec() ->
|
||||
#ejabberd_commands{name = gc, tags = [server],
|
||||
desc = "Force full garbage collection",
|
||||
module = ?MODULE, function = gc,
|
||||
args = [], result = {res, rescode}}
|
||||
args = [], result = {res, rescode}},
|
||||
#ejabberd_commands{name = man, tags = [documentation],
|
||||
desc = "Generate Unix manpage for current ejabberd version",
|
||||
module = ejabberd_doc, function = man,
|
||||
args = [], result = {res, restuple}}
|
||||
].
|
||||
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
-export([default_db/2, default_db/3, default_ram_db/2, default_ram_db/3]).
|
||||
-export([beams/1, validators/1, globals/0, may_hide_data/1]).
|
||||
-export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]).
|
||||
-export([callback_modules/1]).
|
||||
|
||||
%% Deprecated functions
|
||||
-export([get_option/2, set_option/2]).
|
||||
@ -63,6 +64,7 @@
|
||||
-callback opt_type(atom()) -> econf:validator().
|
||||
-callback options() -> [atom() | {atom(), term()}].
|
||||
-callback globals() -> [atom()].
|
||||
-callback doc() -> any().
|
||||
|
||||
-optional_callbacks([globals/0]).
|
||||
|
||||
|
459
src/ejabberd_doc.erl
Normal file
459
src/ejabberd_doc.erl
Normal file
@ -0,0 +1,459 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : ejabberd_doc.erl
|
||||
%%% Purpose : Options documentation generator
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
-module(ejabberd_doc).
|
||||
|
||||
%% API
|
||||
-export([man/0, man/1, have_a2x/0]).
|
||||
|
||||
-include("translate.hrl").
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
%%%===================================================================
|
||||
man() ->
|
||||
man(<<"en">>).
|
||||
|
||||
man(Lang) when is_list(Lang) ->
|
||||
man(list_to_binary(Lang));
|
||||
man(Lang) ->
|
||||
{ModDoc, SubModDoc} =
|
||||
lists:foldl(
|
||||
fun(M, {Mods, SubMods} = Acc) ->
|
||||
case lists:prefix("mod_", atom_to_list(M)) orelse
|
||||
lists:prefix("Elixir.Mod", atom_to_list(M)) of
|
||||
true ->
|
||||
try M:mod_doc() of
|
||||
#{desc := Descr} = Map ->
|
||||
DocOpts = maps:get(opts, Map, []),
|
||||
Example = maps:get(example, Map, []),
|
||||
{[{M, Descr, DocOpts, #{example => Example}}|Mods], SubMods};
|
||||
#{opts := DocOpts} ->
|
||||
{ParentMod, Backend} = strip_backend_suffix(M),
|
||||
{Mods, dict:append(ParentMod, {M, Backend, DocOpts}, SubMods)}
|
||||
catch _:undef ->
|
||||
case erlang:function_exported(
|
||||
M, mod_options, 1) of
|
||||
true ->
|
||||
warn("module ~s is not documented", [M]);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
Acc
|
||||
end;
|
||||
false ->
|
||||
Acc
|
||||
end
|
||||
end, {[], dict:new()}, ejabberd_config:beams(all)),
|
||||
Doc = lists:flatmap(
|
||||
fun(M) ->
|
||||
try M:doc()
|
||||
catch _:undef -> []
|
||||
end
|
||||
end, ejabberd_config:callback_modules(all)),
|
||||
Options =
|
||||
["TOP LEVEL OPTIONS",
|
||||
"-----------------",
|
||||
tr(Lang, ?T("This section describes top level options of ejabberd.")),
|
||||
io_lib:nl()] ++
|
||||
lists:flatmap(
|
||||
fun(Opt) ->
|
||||
opt_to_man(Lang, Opt, 1)
|
||||
end, lists:keysort(1, Doc)),
|
||||
ModDoc1 = lists:map(
|
||||
fun({M, Descr, DocOpts, Ex}) ->
|
||||
case dict:find(M, SubModDoc) of
|
||||
{ok, Backends} ->
|
||||
{M, Descr, DocOpts, Backends, Ex};
|
||||
error ->
|
||||
{M, Descr, DocOpts, [], Ex}
|
||||
end
|
||||
end, ModDoc),
|
||||
ModOptions =
|
||||
[io_lib:nl(),
|
||||
"MODULES",
|
||||
"-------",
|
||||
"[[modules]]",
|
||||
tr(Lang, ?T("This section describes options of all ejabberd modules.")),
|
||||
io_lib:nl()] ++
|
||||
lists:flatmap(
|
||||
fun({M, Descr, DocOpts, Backends, Example}) ->
|
||||
ModName = atom_to_list(M),
|
||||
[io_lib:nl(),
|
||||
ModName,
|
||||
lists:duplicate(length(atom_to_list(M)), $~),
|
||||
"[[" ++ ModName ++ "]]",
|
||||
io_lib:nl()] ++
|
||||
tr_multi(Lang, Descr) ++ [io_lib:nl()] ++
|
||||
opts_to_man(Lang, [{M, '', DocOpts}|Backends]) ++
|
||||
format_example(0, Lang, Example)
|
||||
end, lists:keysort(1, ModDoc1)),
|
||||
ListenOptions =
|
||||
[io_lib:nl(),
|
||||
"LISTENERS",
|
||||
"-------",
|
||||
"[[listeners]]",
|
||||
tr(Lang, ?T("This section describes options of all ejabberd listeners.")),
|
||||
io_lib:nl(),
|
||||
"TODO"],
|
||||
AsciiData =
|
||||
[[unicode:characters_to_binary(Line), io_lib:nl()]
|
||||
|| Line <- man_header(Lang) ++ Options ++ [io_lib:nl()]
|
||||
++ ModOptions ++ ListenOptions ++ man_footer(Lang)],
|
||||
warn_undocumented_modules(ModDoc1),
|
||||
warn_undocumented_options(Doc),
|
||||
write_man(AsciiData).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
opts_to_man(Lang, [{_, _, []}]) ->
|
||||
Text = tr(Lang, ?T("The module has no options.")),
|
||||
[Text, io_lib:nl()];
|
||||
opts_to_man(Lang, Backends) ->
|
||||
lists:flatmap(
|
||||
fun({_, Backend, DocOpts}) when DocOpts /= [] ->
|
||||
Text = if Backend == '' ->
|
||||
tr(Lang, ?T("Available options"));
|
||||
true ->
|
||||
lists:flatten(
|
||||
io_lib:format(
|
||||
tr(Lang, ?T("Available options for '~s' backend")),
|
||||
[Backend]))
|
||||
end,
|
||||
[Text ++ ":", lists:duplicate(length(Text)+1, $^)|
|
||||
lists:flatmap(
|
||||
fun(Opt) -> opt_to_man(Lang, Opt, 1) end,
|
||||
lists:keysort(1, DocOpts))] ++ [io_lib:nl()];
|
||||
(_) ->
|
||||
[]
|
||||
end, Backends).
|
||||
|
||||
opt_to_man(Lang, {Option, Options}, Level) ->
|
||||
[format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++
|
||||
format_example(Level, Lang, Options);
|
||||
opt_to_man(Lang, {Option, Options, Children}, Level) ->
|
||||
[format_option(Lang, Option, Options)|format_desc(Lang, Options)] ++
|
||||
lists:append(
|
||||
[[H ++ ":"|T]
|
||||
|| [H|T] <- lists:map(
|
||||
fun(Opt) -> opt_to_man(Lang, Opt, Level+1) end,
|
||||
lists:keysort(1, Children))]) ++
|
||||
[io_lib:nl()|format_example(Level, Lang, Options)].
|
||||
|
||||
format_option(Lang, Option, #{value := Val}) ->
|
||||
"*" ++ atom_to_list(Option) ++ "*: 'pass:[" ++
|
||||
tr(Lang, Val) ++ "]'::";
|
||||
format_option(_Lang, Option, #{}) ->
|
||||
"*" ++ atom_to_list(Option) ++ "*::".
|
||||
|
||||
format_desc(Lang, #{desc := Desc}) ->
|
||||
tr_multi(Lang, Desc).
|
||||
|
||||
format_example(Level, Lang, #{example := [_|_] = Example}) ->
|
||||
case lists:all(fun is_list/1, Example) of
|
||||
true ->
|
||||
if Level == 0 ->
|
||||
["*Example*:",
|
||||
"^^^^^^^^^^"];
|
||||
true ->
|
||||
["+", "*Example*:", "+"]
|
||||
end ++ format_yaml(Example);
|
||||
false when Level == 0 ->
|
||||
["Examples:",
|
||||
"^^^^^^^^^"] ++
|
||||
lists:flatmap(
|
||||
fun({Text, Lines}) ->
|
||||
[tr(Lang, Text)] ++ format_yaml(Lines)
|
||||
end, Example);
|
||||
false ->
|
||||
lists:flatmap(
|
||||
fun(Block) ->
|
||||
["+", "''''", "+"|Block]
|
||||
end,
|
||||
lists:map(
|
||||
fun({Text, Lines}) ->
|
||||
[tr(Lang, Text), "+"] ++ format_yaml(Lines)
|
||||
end, Example))
|
||||
end;
|
||||
format_example(_, _, _) ->
|
||||
[].
|
||||
|
||||
format_yaml(Lines) ->
|
||||
["==========================",
|
||||
"[source,yaml]",
|
||||
"----"|Lines] ++
|
||||
["----",
|
||||
"=========================="].
|
||||
|
||||
man_header(Lang) ->
|
||||
["ejabberd.yml(5)",
|
||||
"===============",
|
||||
":doctype: manpage",
|
||||
":version: " ++ binary_to_list(ejabberd_config:version()),
|
||||
io_lib:nl(),
|
||||
"NAME",
|
||||
"----",
|
||||
"ejabberd.yml - " ++ tr(Lang, ?T("main configuration file for ejabberd.")),
|
||||
io_lib:nl(),
|
||||
"SYNOPSIS",
|
||||
"--------",
|
||||
"ejabberd.yml",
|
||||
io_lib:nl(),
|
||||
"DESCRIPTION",
|
||||
"-----------",
|
||||
tr(Lang, ?T("The configuration file is written in "
|
||||
"https://en.wikipedia.org/wiki/YAML[YAML] language.")),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("WARNING: YAML is indentation sensitive, so make sure you respect "
|
||||
"indentation, or otherwise you will get pretty cryptic "
|
||||
"configuration errors.")),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Logically, configuration options are splitted into 3 main categories: "
|
||||
"'Modules', 'Listeners' and everything else called 'Top Level' options. "
|
||||
"Thus this document is splitted into 3 main chapters describing each "
|
||||
"category separately. So, the contents of ejabberd.yml will typically "
|
||||
"look like this:")),
|
||||
io_lib:nl(),
|
||||
"==========================",
|
||||
"[source,yaml]",
|
||||
"----",
|
||||
"hosts:",
|
||||
" - example.com",
|
||||
" - domain.tld",
|
||||
"loglevel: info",
|
||||
"...",
|
||||
"listen:",
|
||||
" -",
|
||||
" port: 5222",
|
||||
" module: ejabberd_c2s",
|
||||
" ...",
|
||||
"modules:",
|
||||
" mod_roster: {}",
|
||||
" ...",
|
||||
"----",
|
||||
"==========================",
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Any configuration error (such as syntax error, unknown option "
|
||||
"or invalid option value) is fatal in the sense that ejabberd will "
|
||||
"refuse to load the whole configuration file and will not start or will "
|
||||
"abort configuration reload.")),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("All options can be changed in runtime by running 'ejabberdctl "
|
||||
"reload-config' command. Configuration reload is atomic: either all options "
|
||||
"are accepted and applied simultaneously or the new configuration is "
|
||||
"refused without any impact on currently running configuration.")),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Some options can be specified for particular virtual host(s) only "
|
||||
"using 'host_config' or 'append_host_config' options. Such options "
|
||||
"are called 'local'. Examples are 'modules', 'auth_method' and 'default_db'. "
|
||||
"The options that cannot be defined per virtual host are called 'global'. "
|
||||
"Examples are 'loglevel', 'certfiles' and 'listen'. It is a configuration "
|
||||
"mistake to put 'global' options under 'host_config' or 'append_host_config' "
|
||||
"section - ejabberd will refuse to load such configuration.")),
|
||||
io_lib:nl(),
|
||||
str:format(
|
||||
tr(Lang, ?T("It is not recommended to write ejabberd.yml from scratch. Instead it is "
|
||||
"better to start from \"default\" configuration file available at ~s. "
|
||||
"Once you get ejabberd running you can start changing configuration "
|
||||
"options to meet your requirements.")),
|
||||
[default_config_url()]),
|
||||
io_lib:nl(),
|
||||
str:format(
|
||||
tr(Lang, ?T("Note that this document is intended to provide comprehensive description of "
|
||||
"all configuration options that can be consulted to understand the meaning "
|
||||
"of a particular option, its format and possible values. It will be quite "
|
||||
"hard to understand how to configure ejabberd by reading this document only "
|
||||
"- for this purpose the reader is recommended to read online Configuration "
|
||||
"Guide available at ~s.")),
|
||||
[configuration_guide_url()]),
|
||||
io_lib:nl()].
|
||||
|
||||
man_footer(Lang) ->
|
||||
{Year, _, _} = date(),
|
||||
[io_lib:nl(),
|
||||
"AUTHOR",
|
||||
"------",
|
||||
"https://www.process-one.net[ProcessOne].",
|
||||
io_lib:nl(),
|
||||
"VERSION",
|
||||
"-------",
|
||||
str:format(
|
||||
tr(Lang, ?T("This document describes the configuration file of ejabberd ~ts. "
|
||||
"Configuration options of other ejabberd versions "
|
||||
"may differ significantly.")),
|
||||
[ejabberd_config:version()]),
|
||||
io_lib:nl(),
|
||||
"REPORTING BUGS",
|
||||
"--------------",
|
||||
tr(Lang, ?T("Report bugs to <https://github.com/processone/ejabberd/issues>")),
|
||||
io_lib:nl(),
|
||||
"SEE ALSO",
|
||||
"---------",
|
||||
tr(Lang, ?T("Default configuration file")) ++ ": " ++ default_config_url(),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Main site")) ++ ": <https://ejabberd.im>",
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Documentation")) ++ ": <https://docs.ejabberd.im>",
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Configuration Guide")) ++ ": " ++ configuration_guide_url(),
|
||||
io_lib:nl(),
|
||||
tr(Lang, ?T("Source code")) ++ ": <https://github.com/processone/ejabberd>",
|
||||
io_lib:nl(),
|
||||
"COPYING",
|
||||
"-------",
|
||||
"Copyright (c) 2002-" ++ integer_to_list(Year) ++
|
||||
" https://www.process-one.net[ProcessOne]."].
|
||||
|
||||
tr(Lang, {Format, Args}) ->
|
||||
unicode:characters_to_list(
|
||||
str:format(
|
||||
translate:translate(Lang, iolist_to_binary(Format)),
|
||||
Args));
|
||||
tr(Lang, Txt) ->
|
||||
unicode:characters_to_list(translate:translate(Lang, iolist_to_binary(Txt))).
|
||||
|
||||
tr_multi(Lang, Txt) when is_binary(Txt) ->
|
||||
tr_multi(Lang, [Txt]);
|
||||
tr_multi(Lang, {Format, Args}) ->
|
||||
tr_multi(Lang, [{Format, Args}]);
|
||||
tr_multi(Lang, Lines) when is_list(Lines) ->
|
||||
[tr(Lang, Txt) || Txt <- Lines].
|
||||
|
||||
write_man(AsciiData) ->
|
||||
case file:get_cwd() of
|
||||
{ok, Cwd} ->
|
||||
AsciiDocFile = filename:join(Cwd, "ejabberd.yml.5.txt"),
|
||||
ManPage = filename:join(Cwd, "ejabberd.yml.5"),
|
||||
case file:write_file(AsciiDocFile, AsciiData) of
|
||||
ok ->
|
||||
Ret = run_a2x(Cwd, AsciiDocFile),
|
||||
%%file:delete(AsciiDocFile),
|
||||
case Ret of
|
||||
ok ->
|
||||
{ok, lists:flatten(
|
||||
io_lib:format(
|
||||
"The manpage saved as ~ts", [ManPage]))};
|
||||
{error, Error} ->
|
||||
{error, lists:flatten(
|
||||
io_lib:format(
|
||||
"Failed to generate manpage: ~ts", [Error]))}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
{error, lists:flatten(
|
||||
io_lib:format(
|
||||
"Failed to write to ~ts: ~s",
|
||||
[AsciiDocFile, file:format_error(Reason)]))}
|
||||
end;
|
||||
{error, Reason} ->
|
||||
{error, lists:flatten(
|
||||
io_lib:format("Failed to get current directory: ~s",
|
||||
[file:format_error(Reason)]))}
|
||||
end.
|
||||
|
||||
have_a2x() ->
|
||||
case os:find_executable("a2x") of
|
||||
false -> false;
|
||||
Path -> {true, Path}
|
||||
end.
|
||||
|
||||
run_a2x(Cwd, AsciiDocFile) ->
|
||||
case have_a2x() of
|
||||
false ->
|
||||
{error, "a2x was not found: do you have 'asciidoc' installed?"};
|
||||
{true, Path} ->
|
||||
Cmd = lists:flatten(
|
||||
io_lib:format("~ts -f manpage ~ts -D ~ts",
|
||||
[Path, AsciiDocFile, Cwd])),
|
||||
case os:cmd(Cmd) of
|
||||
"" -> ok;
|
||||
Ret -> {error, Ret}
|
||||
end
|
||||
end.
|
||||
|
||||
warn_undocumented_modules(Docs) ->
|
||||
lists:foreach(
|
||||
fun({M, _, DocOpts, Backends, _}) ->
|
||||
warn_undocumented_module(M, DocOpts),
|
||||
lists:foreach(
|
||||
fun({SubM, _, SubOpts}) ->
|
||||
warn_undocumented_module(SubM, SubOpts)
|
||||
end, Backends)
|
||||
end, Docs).
|
||||
|
||||
warn_undocumented_module(M, DocOpts) ->
|
||||
try M:mod_options(ejabberd_config:get_myname()) of
|
||||
Defaults ->
|
||||
lists:foreach(
|
||||
fun(OptDefault) ->
|
||||
Opt = case OptDefault of
|
||||
O when is_atom(O) -> O;
|
||||
{O, _} -> O
|
||||
end,
|
||||
case lists:keymember(Opt, 1, DocOpts) of
|
||||
false ->
|
||||
warn("~s: option ~s is not documented",
|
||||
[M, Opt]);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Defaults)
|
||||
catch _:undef ->
|
||||
ok
|
||||
end.
|
||||
|
||||
warn_undocumented_options(Docs) ->
|
||||
Opts = lists:flatmap(
|
||||
fun(M) ->
|
||||
try M:options() of
|
||||
Defaults ->
|
||||
lists:map(
|
||||
fun({O, _}) -> O;
|
||||
(O) when is_atom(O) -> O
|
||||
end, Defaults)
|
||||
catch _:undef ->
|
||||
[]
|
||||
end
|
||||
end, ejabberd_config:callback_modules(all)),
|
||||
lists:foreach(
|
||||
fun(Opt) ->
|
||||
case lists:keymember(Opt, 1, Docs) of
|
||||
false ->
|
||||
warn("option ~s is not documented", [Opt]);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, Opts).
|
||||
|
||||
warn(Format, Args) ->
|
||||
io:format(standard_error, "Warning: " ++ Format ++ "~n", Args).
|
||||
|
||||
strip_backend_suffix(M) ->
|
||||
[H|T] = lists:reverse(string:tokens(atom_to_list(M), "_")),
|
||||
{list_to_atom(string:join(lists:reverse(T), "_")), list_to_atom(H)}.
|
||||
|
||||
default_config_url() ->
|
||||
"<https://github.com/processone/ejabberd/blob/" ++
|
||||
binary_to_list(binary:part(ejabberd_config:version(), {0,5})) ++
|
||||
"/ejabberd.yml.example>".
|
||||
|
||||
configuration_guide_url() ->
|
||||
"<https://docs.ejabberd.im/admin/configuration>".
|
@ -19,7 +19,7 @@
|
||||
-module(ejabberd_options).
|
||||
-behaviour(ejabberd_config).
|
||||
|
||||
-export([opt_type/1, options/0, globals/0]).
|
||||
-export([opt_type/1, options/0, globals/0, doc/0]).
|
||||
|
||||
-ifdef(NEW_SQL_SCHEMA).
|
||||
-define(USE_NEW_SQL_SCHEMA_DEFAULT, true).
|
||||
@ -730,6 +730,9 @@ globals() ->
|
||||
websocket_ping_interval,
|
||||
websocket_timeout].
|
||||
|
||||
doc() ->
|
||||
ejabberd_options_doc:doc().
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal functions
|
||||
%%%===================================================================
|
||||
|
1256
src/ejabberd_options_doc.erl
Normal file
1256
src/ejabberd_options_doc.erl
Normal file
File diff suppressed because it is too large
Load Diff
@ -53,12 +53,18 @@
|
||||
|
||||
-type opts() :: #{atom() => term()}.
|
||||
-type db_type() :: atom().
|
||||
-type opt_desc() :: #{desc => binary() | [binary()],
|
||||
value => string() | binary()}.
|
||||
-type opt_doc() :: {atom(), opt_desc()} | {atom(), opt_desc(), [opt_doc()]}.
|
||||
|
||||
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
-callback stop(binary()) -> any().
|
||||
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()} | {error, term()}.
|
||||
-callback mod_opt_type(atom()) -> econf:validator().
|
||||
-callback mod_options(binary()) -> [{atom(), term()} | atom()].
|
||||
-callback mod_doc() -> #{desc => binary() | [binary()],
|
||||
opts => [opt_doc()],
|
||||
example => [string()] | [{binary(), [string()]}]}.
|
||||
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
|
||||
|
||||
-optional_callbacks([mod_opt_type/1, reload/3]).
|
||||
|
@ -36,7 +36,7 @@
|
||||
get_local_identity/5, get_local_features/5,
|
||||
get_sm_commands/5, get_sm_identity/5, get_sm_features/5,
|
||||
ping_item/4, ping_command/4, mod_opt_type/1, depends/2,
|
||||
mod_options/1]).
|
||||
mod_options/1, mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@ -278,3 +278,15 @@ mod_opt_type(report_commands_node) ->
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{report_commands_node, false}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module implements https://xmpp.org/extensions/xep-0050.html"
|
||||
"[XEP-0050: Ad-Hoc Commands]. It's an auxiliary module and is "
|
||||
"only needed by some of the other modules."),
|
||||
opts =>
|
||||
[{report_commands_node,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Provide the Commands item in the Service Disvocery. "
|
||||
"Default value: 'false'.")}}]}.
|
||||
|
@ -29,9 +29,10 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-export([start/2, stop/1, reload/3, mod_options/1,
|
||||
get_commands_spec/0, depends/2]).
|
||||
get_commands_spec/0, depends/2, mod_doc/0]).
|
||||
|
||||
% Commands API
|
||||
-export([
|
||||
@ -1589,3 +1590,59 @@ num_prio(_) ->
|
||||
-1.
|
||||
|
||||
mod_options(_) -> [].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("This module provides additional administrative commands."), "",
|
||||
?T("Details for some commands:"), "",
|
||||
?T("- 'ban-acount':"),
|
||||
?T("This command kicks all the connected sessions of the
|
||||
account from the server. It also changes their password to
|
||||
a randomly generated one, so they can't login anymore
|
||||
unless a server administrator changes their password
|
||||
again. It is possible to define the reason of the ban. The
|
||||
new password also includes the reason and the date and time
|
||||
of the ban. For example, if this command is called:
|
||||
'ejabberdctl vhost example.org ban-account boby \"Spammed
|
||||
rooms\"', then the sessions of the local account which JID
|
||||
is boby@example.org will be kicked, and its password will
|
||||
be set to something like this:
|
||||
'BANNED_ACCOUNT--20080425T21:45:07--2176635--Spammed_rooms'"),
|
||||
?T("- 'pushroster' (and 'pushroster-all'):"),
|
||||
?T("The roster file must be placed, if using Windows, on
|
||||
the directory where you installed ejabberd: C:/Program
|
||||
Files/ejabberd or similar. If you use other Operating
|
||||
System, place the file on the same directory where the
|
||||
.beam files are installed. See below an example roster
|
||||
file."),
|
||||
?T("- 'srg-create':"),
|
||||
?T("If you want to put a group Name with blankspaces, use
|
||||
the characters \"\' and \\'\" to define when the Name
|
||||
starts and ends. For example: 'ejabberdctl srg-create g1
|
||||
example.org \"\'Group number 1\\'\" this_is_g1 g1'")],
|
||||
opts =>
|
||||
[{module_resource,
|
||||
#{value => ?T("Resource"),
|
||||
desc =>
|
||||
?T("Indicate the resource that the XMPP stanzas must
|
||||
use in the FROM or TO JIDs. This is only useful in
|
||||
the 'get_vcard*' and 'set_vcard*' commands. The
|
||||
default value is 'mod_admin_extra'.")}}],
|
||||
example =>
|
||||
[{?T("With this configuration, vCards can only be modified
|
||||
with mod_admin_extra commands:"),
|
||||
["acl:",
|
||||
" adminextraresource:",
|
||||
" - resource: \"modadminextraf8x,31ad\"",
|
||||
"access_rules:",
|
||||
" vcard_set:",
|
||||
" - allow: adminextraresource",
|
||||
"modules:",
|
||||
" mod_admin_extra:",
|
||||
" module_resource: \"modadminextraf8x,31ad\"",
|
||||
" mod_vcard:",
|
||||
" access_set: vcard_set"]},
|
||||
{?T("Content of roster file for 'pushroster' command:"),
|
||||
["[{<<\"bob\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Bob\">>},",
|
||||
"{<<\"mart\">>, <<\"example.org\">>, <<\"workers\">>, <<\"Mart\">>},",
|
||||
"{<<\"Rich\">>, <<\"example.org\">>, <<\"bosses\">>, <<\"Rich\">>}]."]}]}.
|
||||
|
@ -29,7 +29,7 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([start/2, stop/1, reload/3, mod_options/1,
|
||||
get_commands_spec/0, depends/2]).
|
||||
get_commands_spec/0, depends/2, mod_doc/0]).
|
||||
|
||||
% Commands API
|
||||
-export([update_sql/0]).
|
||||
@ -39,6 +39,7 @@
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
%%%
|
||||
%%% gen_mod
|
||||
@ -358,3 +359,9 @@ sql_query(Host, Query) ->
|
||||
end.
|
||||
|
||||
mod_options(_) -> [].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module can be used to update existing SQL database "
|
||||
"from 'old' to 'new' schema. When the module is loaded "
|
||||
"use 'update_sql' ejabberdctl command.")}.
|
||||
|
@ -35,7 +35,7 @@
|
||||
-export([start/2, stop/1, reload/3, export/1, import_info/0,
|
||||
import_start/2, import/5, announce/1, send_motd/1, disco_identity/5,
|
||||
disco_features/5, disco_items/5, depends/2,
|
||||
send_announcement_to_all/3, announce_commands/4,
|
||||
send_announcement_to_all/3, announce_commands/4, mod_doc/0,
|
||||
announce_items/4, mod_opt_type/1, mod_options/1, clean_cache/1]).
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3]).
|
||||
@ -917,3 +917,64 @@ mod_options(Host) ->
|
||||
{cache_size, ejabberd_option:cache_size(Host)},
|
||||
{cache_missed, ejabberd_option:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("This module enables configured users to broadcast "
|
||||
"announcements and to set the message of the day (MOTD). "
|
||||
"Configured users can perform these actions with an XMPP "
|
||||
"client either using Ad-hoc Commands or sending messages "
|
||||
"to specific JIDs."), "",
|
||||
?T("The Ad-hoc Commands are listed in the Server Discovery. "
|
||||
"For this feature to work, 'mod_adhoc' must be enabled."), "",
|
||||
?T("The specific JIDs where messages can be sent are listed below. "
|
||||
"The first JID in each entry will apply only to the specified "
|
||||
"virtual host example.org, while the JID between brackets "
|
||||
"will apply to all virtual hosts in ejabberd:"), "",
|
||||
"example.org/announce/all (example.org/announce/all-hosts/all)::",
|
||||
?T("The message is sent to all registered users. If the user is "
|
||||
"online and connected to several resources, only the resource "
|
||||
"with the highest priority will receive the message. "
|
||||
"If the registered user is not connected, the message will be "
|
||||
"stored offline in assumption that offline storage (see 'mod_offline') "
|
||||
"is enabled."),
|
||||
"example.org/announce/online (example.org/announce/all-hosts/online)::",
|
||||
?T("The message is sent to all connected users. If the user is "
|
||||
"online and connected to several resources, all resources will "
|
||||
"receive the message."),
|
||||
"example.org/announce/motd (example.org/announce/all-hosts/motd)::",
|
||||
?T("The message is set as the message of the day (MOTD) and is sent "
|
||||
"to users when they login. In addition the message is sent to all "
|
||||
"connected users (similar to announce/online)."),
|
||||
"example.org/announce/motd/update (example.org/announce/all-hosts/motd/update)::",
|
||||
?T("The message is set as message of the day (MOTD) and is sent to users "
|
||||
"when they login. The message is not sent to any currently connected user."),
|
||||
"example.org/announce/motd/delete (example.org/announce/all-hosts/motd/delete)::",
|
||||
?T("Any message sent to this JID removes the existing message of the day (MOTD).")],
|
||||
opts =>
|
||||
[{access,
|
||||
#{value => ?T("AccessName"),
|
||||
desc =>
|
||||
?T("This option specifies who is allowed to send announcements "
|
||||
"and to set the message of the day. The default value is 'none' "
|
||||
"(i.e. nobody is able to send such messages).")}},
|
||||
{db_type,
|
||||
#{value => "mnesia | sql",
|
||||
desc =>
|
||||
?T("Same as top-level 'default_db' option, but applied to this module only.")}},
|
||||
{use_cache,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
|
||||
{cache_size,
|
||||
#{value => "pos_integer() | infinity",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
|
||||
{cache_missed,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
|
||||
{cache_life_time,
|
||||
#{value => "timeout()",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
%% gen_mod API
|
||||
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
|
||||
-export([mod_doc/0]).
|
||||
%% Hooks
|
||||
-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1,
|
||||
get_sm_features/5]).
|
||||
@ -33,6 +34,7 @@
|
||||
-include("xmpp.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("pubsub.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-type avatar_id_meta() :: #{avatar_meta => {binary(), avatar_meta()}}.
|
||||
-opaque convert_rule() :: {default | eimp:img_type(), eimp:img_type()}.
|
||||
@ -458,3 +460,36 @@ mod_opt_type(rate_limit) ->
|
||||
mod_options(_) ->
|
||||
[{rate_limit, 10},
|
||||
{convert, []}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("The purpose of the module is to cope with legacy and modern "
|
||||
"XMPP clients posting avatars. The process is described in "
|
||||
"https://xmpp.org/extensions/xep-0398.html"
|
||||
"[XEP-0398: User Avatar to vCard-Based Avatars Conversion]."), "",
|
||||
?T("Also, the module supports conversion between avatar "
|
||||
"image formats on the fly."), "",
|
||||
?T("The module depends on 'mod_vcard', 'mod_vcard_xupdate' and "
|
||||
"'mod_pubsub'.")],
|
||||
opts =>
|
||||
[{convert,
|
||||
#{value => "{From: To}",
|
||||
desc =>
|
||||
?T("Defines image convertion rules: the format in 'From' "
|
||||
"will be converted to format in 'To'. The value of 'From' "
|
||||
"can also be 'default', which is match-all rule. NOTE: "
|
||||
"the list of supported formats is detected at compile time "
|
||||
"depending on the image libraries installed in the system."),
|
||||
example =>
|
||||
[{?T("In this example avatars in WebP format are "
|
||||
"converted to JPEG, all other formats are "
|
||||
"converted to PNG:"),
|
||||
["convert:",
|
||||
" webp: jpg",
|
||||
" default: png"]}]}},
|
||||
{rate_limit,
|
||||
#{value => ?T("Number"),
|
||||
desc =>
|
||||
?T("Limit any given JID by the number of avatars it is able "
|
||||
"to convert per minute. This is to protect the server from "
|
||||
"image convertion DoS. The default value is '10'.")}}]}.
|
||||
|
@ -29,7 +29,7 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
%% API
|
||||
-export([start/2, stop/1, reload/3,
|
||||
-export([start/2, stop/1, reload/3, mod_doc/0,
|
||||
depends/2, mod_opt_type/1, mod_options/1]).
|
||||
|
||||
-export([filter_packet/1, filter_offline_msg/1, filter_subscription/2]).
|
||||
@ -260,3 +260,54 @@ mod_options(_) ->
|
||||
{captcha, false},
|
||||
{allow_local_users, true},
|
||||
{allow_transports, true}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module allows to block/log messages coming from an "
|
||||
"unknown entity. If a writing entity is not in your roster, "
|
||||
"you can let this module drop and/or log the message. "
|
||||
"By default you'll just not receive message from that entity. "
|
||||
"Enable this module if you want to drop SPAM messages."),
|
||||
opts =>
|
||||
[{access,
|
||||
#{value => ?T("AccessName"),
|
||||
desc =>
|
||||
?T("The option is supposed to be used when 'allow_local_users' "
|
||||
"and 'allow_transports' are not enough. It's an ACL where "
|
||||
"'deny' means the message will be rejected (or a CAPTCHA "
|
||||
"would be generated for a presence, if configured), and "
|
||||
"'allow' means the sender is whitelisted and the stanza "
|
||||
"will pass through. The default value is 'none', which "
|
||||
"means nothing is whitelisted.")}},
|
||||
{drop,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("This option specifies if strangers messages should "
|
||||
"be dropped or not. The default value is 'true'.")}},
|
||||
{log,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("This option specifies if strangers' messages should "
|
||||
"be logged (as info message) in ejabberd.log. "
|
||||
"The default value is 'false'.")}},
|
||||
{allow_local_users,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("This option specifies if strangers from the same "
|
||||
"local host should be accepted or not. "
|
||||
"The default value is 'true'.")}},
|
||||
{allow_transports,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("If set to 'true' and some server's JID is in user's "
|
||||
"roster, then messages from any user of this server "
|
||||
"are accepted even if no subscription present. "
|
||||
"The default value is 'true'.")}},
|
||||
{captcha,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Whether to generate CAPTCHA or not in response to "
|
||||
"messages from strangers. See also section "
|
||||
"https://docs.ejabberd.im/admin/configuration/#captcha"
|
||||
"[CAPTCHA] of the Configuration Guide. "
|
||||
"The default value is 'false'.")}}]}.
|
||||
|
@ -30,7 +30,7 @@
|
||||
-protocol({xep, 191, '1.2'}).
|
||||
|
||||
-export([start/2, stop/1, reload/3, process_iq/1, depends/2,
|
||||
disco_features/5, mod_options/1]).
|
||||
disco_features/5, mod_options/1, mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@ -265,3 +265,11 @@ err_db_failure(#iq{lang = Lang} = IQ) ->
|
||||
|
||||
mod_options(_Host) ->
|
||||
[].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("The module implements "
|
||||
"https://xmpp.org/extensions/xep-0191.html"
|
||||
"[XEP-0191: Blocking Command]."), "",
|
||||
?T("This module depends on 'mod_privacy' where "
|
||||
"all the configuration is performed.")]}.
|
||||
|
@ -36,13 +36,14 @@
|
||||
-export([start/2, stop/1, reload/3, process/2, open_session/2,
|
||||
close_session/1, find_session/1, clean_cache/1]).
|
||||
|
||||
-export([depends/2, mod_opt_type/1, mod_options/1]).
|
||||
-export([depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include("bosh.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-callback init() -> any().
|
||||
-callback open_session(binary(), pid()) -> ok | {error, any()}.
|
||||
@ -197,6 +198,51 @@ mod_options(Host) ->
|
||||
{cache_missed, ejabberd_option:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module implements XMPP over BOSH as defined in "
|
||||
"https://xmpp.org/extensions/xep-0124.html[XEP-0124] and "
|
||||
"https://xmpp.org/extensions/xep-0206.html[XEP-0206]. BOSH "
|
||||
"stands for Bidirectional-streams Over Synchronous HTTP. "
|
||||
"It makes it possible to simulate long lived connections "
|
||||
"required by XMPP over the HTTP protocol. In practice, "
|
||||
"this module makes it possible to use XMPP in a browser without "
|
||||
"Websocket support and more generally to have a way to use "
|
||||
"XMPP while having to get through an HTTP proxy."),
|
||||
opts =>
|
||||
[{json,
|
||||
#{value => "true | false",
|
||||
desc => ?T("This option has no effect.")}},
|
||||
{max_inactivity,
|
||||
#{value => "timeout()",
|
||||
desc =>
|
||||
?T("The option defines the maximum inactivity period. "
|
||||
"The default value is '30' seconds.")}},
|
||||
{queue_type,
|
||||
#{value => "ram | file",
|
||||
desc =>
|
||||
?T("Same as top-level 'queue_type' option, but applied to this module only.")}},
|
||||
{ram_db_type,
|
||||
#{value => "mnesia | sql | redis",
|
||||
desc =>
|
||||
?T("Same as 'default_ram_db' but applied to this module only.")}},
|
||||
{use_cache,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
|
||||
{cache_size,
|
||||
#{value => "pos_integer() | infinity",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
|
||||
{cache_missed,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
|
||||
{cache_life_time,
|
||||
#{value => "timeout()",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Cache stuff
|
||||
%%%----------------------------------------------------------------------
|
||||
|
@ -49,12 +49,13 @@
|
||||
handle_cast/2, terminate/2, code_change/3]).
|
||||
|
||||
-export([user_send_packet/1, user_receive_packet/1,
|
||||
c2s_presence_in/2, mod_opt_type/1, mod_options/1]).
|
||||
c2s_presence_in/2, mod_opt_type/1, mod_options/1, mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("xmpp.hrl").
|
||||
-include("mod_caps.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(BAD_HASH_LIFETIME, 600).
|
||||
|
||||
@ -563,3 +564,32 @@ mod_options(Host) ->
|
||||
{cache_size, ejabberd_option:cache_size(Host)},
|
||||
{cache_missed, ejabberd_option:cache_missed(Host)},
|
||||
{cache_life_time, ejabberd_option:cache_life_time(Host)}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("This module implements "
|
||||
"https://xmpp.org/extensions/xep-0115.html"
|
||||
"[XEP-0115: Entity Capabilities]."),
|
||||
?T("The main purpose of the module is to provide "
|
||||
"PEP functionality (see 'mod_pubsub').")],
|
||||
opts =>
|
||||
[{db_type,
|
||||
#{value => "mnesia | sql",
|
||||
desc =>
|
||||
?T("Same as top-level 'default_db' option, but applied to this module only.")}},
|
||||
{use_cache,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'use_cache' option, but applied to this module only.")}},
|
||||
{cache_size,
|
||||
#{value => "pos_integer() | infinity",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_size' option, but applied to this module only.")}},
|
||||
{cache_missed,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_missed' option, but applied to this module only.")}},
|
||||
{cache_life_time,
|
||||
#{value => "timeout()",
|
||||
desc =>
|
||||
?T("Same as top-level 'cache_life_time' option, but applied to this module only.")}}]}.
|
||||
|
@ -37,7 +37,7 @@
|
||||
-export([user_send_packet/1, user_receive_packet/1,
|
||||
iq_handler/1, disco_features/5,
|
||||
is_carbon_copy/1, depends/2,
|
||||
mod_options/1]).
|
||||
mod_options/1, mod_doc/0]).
|
||||
-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]).
|
||||
%% For debugging purposes
|
||||
-export([list/2]).
|
||||
@ -301,3 +301,10 @@ depends(_Host, _Opts) ->
|
||||
|
||||
mod_options(_) ->
|
||||
[].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("The module implements https://xmpp.org/extensions/xep-0280.html"
|
||||
"[XEP-0280: Message Carbons]. "
|
||||
"The module broadcasts messages on all connected "
|
||||
"user resources (devices).")}.
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
%% gen_mod callbacks.
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
|
||||
-export([mod_doc/0]).
|
||||
|
||||
%% ejabberd_hooks callbacks.
|
||||
-export([filter_presence/1, filter_chat_states/1,
|
||||
@ -42,6 +43,7 @@
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-define(CSI_QUEUE_MAX, 100).
|
||||
|
||||
@ -151,6 +153,38 @@ mod_options(_) ->
|
||||
{queue_chat_states, true},
|
||||
{queue_pep, true}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
[?T("This module allows for queueing certain types of stanzas "
|
||||
"when a client indicates that the user is not actively using "
|
||||
"the client right now (see https://xmpp.org/extensions/xep-0352.html"
|
||||
"[XEP-0352: Client State Indication]). This can save bandwidth and "
|
||||
"resources."), "",
|
||||
?T("A stanza is dropped from the queue if it's effectively obsoleted "
|
||||
"by a new one (e.g., a new presence stanza would replace an old "
|
||||
"one from the same client). The queue is flushed if a stanza arrives "
|
||||
"that won't be queued, or if the queue size reaches a certain limit "
|
||||
"(currently 100 stanzas), or if the client becomes active again.")],
|
||||
opts =>
|
||||
[{queue_presence,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("While a client is inactive, queue presence stanzas "
|
||||
"that indicate (un)availability. The default value is 'true'.")}},
|
||||
{queue_chat_states,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Queue \"standalone\" chat state notifications (as defined in "
|
||||
"https://xmpp.org/extensions/xep-0085.html"
|
||||
"[XEP-0085: Chat State Notifications]) while a client "
|
||||
"indicates inactivity. The default value is 'true'.")}},
|
||||
{queue_pep,
|
||||
#{value => "true | false",
|
||||
desc =>
|
||||
?T("Queue PEP notifications while a client is inactive. "
|
||||
"When the queue is flushed, only the most recent notification "
|
||||
"of a given PEP node is delivered. The default value is 'true'.")}}]}.
|
||||
|
||||
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
|
||||
depends(_Host, _Opts) ->
|
||||
[].
|
||||
|
@ -36,7 +36,7 @@
|
||||
adhoc_local_items/4, adhoc_local_commands/4,
|
||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||
adhoc_sm_items/4, adhoc_sm_commands/4, mod_options/1,
|
||||
depends/2]).
|
||||
depends/2, mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("xmpp.hrl").
|
||||
@ -1558,3 +1558,10 @@ tr(Lang, Text) ->
|
||||
translate:translate(Lang, Text).
|
||||
|
||||
mod_options(_) -> [].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("The module provides server configuration functionality via "
|
||||
"https://xmpp.org/extensions/xep-0050.html"
|
||||
"[XEP-0050: Ad-Hoc Commands]. This module requires "
|
||||
"'mod_adhoc' to be loaded.")}.
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
%% API
|
||||
-export([start/2, stop/1, reload/3, mod_opt_type/1, depends/2, mod_options/1]).
|
||||
-export([mod_doc/0]).
|
||||
%% gen_server callbacks
|
||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||
terminate/2, code_change/3]).
|
||||
@ -82,6 +83,51 @@ mod_opt_type(namespaces) ->
|
||||
mod_options(_Host) ->
|
||||
[{namespaces, []}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module is an implementation of "
|
||||
"https://xmpp.org/extensions/xep-0355.html"
|
||||
"[XEP-0355: Namespace Delegation]. "
|
||||
"Only admin mode has been implemented by now. "
|
||||
"Namespace delegation allows external services to "
|
||||
"handle IQ using specific namespace. This may be applied "
|
||||
"for external PEP service."),
|
||||
opts =>
|
||||
[{namespaces,
|
||||
#{value => "{Namespace: Options}",
|
||||
desc =>
|
||||
?T("If you want to delegate namespaces to a component, "
|
||||
"specify them in this option, and associate them "
|
||||
"to an access rule. The 'Options' are:")},
|
||||
[{filtering,
|
||||
#{value => ?T("Attributes"),
|
||||
desc =>
|
||||
?T("The list of attributes. Currently not used.")}},
|
||||
{access,
|
||||
#{value => ?T("AccessName"),
|
||||
desc =>
|
||||
?T("The option defines which components are allowed "
|
||||
"for namespace delegation. The default value is 'none'.")}}]}],
|
||||
example =>
|
||||
["access_rules:",
|
||||
" external_pubsub:",
|
||||
" allow: external_component",
|
||||
" external_mam:",
|
||||
" allow: external_component",
|
||||
"",
|
||||
"acl:",
|
||||
" external_component:",
|
||||
" server: sat-pubsub.example.org",
|
||||
"",
|
||||
"modules:",
|
||||
" ...",
|
||||
" mod_delegation:",
|
||||
" namespaces:",
|
||||
" urn:xmpp:mam:1:",
|
||||
" access: external_mam",
|
||||
" http://jabber.org/protocol/pubsub:",
|
||||
" access: external_pubsub"]}.
|
||||
|
||||
depends(_, _) ->
|
||||
[].
|
||||
|
||||
|
@ -37,7 +37,8 @@
|
||||
get_local_features/5, get_local_services/5,
|
||||
process_sm_iq_items/1, process_sm_iq_info/1,
|
||||
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
|
||||
get_info/5, mod_opt_type/1, mod_options/1, depends/2]).
|
||||
get_info/5, mod_opt_type/1, mod_options/1, depends/2,
|
||||
mod_doc/0]).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
@ -436,3 +437,70 @@ mod_options(_Host) ->
|
||||
[{extra_domains, []},
|
||||
{server_info, []},
|
||||
{name, ?T("ejabberd")}].
|
||||
|
||||
mod_doc() ->
|
||||
#{desc =>
|
||||
?T("This module adds support for "
|
||||
"https://xmpp.org/extensions/xep-0030.html"
|
||||
"[XEP-0030: Service Discovery]. With this module enabled, "
|
||||
"services on your server can be discovered by XMPP clients."),
|
||||
opts =>
|
||||
[{extra_domains,
|
||||
#{value => "[Domain, ...]",
|
||||
desc =>
|
||||
?T("With this option, you can specify a list of extra "
|
||||
"domains that are added to the Service Discovery item list. "
|
||||
"The default value is an empty list.")}},
|
||||
{name,
|
||||
#{value => ?T("Name"),
|
||||
desc =>
|
||||
?T("A name of the server in the Service Discovery. "
|
||||
"This will only be displayed by special XMPP clients. "
|
||||
"The default value is 'ejabberd'.")}},
|
||||
{server_info,
|
||||
#{value => "[Info, ...]",
|
||||
example =>
|
||||
["server_info:",
|
||||
" -",
|
||||
" modules: all",
|
||||
" name: abuse-addresses",
|
||||
" urls: [mailto:abuse@shakespeare.lit]",
|
||||
" -",
|
||||
" modules: [mod_muc]",
|
||||
" name: \"Web chatroom logs\"",
|
||||
" urls: [http://www.example.org/muc-logs]",
|
||||
" -",
|
||||
" modules: [mod_disco]",
|
||||
" name: feedback-addresses",
|
||||
" urls:",
|
||||
" - http://shakespeare.lit/feedback.php",
|
||||
" - mailto:feedback@shakespeare.lit",
|
||||
" - xmpp:feedback@shakespeare.lit",
|
||||
" -",
|
||||
" modules:",
|
||||
" - mod_disco",
|
||||
" - mod_vcard",
|
||||
" name: admin-addresses",
|
||||
" urls:",
|
||||
" - mailto:xmpp@shakespeare.lit",
|
||||
" - xmpp:admins@shakespeare.lit"],
|
||||
desc =>
|
||||
?T("Specify additional information about the server, "
|
||||
"as described in https://xmpp.org/extensions/xep-0157.html"
|
||||
"[XEP-0157: Contact Addresses for XMPP Services]. Every 'Info' "
|
||||
"element in the list is constructed from the following options:")},
|
||||
[{modules,
|
||||
#{value => "all | [Module, ...]",
|
||||
desc =>
|
||||
?T("The value can be the keyword 'all', in which case the "
|
||||
"information is reported in all the services, "
|
||||
"or a list of ejabberd modules, in which case the "
|
||||
"information is only specified for the services provided "
|
||||
"by those modules.")}},
|
||||
{name,
|
||||
#{value => ?T("Name"),
|
||||
desc => ?T("Any arbitrary name of the contact.")}},
|
||||
{urls,
|
||||
#{value => "[URI, ...]",
|
||||
desc => ?T("A list of contact URIs, such as "
|
||||
"HTTP URLs, XMPP URIs and so on.")}}]}]}.
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
mod_opt_type/1, mod_options/1, depends/2]).
|
||||
mod_opt_type/1, mod_options/1, depends/2, mod_doc/0]).
|
||||