Add AMI event documentation

This patch adds the core changes necessary to support AMI event documentation
in the source files of Asterisk, and adds documentation to those AMI events
defined in the core application modules.  Event documentation is built from
the source by two new python scripts, located in build_tools:
get_documentation.py and post_process_documentation.py.

The get_documentation.py script mirrors the actions of the existing AWK
get_documentation scripts, except that it will scan the entirety of a source
file for Asterisk documentation.  Upon encountering it, if the documentation
happens to be an AMI event, it will attempt to extract information about the
event directly from the manager event macro calls that raise the event.  The
post_process_documentation.py script combines manager event instances that
are the same event but documented in multiple source files.  It generates
the final core-[lang].xml file.

As this process can take longer to complete than a typical 'make all', it
is only performed if a new make target, 'full', is chosen.

Review: https://reviewboard.asterisk.org/r/1967/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@369346 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Matthew Jordan 2012-06-25 17:59:34 +00:00
parent d0fda07d74
commit 82a7409c15
19 changed files with 2056 additions and 614 deletions

15
CHANGES
View File

@ -12,6 +12,14 @@
--- Functionality changes from Asterisk 10 to Asterisk 11 --------------------
------------------------------------------------------------------------------
Build System
----
* A new make target, 'full', has been added to the Makefile. This performs
the same compilation actions as make all, but will also scan the entirety of
each source file for documentation. This option is needed to generate AMI
event documentation. Note that your system must have Python in order for
this make target to succeed.
Core
----
* The expression parser now recognizes the ABS() absolute value function,
@ -202,6 +210,13 @@ AMI (Asterisk Manager Interface) changes
* Support for IPv6 addresses has been added.
* AMI Events can now be documented in the Asterisk source. Two new CLI
commands have been added to display information about AMI events at run time:
manager show events, which shows a list of all known and documented AMI
events, and manager show event [event name], which shows detail information
about a specific AMI event. Note that AMI event documentation is only
generated when Asterisk is compiled using 'make full'.
FAX changes
-----------
* FAXOPT(faxdetect) will enable a generic fax detect framehook for dialplan

View File

@ -96,6 +96,7 @@ export MD5
export WGET_EXTRA_ARGS
export LDCONFIG
export LDCONFIG_FLAGS
export PYTHON
# even though we could use '-include makeopts' here, use a wildcard
# lookup anyway, so that make won't try to build makeopts if it doesn't
@ -315,13 +316,29 @@ all: _cleantest_all
@echo " + $(mK) install +"
@echo " +-------------------------------------------+"
full: _cleantest_all_full
@echo " +--------- Asterisk Build Complete ---------+"
@echo " + Asterisk has successfully been built, and +"
@echo " + can be installed by running: +"
@echo " + +"
@echo " + $(mK) install +"
@echo " +-------------------------------------------+"
# For parallel builds, we must call cleantest *before* running the
# other dependencies on _all.
_cleantest_all: cleantest
@$(MAKE) _all
# For parallel builds, we must call cleantest *before* running the
# other dependencies on _all.
_cleantest_all_full: cleantest
@$(MAKE) _all_full
_all: makeopts $(SUBDIRS) doc/core-en_US.xml $(ADDL_TARGETS)
_all_full: makeopts $(SUBDIRS) doc/full-en_US.xml $(ADDL_TARGETS)
makeopts: configure
@echo "****"
@echo "**** The configure script must be executed before running '$(MAKE)'."
@ -413,6 +430,8 @@ _clean:
rm -f defaults.h
rm -f include/asterisk/build.h
rm -f main/version.c
rm -f doc/core-en_US.xml
rm -f doc/full-en_US.xml
@$(MAKE) -C menuselect clean
cp -f .cleancount .lastclean
@ -465,6 +484,27 @@ doc/core-en_US.xml: $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"
@echo
@echo "</docs>" >> $@
doc/full-en_US.xml: $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"en_US\"" $(dir)/*.c $(dir)/*.cc 2>/dev/null))
ifeq ($(PYTHON),:)
@echo "--------------------------------------------------------------------------"
@echo "--- Please install python to build full documentation ---"
@echo "--------------------------------------------------------------------------"
else
@printf "Building Documentation For: "
@echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" > $@
@echo "<!DOCTYPE docs SYSTEM \"appdocsxml.dtd\">" >> $@
@echo "<docs xmlns:xi=\"http://www.w3.org/2001/XInclude\">" >> $@
@for x in $(MOD_SUBDIRS); do \
printf "$$x " ; \
for i in $$x/*.c; do \
$(PYTHON) build_tools/get_documentation.py < $$i >> $@ ; \
done ; \
done
@echo
@echo "</docs>" >> $@
@$(PYTHON) build_tools/post_process_documentation.py -i $@ -o "doc/core-en_US.xml"
endif
validate-docs: doc/core-en_US.xml
ifeq ($(XMLSTARLET)$(XMLLINT),::)
@echo "--------------------------------------------------------------------------"

View File

@ -539,6 +539,16 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
ast_channel_unlock(spyee_autochan->chan);
ast_verb(2, "Spying on channel %s\n", name);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel has started spying on another channel.</synopsis>
<see-also>
<ref type="application">ChanSpy</ref>
<ref type="application">ExtenSpy</ref>
<ref type="managerEvent">ChanSpyStop</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans,
"SpyerChannel: %s\r\n"
"SpyeeChannel: %s\r\n",
@ -727,6 +737,14 @@ static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_auto
}
ast_verb(2, "Done Spying on channel %s\n", name);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel has stopped spying on another channel.</synopsis>
<see-also>
<ref type="managerEvent">ChanSpyStart</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
return running;

View File

@ -529,16 +529,53 @@ static int conf_start_record(struct conference_bridge *conference_bridge)
static void send_conf_start_event(const char *conf_name)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a conference starts.</synopsis>
<syntax>
<parameter name="Conference">
<para>The name of the Confbridge conference.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">ConfbridgeEnd</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_CALL, "ConfbridgeStart", "Conference: %s\r\n", conf_name);
}
static void send_conf_end_event(const char *conf_name)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a conference ends.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
</syntax>
<see-also>
<ref type="managerEvent">ConfbridgeStart</ref>
<ref type="application">ConfBridge</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_CALL, "ConfbridgeEnd", "Conference: %s\r\n", conf_name);
}
static void send_join_event(struct ast_channel *chan, const char *conf_name)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel joins a Confbridge conference.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
</syntax>
<see-also>
<ref type="managerEvent">ConfbridgeLeave</ref>
<ref type="application">ConfBridge</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeJoin",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -555,6 +592,17 @@ static void send_join_event(struct ast_channel *chan, const char *conf_name)
static void send_leave_event(struct ast_channel *chan, const char *conf_name)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel leaves a Confbridge conference.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
</syntax>
<see-also>
<ref type="managerEvent">ConfbridgeJoin</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "ConfbridgeLeave",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -1242,6 +1290,20 @@ static void conf_handle_talker_cb(struct ast_bridge *bridge, struct ast_bridge_c
}
/* notify AMI someone is has either started or stopped talking */
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a conference participant has started or stopped talking.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='ConfbridgeStart']/managerEventInstance/syntax/parameter[@name='Conference'])" />
<parameter name="TalkingStatus">
<enumlist>
<enum name="on"/>
<enum name="off"/>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(bridge_channel->chan, EVENT_FLAG_CALL, "ConfbridgeTalking",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"

View File

@ -820,6 +820,20 @@ static const char *get_cid_name(char *name, int namelen, struct ast_channel *cha
static void senddialevent(struct ast_channel *src, struct ast_channel *dst, const char *dialstring)
{
struct ast_channel *chans[] = { src, dst };
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a dial action has started.</synopsis>
<syntax>
<parameter name="SubEvent">
<enumlist>
<enum name="Begin"/>
<enum name="End"/>
</enumlist>
<para>A sub event type, specifying whether the dial action has begun or ended.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event_multichan(EVENT_FLAG_CALL, "Dial", 2, chans,
"SubEvent: Begin\r\n"
"Channel: %s\r\n"
@ -842,6 +856,16 @@ static void senddialevent(struct ast_channel *src, struct ast_channel *dst, cons
static void senddialendevent(struct ast_channel *src, const char *dialstatus)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a dial action has ended.</synopsis>
<syntax>
<parameter name="DialStatus">
<para>The value of the <variable>DIALSTATUS</variable> channel variable.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(src, EVENT_FLAG_CALL, "Dial",
"SubEvent: End\r\n"
"Channel: %s\r\n"

View File

@ -1845,6 +1845,17 @@ static int conf_free(struct ast_conference *conf)
struct announce_listitem *item;
AST_LIST_REMOVE(&confs, conf, list);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe conference ends.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
</syntax>
<see-also>
<ref type="managerEvent">MeetmeJoin</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
if (conf->recording == MEETME_RECORD_ACTIVE) {
@ -2179,13 +2190,30 @@ static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
{
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe user begins or ends talking.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
<parameter name="Status">
<enumlist>
<enum name="on"/>
<enum name="off"/>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
"Meetme: %s\r\n"
"Usernum: %d\r\n"
"Status: %s\r\n",
ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no, talking ? "on" : "off");
ast_channel_name(chan), ast_channel_uniqueid(chan),
conf->confno,
user->user_no, talking ? "on" : "off");
}
static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
@ -2777,6 +2805,23 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
ast_debug(1, "Placed channel %s in DAHDI conf %d\n", ast_channel_name(chan), conf->dahdiconf);
if (!sent_event) {
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a user joins a MeetMe conference.</synopsis>
<syntax>
<parameter name="Meetme">
<para>The identifier for the MeetMe conference.</para>
</parameter>
<parameter name="Usernum">
<para>The identifier of the MeetMe user who joined.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">MeetmeLeave</ref>
<ref type="application">MeetMe</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -3142,7 +3187,21 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
if (ast_test_flag64(confflags, (CONFFLAG_MONITORTALKER | CONFFLAG_OPTIMIZETALKER))) {
set_user_talking(chan, conf, user, -1, ast_test_flag64(confflags, CONFFLAG_MONITORTALKER));
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe user is muted.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
<parameter name="Status">
<enumlist>
<enum name="on"/>
<enum name="off"/>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -3160,7 +3219,11 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
ret = -1;
break;
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe user is unmuted.</synopsis>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeMute",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -3174,6 +3237,21 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
(user->adminflags & ADMINFLAG_T_REQUEST) && !(talkreq_manager)) {
talkreq_manager = 1;
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe user has started talking.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
<parameter name="Status">
<enumlist>
<enum name="on"/>
<enum name="off"/>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -3183,10 +3261,14 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struc
ast_channel_name(chan), ast_channel_uniqueid(chan), conf->confno, user->user_no);
}
if (!(user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED)) &&
!(user->adminflags & ADMINFLAG_T_REQUEST) && (talkreq_manager)) {
talkreq_manager = 0;
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a MeetMe user has finished talking.</synopsis>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalkRequest",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"
@ -3850,6 +3932,21 @@ bailoutandtrynormal:
now = ast_tvnow();
if (sent_event) {
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a user leaves a MeetMe conference.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Meetme'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='MeetmeJoin']/managerEventInstance/syntax/parameter[@name='Usernum'])" />
<parameter name="Duration">
<para>The length of time in seconds that the Meetme user was in the conference.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">MeetmeJoin</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeLeave",
"Channel: %s\r\n"
"Uniqueid: %s\r\n"

View File

@ -1579,6 +1579,50 @@ static int update_status(struct call_queue *q, struct member *m, const int statu
return 0;
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a Queue member's status has changed.</synopsis>
<syntax>
<parameter name="Queue">
<para>The name of the queue.</para>
</parameter>
<parameter name="Location">
<para>The queue member's channel technology or location.</para>
</parameter>
<parameter name="MemberName">
<para>The name of the queue member.</para>
</parameter>
<parameter name="StateInterface">
<para>Channel technology or location from which to read device state changes.</para>
</parameter>
<parameter name="Membership">
<enumlist>
<enum name="dynamic"/>
<enum name="realtime"/>
<enum name="static"/>
</enumlist>
</parameter>
<parameter name="Penalty">
<para>The penalty associated with the queue member.</para>
</parameter>
<parameter name="CallsTaken">
<para>The number of calls this queue member has serviced.</para>
</parameter>
<parameter name="LastCall">
<para>The time this member last took call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
</parameter>
<parameter name="Status">
<para>The status of the queue member. This will be a device state value.</para>
</parameter>
<parameter name="Paused">
<enumlist>
<enum name="0"/>
<enum name="1"/>
</enumlist>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
"Queue: %s\r\n"
"Location: %s\r\n"
@ -2746,6 +2790,24 @@ static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *
ast_copy_string(qe->context, q->context, sizeof(qe->context));
q->count++;
res = 0;
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel joins a Queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<parameter name="Position">
<para>This channel's current position in the queue.</para>
</parameter>
<parameter name="Count">
<para>The total number of channels in the queue.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">Leave</ref>
<ref type="application">Queue</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
"Channel: %s\r\n"
"CallerIDNum: %s\r\n"
@ -3033,6 +3095,19 @@ static void leave_queue(struct queue_ent *qe)
q->count--;
/* Take us out of the queue */
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a channel leaves a Queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Count'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Position'])" />
</syntax>
<see-also>
<ref type="managerEvent">Join</ref>
</see-also>
</managerEventInstance>
***/
ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
"Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
ast_channel_name(qe->chan), q->name, q->count, qe->pos, ast_channel_uniqueid(qe->chan));
@ -3435,6 +3510,28 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
ast_channel_lock_both(tmp->chan, qe->chan);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an Agent is notified of a member in the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<parameter name="AgentCalled">
<para>The agent's technology or location.</para>
</parameter>
<parameter name="AgentName">
<para>The name of the agent.</para>
</parameter>
<parameter name="Variable" required="no" multiple="yes">
<para>Optional channel variables from the ChannelCalling channel</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">AgentRingNoAnswer</ref>
<ref type="managerEvent">AgentComplete</ref>
<ref type="managerEvent">AgentConnect</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "AgentCalled",
"Queue: %s\r\n"
"AgentCalled: %s\r\n"
@ -3644,6 +3741,21 @@ static void record_abandoned(struct queue_ent *qe)
{
set_queue_variables(qe->parent, qe->chan);
ao2_lock(qe->parent);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an caller abandons the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Position'])" />
<parameter name="OriginalPosition">
<para>The channel's original position in the queue.</para>
</parameter>
<parameter name="HoldTime">
<para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
@ -3669,14 +3781,32 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member
if (qe->parent->eventwhencalled) {
char vars[2048];
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an agent is notified of a member in the queue and fails to answer.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
<parameter name="Member">
<para>The queue member's channel technology or location.</para>
</parameter>
<parameter name="RingTime">
<para>The time the agent was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">AgentCalled</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"MemberName: %s\r\n"
"Ringtime: %d\r\n"
"RingTime: %d\r\n"
"%s",
qe->parent->name,
ast_channel_uniqueid(qe->chan),
@ -4543,6 +4673,32 @@ static void send_agent_complete(const struct queue_ent *qe, const char *queuenam
break;
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an agent has finished servicing a member in the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
<parameter name="TalkTime">
<para>The time the agent talked with the member in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
</parameter>
<parameter name="Reason">
<enumlist>
<enum name="caller"/>
<enum name="agent"/>
<enum name="transfer"/>
</enumlist>
</parameter>
</syntax>
<see-also>
<ref type="managerEvent">AgentCalled</ref>
<ref type="managerEvent">AgentConnect</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "AgentComplete",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
@ -5086,6 +5242,21 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
if (qe->parent->eventwhencalled)
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an agent hangs up on a member in the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
</syntax>
<see-also>
<ref type="managerEvent">AgentCalled</ref>
<ref type="managerEvent">AgentConnect</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "AgentDump",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
@ -5401,15 +5572,33 @@ static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char *
}
if (qe->parent->eventwhencalled)
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when an agent answers and is bridged to a member in the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
</syntax>
<see-also>
<ref type="managerEvent">AgentCalled</ref>
<ref type="managerEvent">AgentComplete</ref>
<ref type="managerEvent">AgentDump</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "AgentConnect",
"Queue: %s\r\n"
"Uniqueid: %s\r\n"
"Channel: %s\r\n"
"Member: %s\r\n"
"MemberName: %s\r\n"
"Holdtime: %ld\r\n"
"HoldTime: %ld\r\n"
"BridgedChannel: %s\r\n"
"Ringtime: %ld\r\n"
"RingTime: %ld\r\n"
"%s",
queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
(long) time(NULL) - qe->start, ast_channel_uniqueid(peer), (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
@ -5609,6 +5798,20 @@ static int remove_from_queue(const char *queuename, const char *interface)
queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
return RES_NOT_DYNAMIC;
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member is removed from the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
</syntax>
<see-also>
<ref type="managerEvent">QueueMemberAdded</ref>
<ref type="application">RemoveQueueMember</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
"Queue: %s\r\n"
"Location: %s\r\n"
@ -5657,6 +5860,27 @@ static int add_to_queue(const char *queuename, const char *interface, const char
new_member->ringinuse = q->ringinuse;
new_member->dynamic = 1;
ao2_link(q->members, new_member);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member is added to the queue.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='StateInterface'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Membership'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Penalty'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='CallsTaken'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='LastCall'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Status'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
</syntax>
<see-also>
<ref type="managerEvent">QueueMemberRemoved</ref>
<ref type="application">AddQueueMember</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
"Queue: %s\r\n"
"Location: %s\r\n"
@ -5737,6 +5961,21 @@ static int set_member_paused(const char *queuename, const char *interface, const
ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member is paused/unpaused in the queue with a reason.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
</syntax>
<see-also>
<ref type="application">PauseQueueMember</ref>
<ref type="application">UnPauseQueueMember</ref>
</see-also>
</managerEventInstance>
***/
if (!ast_strlen_zero(reason)) {
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
@ -5746,6 +5985,21 @@ static int set_member_paused(const char *queuename, const char *interface, const
"Reason: %s\r\n",
q->name, mem->interface, mem->membername, paused, reason);
} else {
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member is paused/unpaused in the queue without a reason.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
</syntax>
<see-also>
<ref type="application">PauseQueueMember</ref>
<ref type="application">UnPauseQueueMember</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
"Queue: %s\r\n"
"Location: %s\r\n"
@ -5796,6 +6050,19 @@ static int set_member_penalty_help_members(struct call_queue *q, const char *int
update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
}
ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member's penalty is changed.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Penalty'])" />
</syntax>
<see-also>
<ref type="function">QUEUE_MEMBER</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
"Queue: %s\r\n"
"Location: %s\r\n"
@ -5824,6 +6091,24 @@ static int set_member_ringinuse_help_members(struct call_queue *q, const char *i
update_realtime_member_field(mem, q->name, realtime_ringinuse_field, rtringinuse);
}
ast_queue_log(q->name, "NONE", interface, "RINGINUSE", "%d", ringinuse);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
<parameter name="Ringinuse">
<enumlist>
<enum name="0"/>
<enum name="1"/>
</enumlist>
</parameter>
</syntax>
<see-also>
<ref type="function">QUEUE_MEMBER</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_AGENT, "QueueMemberRinginuse",
"Queue: %s\r\n"
"Location: %s\r\n"

View File

@ -247,6 +247,14 @@ static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *fra
pbx_builtin_setvar_helper(chan, var, value);
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a LOCAL channel variable is set due to a subroutine call.</synopsis>
<see-also>
<ref type="application">GoSub</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_DIALPLAN, "VarSet",
"Channel: %s\r\n"
"Variable: LOCAL(%s)\r\n"

View File

@ -89,6 +89,17 @@ static int userevent_exec(struct ast_channel *chan, const char *data)
ast_str_append(&body, 0, "%s\r\n", args.extra[x]);
}
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>A user defined event raised from the dialplan.</synopsis>
<parameter name="UserEvent">
<para>The event name, as specified in the dialplan.</para>
</parameter>
<see-also>
<ref type="application">UserEvent</ref>
</see-also>
</managerEventInstance>
***/
manager_event(EVENT_FLAG_USER, "UserEvent",
"UserEvent: %s\r\n"
"Uniqueid: %s\r\n"

View File

@ -7753,7 +7753,30 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a new message has been left in a voicemail mailbox.</synopsis>
<syntax>
<parameter name="Mailbox">
<para>The mailbox with the new message, specified as <emphasis>mailbox</emphasis>@<emphasis>context</emphasis></para>
</parameter>
<parameter name="Waiting">
<para>Whether or not the mailbox has access to a voicemail application.</para>
</parameter>
<parameter name="New">
<para>The number of new messages.</para>
</parameter>
<parameter name="Old">
<para>The number of old messages.</para>
</parameter>
</syntax>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting",
"Mailbox: %s@%s\r\n"
"Waiting: %d\r\n"
"New: %d\r\n"
"Old: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
run_externnotify(vmu->context, vmu->mailbox, flag);
#ifdef IMAP_STORAGE
@ -11396,6 +11419,11 @@ out:
if (valid) {
int new = 0, old = 0, urgent = 0;
snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
/*** DOCUMENTATION
<managerEventInstance>
<synopsis>Raised when a user has finished listening to their messages.</synopsis>
</managerEventInstance>
***/
ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
/* Urgent flag not passwd to externnotify here */
run_externnotify(vmu->context, vmu->mailbox, NULL);

View File

@ -0,0 +1,175 @@
#! /usr/bin/env python
# vin: sw=3 et:
'''
Copyright (C) 2012, Digium, Inc.
Matt Jordan <mjordan@digium.com>
This program is free software, distributed under the terms of
the GNU General Public License Version 2.
'''
import sys
import os
import xml.dom.minidom
from xml.dom.minidom import Element
def get_manager_event_method_type(candidate_string):
if "ast_manager_event_multichan" in candidate_string:
return "multichan"
elif "ast_manager_event" in candidate_string:
return "ast_manager_event"
elif "manager_event" in candidate_string:
return "manager_event"
return ""
def parse_manager_event_instance(xml_fragment):
''' Parse the information for a manager event
Keyword Arguments:
xml_fragment The XML fragment comment
Returns:
A well-formed XML fragment containing the comments passed in, as well as
information obtained from the manager_event macro calls
'''
def __node_contains_parameter(node, parameter):
''' Return whether or not a node contains a given parameter name '''
return any([n for n in node.getElementsByTagName("parameter")
if __node_contains_attribute(n, parameter)])
def __node_contains_attribute(node, attribute_name):
''' Return whether or not a node contains a given attribute name '''
return any([attr for attr in node.attributes.items()
if attr[1] == attribute_name])
candidate_lines = []
type = ""
# Read the manager_event method call, which should occur after
# the documentation block
for line in sys.stdin:
if len(line):
candidate_lines.append(line)
if ");" in line:
break
candidate_string = ''.join(candidate_lines)
type = get_manager_event_method_type(candidate_string)
if not type:
# Unknown, return what we have
return ''.join(xml_fragment)
# strip off the macro name
first_paren = candidate_string.index("(", 0)
last_paren = candidate_string.rindex(");")
candidate_string = candidate_string[first_paren + 1:last_paren]
# split into parameter tokens
func_parameter_tokens = candidate_string.split(',')
if type == "manager_event" or type == "multichan":
class_level = func_parameter_tokens[0].strip()
event_type = func_parameter_tokens[1].strip()
else:
class_level = func_parameter_tokens[1].strip()
event_type = func_parameter_tokens[2].strip()
if type == "manager_event":
event_parameters = func_parameter_tokens[2].strip()
elif type == "ast_manager_event":
event_parameters = func_parameter_tokens[3].strip()
else:
event_parameters = func_parameter_tokens[4].strip()
parameter_tokens = event_parameters.replace("\"", "").split('\\r\\n')
# Build the top level XML element information. Note that we temporarily
# add the xi namespace in case any includes are used
node_text = '<managerEvent language=\"%s\" name=\"%s\" xmlns:xi=\"%s\">'
xml_fragment.insert(0, node_text % ('en_US',
event_type.strip().replace("\"", ""),
'http://www.w3.org/2001/XInclude'))
xml_fragment[1] = "<managerEventInstance class=\"%s\">" % (class_level)
xml_fragment.insert(len(xml_fragment), "</managerEvent>")
# Turn the XML into a DOM to manage the rest of the node manipulations
dom = xml.dom.minidom.parseString(''.join(xml_fragment))
# Get the syntax node if we have one; otherwise make one
instance = dom.getElementsByTagName("managerEventInstance")[0]
syntax = instance.getElementsByTagName("syntax")
if not syntax:
syntax = dom.createElement("syntax")
instance.appendChild(syntax)
# Move any existing parameter nodes over
for node in instance.getElementsByTagName("parameter"):
syntax.appendChild(node.cloneNode(True))
instance.removeChild(node)
else:
syntax = syntax[0]
# Add parameters found in the method invocation that were not previously
# documented
for parameter in parameter_tokens:
if not len(parameter):
continue
index = parameter.find(':')
if index < 0:
index = len(parameter)
parameter = (parameter[:index].strip().replace("\"", ""))
if ('%s' not in parameter and
not __node_contains_parameter(syntax, parameter)):
e = dom.createElement("parameter")
e.setAttribute('name', parameter)
syntax.appendChild(e)
return dom.toxml().replace("<?xml version=\"1.0\" ?>", "").replace(
'xmlns:xi="http://www.w3.org/2001/XInclude"', '')
def main(argv=None):
if argv is None:
argv = sys.argv
in_doc = False
xml_fragment = []
xml = []
line_number = 0
for line in sys.stdin:
# Note: multiple places may have to read a line, so iterating over
# readlines isn't possible. Break when a null line is returned
line_number += 1
if not line:
break
line = line.strip()
if ("/*** DOCUMENTATION" in line):
in_doc = True
elif ("***/" in line and in_doc):
# Depending on what we're processing, determine if we need to do
# any additional work
in_doc = False
if not xml_fragment:
# Nothing read, move along
continue
if "<managerEventInstance>" in xml_fragment[0]:
xml.append(parse_manager_event_instance(xml_fragment))
else:
xml.append(''.join(xml_fragment))
xml_fragment = []
elif (in_doc):
xml_fragment.append("%s\n" % line)
sys.stdout.write(''.join(xml))
return 0
if __name__ == "__main__":
sys.exit(main() or 0)

View File

@ -0,0 +1,105 @@
#! /usr/bin/env python
# vin: sw=3 et:
'''
Copyright (C) 2012, Digium, Inc.
Matt Jordan <mjordan@digium.com>
This program is free software, distributed under the terms of
the GNU General Public License Version 2.
'''
import sys
import os
import optparse
import xml.dom.minidom
from xml.dom.minidom import Element, parse
def merge_parameter_information(managerEvent):
''' Merge the parameter information across all managerEventInstances
within a managerEvent node '''
def __swap_parameter_documentation(one, two):
# See who has the better documentation and use it
if (one.hasChildNodes()):
two.parentNode.replaceChild(one.cloneNode(True), two)
elif (two.hasChildNodes()):
one.parentNode.replaceChild(two.cloneNode(True), one)
def __merge_parameter(param, other_instances):
# Compare the parameter to every other instance's set of parameters
for other in other_instances:
other_parameters = other.getElementsByTagName("parameter")
match = [p for p in other_parameters
if p.getAttribute('name') == param.getAttribute('name')]
if (match):
# See who has the better documentation and use it
__swap_parameter_documentation(param, match[0])
instances = managerEvent.getElementsByTagName("managerEventInstance")
merged = []
for instance in instances:
others = [i for i in instances if i != instance]
parameters = instance.getElementsByTagName("parameter")
for parameter in parameters:
if parameter not in merged:
merged.append(parameter)
__merge_parameter(parameter, others)
def collapse_event_pair(managerEventOne, managerEventTwo):
# Move all children of managerEventTwo to managerEventOne
for node in managerEventTwo.childNodes:
managerEventOne.appendChild(node.cloneNode(True))
return managerEventOne
def collapse_manager_events(rootNode, managerEvents):
events = {}
for managerEvent in managerEvents:
rootNode.removeChild(managerEvent)
attr = managerEvent.getAttribute('name')
if attr in events:
# match, collapse the two managerEvents
events[attr] = collapse_event_pair(events[attr], managerEvent)
else:
events[attr] = managerEvent
# Combine parameter information and re-add the manager Events
for k, event in events.items():
merge_parameter_information(event)
rootNode.appendChild(event)
return
def main(argv=None):
if argv is None:
argv = sys.argv
parser = optparse.OptionParser()
parser.add_option('-i', '--input', dest='input_file',
default='doc/core-full-en_US.xml',
help='The XML file to process')
parser.add_option('-o', '--output', dest='output_file',
default='doc/core-en_US.xml',
help='The XML file to create')
(options, args) = parser.parse_args(argv)
dom = parse(options.input_file)
datasource = open(options.output_file, 'w')
docs = dom.getElementsByTagName("docs")[0]
managerEvents = dom.getElementsByTagName("managerEvent")
if (managerEvents):
collapse_manager_events(docs, managerEvents)
dom.writexml(datasource)
datasource.close()
return 0
if __name__ == "__main__":
sys.exit(main() or 0)

1064
configure vendored

File diff suppressed because it is too large Load Diff

View File

@ -247,6 +247,7 @@ AC_PATH_PROG([BISON], [bison], :)
AC_PATH_PROG([CMP], [cmp], :)
AC_PATH_PROG([FLEX], [flex], :)
AC_PATH_PROG([GREP], [grep], :)
AC_PATH_PROG([PYTHON], [python], :)
AC_PATH_PROG([FIND], [find], :)
AC_PATH_PROG([COMPRESS], [compress], :)
AC_PATH_PROG([BASENAME], [basename], :)

View File

@ -1,4 +1,4 @@
<!ELEMENT docs (application|function|agi|manager)*>
<!ELEMENT docs (application|function|agi|manager|managerEvent)*>
<!ATTLIST docs xmlns:xi CDATA #FIXED "http://www.w3.org/2001/XInclude">
<!ELEMENT xi:include (xi:fallback?) >
@ -29,10 +29,17 @@
<!ATTLIST manager name CDATA #REQUIRED>
<!ATTLIST manager language CDATA #REQUIRED>
<!ELEMENT managerEvent (managerEventInstance+)>
<!ATTLIST managerEvent name CDATA #REQUIRED>
<!ATTLIST managerEvent language CDATA #REQUIRED>
<!ELEMENT managerEventInstance (synopsis?,syntax?,description?,see-also?)*>
<!ATTLIST managerEventInstance class CDATA #REQUIRED>
<!ELEMENT see-also (ref|xi:include)*>
<!ELEMENT ref (#PCDATA)>
<!ATTLIST ref type (application|function|astcli|link|manpage|filename|agi) #REQUIRED>
<!ATTLIST ref type (application|function|astcli|link|manpage|filename|agi|managerEvent) #REQUIRED>
<!ELEMENT synopsis (#PCDATA)>

View File

@ -22,6 +22,8 @@
*/
#include "asterisk/xml.h"
#include "asterisk/stringfields.h"
#include "asterisk/strings.h"
/*! \brief From where the documentation come from, this structure is useful for
* use it inside application/functions/manager actions structure. */
@ -32,6 +34,38 @@ enum ast_doc_src {
#ifdef AST_XML_DOCS
struct ao2_container;
/*! \brief Struct that contains the XML documentation for a particular item. Note
* that this is an ao2 ref counted object.
*
* \note
* Each of the ast_str objects are built from the corresponding ast_xmldoc_build_*
* calls
*
* \since 11
*/
struct ast_xml_doc_item {
/*! The syntax of the item */
struct ast_str *syntax;
/*! Seealso tagged information, if it exists */
struct ast_str *seealso;
/*! The arguments to the item */
struct ast_str *arguments;
/*! A synopsis of the item */
struct ast_str *synopsis;
/*! A description of the item */
struct ast_str *description;
AST_DECLARE_STRING_FIELDS(
/*! The name of the item */
AST_STRING_FIELD(name);
/*! The type of the item */
AST_STRING_FIELD(type);
);
/*! The next XML documentation item that matches the same name/item type */
struct ast_xml_doc_item *next;
};
/*!
* \brief Get the syntax for a specified application or function.
* \param type Application, Function or AGI ?
@ -92,6 +126,18 @@ char *ast_xmldoc_build_synopsis(const char *type, const char *name, const char *
*/
char *ast_xmldoc_build_description(const char *type, const char *name, const char *module);
/*!
* \brief Build the documentation for a particular source type
* \param type The source of the documentation items (application, function, etc.)
*
* \retval NULL on error
* \retval An ao2_container populated with ast_xml_doc instances for each item
* that exists for the specified source type
*
* \since 11
*/
struct ao2_container *ast_xmldoc_build_documentation(const char *type);
#endif /* AST_XML_DOCS */
#endif /* _ASTERISK_XMLDOC_H */

View File

@ -1104,6 +1104,9 @@ static AST_RWLIST_HEAD_STATIC(actions, manager_action);
/*! \brief list of hooks registered */
static AST_RWLIST_HEAD_STATIC(manager_hooks, manager_custom_hook);
/*! \brief A container of event documentation nodes */
AO2_GLOBAL_OBJ_STATIC(event_docs);
static void free_channelvars(void);
static enum add_filter_result manager_add_filter(const char *filter_pattern, struct ao2_container *whitefilters, struct ao2_container *blackfilters);
@ -6800,6 +6803,170 @@ static char *handle_manager_show_settings(struct ast_cli_entry *e, int cmd, stru
return CLI_SUCCESS;
}
#ifdef AST_XML_DOCS
static int ast_xml_doc_item_cmp_fn(const void *a, const void *b)
{
struct ast_xml_doc_item **item_a = (struct ast_xml_doc_item **)a;
struct ast_xml_doc_item **item_b = (struct ast_xml_doc_item **)b;
return strcmp((*item_a)->name, (*item_b)->name);
}
static char *handle_manager_show_events(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_container *events;
struct ao2_iterator *it_events;
struct ast_xml_doc_item *item;
struct ast_xml_doc_item **items;
struct ast_str *buffer;
int i = 0, totalitems = 0;
switch (cmd) {
case CLI_INIT:
e->command = "manager show events";
e->usage =
"Usage: manager show events\n"
" Prints a listing of the available Asterisk manager interface events.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
buffer = ast_str_create(128);
if (!buffer) {
return CLI_SUCCESS;
}
events = ao2_global_obj_ref(event_docs);
if (!events) {
ast_cli(a->fd, "No manager event documentation loaded\n");
ast_free(buffer);
return CLI_SUCCESS;
}
ao2_lock(events);
if (!(it_events = ao2_callback(events, OBJ_MULTIPLE | OBJ_NOLOCK, NULL, NULL))) {
ao2_unlock(events);
ast_log(AST_LOG_ERROR, "Unable to create iterator for events container\n");
ast_free(buffer);
ao2_ref(events, -1);
return CLI_SUCCESS;
}
if (!(items = ast_calloc(sizeof(struct ast_xml_doc_item *), ao2_container_count(events)))) {
ao2_unlock(events);
ast_log(AST_LOG_ERROR, "Unable to create temporary sorting array for events\n");
ao2_iterator_destroy(it_events);
ast_free(buffer);
ao2_ref(events, -1);
return CLI_SUCCESS;
}
ao2_unlock(events);
while ((item = ao2_iterator_next(it_events))) {
items[totalitems++] = item;
ao2_ref(item, -1);
}
qsort(items, totalitems, sizeof(struct ast_xml_doc_item *), ast_xml_doc_item_cmp_fn);
ast_cli(a->fd, "Events:\n");
ast_cli(a->fd, " -------------------- -------------------- -------------------- \n");
for (i = 0; i < totalitems; i++) {
ast_str_append(&buffer, 0, " %-20.20s", items[i]->name);
if ((i + 1) % 3 == 0) {
ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
ast_str_set(&buffer, 0, "%s", "");
}
}
if ((i + 1) % 3 != 0) {
ast_cli(a->fd, "%s\n", ast_str_buffer(buffer));
}
ao2_iterator_destroy(it_events);
ast_free(items);
ao2_ref(events, -1);
ast_free(buffer);
return CLI_SUCCESS;
}
static char *handle_manager_show_event(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ao2_container *events;
struct ast_xml_doc_item *item, *temp;
char syntax_title[64], description_title[64], synopsis_title[64], seealso_title[64], arguments_title[64];
switch (cmd) {
case CLI_INIT:
e->command = "manager show event";
e->usage =
"Usage: manager show event <eventname>\n"
" Provides a detailed description a Manager interface event.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
events = ao2_global_obj_ref(event_docs);
if (!events) {
ast_cli(a->fd, "No manager event documentation loaded\n");
return CLI_SUCCESS;
}
if (!(item = ao2_find(events, a->argv[3], OBJ_KEY))) {
ast_cli(a->fd, "Could not find event '%s'\n", a->argv[3]);
ao2_ref(events, -1);
return CLI_SUCCESS;
}
term_color(synopsis_title, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
term_color(description_title, "[Description]\n", COLOR_MAGENTA, 0, 40);
term_color(syntax_title, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
term_color(seealso_title, "[See Also]\n", COLOR_MAGENTA, 0, 40);
term_color(arguments_title, "[Arguments]\n", COLOR_MAGENTA, 0, 40);
ast_cli(a->fd, "Event: %s\n", a->argv[3]);
for (temp = item; temp; temp = temp->next) {
if (!ast_strlen_zero(ast_str_buffer(temp->synopsis))) {
ast_cli(a->fd, "%s%s\n\n",
synopsis_title,
ast_xmldoc_printable(ast_str_buffer(temp->synopsis), 1));
}
if (!ast_strlen_zero(ast_str_buffer(temp->syntax))) {
ast_cli(a->fd, "%s%s\n\n",
syntax_title,
ast_xmldoc_printable(ast_str_buffer(temp->syntax), 1));
}
if (!ast_strlen_zero(ast_str_buffer(temp->description))) {
ast_cli(a->fd, "%s%s\n\n",
description_title,
ast_xmldoc_printable(ast_str_buffer(temp->description), 1));
}
if (!ast_strlen_zero(ast_str_buffer(temp->arguments))) {
ast_cli(a->fd, "%s%s\n\n",
arguments_title,
ast_xmldoc_printable(ast_str_buffer(temp->arguments), 1));
}
if (!ast_strlen_zero(ast_str_buffer(temp->seealso))) {
ast_cli(a->fd, "%s%s\n\n",
seealso_title,
ast_xmldoc_printable(ast_str_buffer(temp->seealso), 1));
}
}
ao2_ref(item, -1);
ao2_ref(events, -1);
return CLI_SUCCESS;
}
#endif
static struct ast_cli_entry cli_manager[] = {
AST_CLI_DEFINE(handle_showmancmd, "Show a manager interface command"),
AST_CLI_DEFINE(handle_showmancmds, "List manager interface commands"),
@ -6810,6 +6977,10 @@ static struct ast_cli_entry cli_manager[] = {
AST_CLI_DEFINE(handle_mandebug, "Show, enable, disable debugging of the manager code"),
AST_CLI_DEFINE(handle_manager_reload, "Reload manager configurations"),
AST_CLI_DEFINE(handle_manager_show_settings, "Show manager global settings"),
#ifdef AST_XML_DOCS
AST_CLI_DEFINE(handle_manager_show_events, "List manager interface events"),
AST_CLI_DEFINE(handle_manager_show_event, "Show a manager interface event"),
#endif
};
/*!
@ -6852,6 +7023,9 @@ static void load_channelvars(struct ast_variable *var)
static int __init_manager(int reload)
{
struct ast_config *ucfg = NULL, *cfg = NULL;
#ifdef AST_XML_DOCS
struct ao2_container *temp_event_docs;
#endif
const char *val;
char *cat = NULL;
int newhttptimeout = 60;
@ -6910,6 +7084,17 @@ static int __init_manager(int reload)
/* Append placeholder event so master_eventq never runs dry */
append_event("Event: Placeholder\r\n\r\n", 0);
}
#ifdef AST_XML_DOCS
temp_event_docs = ast_xmldoc_build_documentation("managerEvent");
if (temp_event_docs) {
temp_event_docs = ao2_global_obj_replace(event_docs, temp_event_docs);
if (temp_event_docs) {
ao2_ref(temp_event_docs, -1);
}
}
#endif
if ((cfg = ast_config_load2("manager.conf", "manager", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
return 0;
}

View File

@ -34,9 +34,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
#include "asterisk/paths.h"
#include "asterisk/linkedlists.h"
#include "asterisk/strings.h"
#include "asterisk/config.h"
#include "asterisk/term.h"
#include "asterisk/astobj2.h"
#include "asterisk/xmldoc.h"
#ifdef AST_XML_DOCS
@ -1043,13 +1043,14 @@ static char *xmldoc_get_syntax_cmd(struct ast_xml_node *fixnode, const char *nam
}
/*! \internal
* \brief Generate an AMI action syntax.
* \param fixnode The manager action node pointer.
* \param name The name of the manager action.
* \brief Generate an AMI action/event syntax.
* \param fixnode The manager action/event node pointer.
* \param name The name of the manager action/event.
* \param manager_type "Action" or "Event"
* \retval The generated syntax.
* \retval NULL on error.
*/
static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name)
static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char *name, const char *manager_type)
{
struct ast_str *syntax;
struct ast_xml_node *node = fixnode;
@ -1062,7 +1063,7 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
return ast_strdup(name);
}
ast_str_append(&syntax, 0, "Action: %s", name);
ast_str_append(&syntax, 0, "%s: %s", manager_type, name);
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (strcasecmp(ast_xml_node_get_name(node), "parameter")) {
@ -1070,7 +1071,7 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
}
/* Is this parameter required? */
required = 0;
required = !strcasecmp(manager_type, "event") ? 1 : 0;
paramtype = ast_xml_get_attribute(node, "required");
if (paramtype) {
required = ast_true(paramtype);
@ -1087,7 +1088,6 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
(required ? "" : "["),
attrname,
(required ? "" : "]"));
ast_xml_free_attr(attrname);
}
@ -1102,6 +1102,7 @@ static char *xmldoc_get_syntax_manager(struct ast_xml_node *fixnode, const char
enum syntaxtype {
FUNCTION_SYNTAX,
MANAGER_SYNTAX,
MANAGER_EVENT_SYNTAX,
COMMAND_SYNTAX
};
@ -1110,10 +1111,11 @@ static struct strsyntaxtype {
const char *type;
enum syntaxtype stxtype;
} stxtype[] = {
{ "function", FUNCTION_SYNTAX },
{ "application", FUNCTION_SYNTAX },
{ "manager", MANAGER_SYNTAX },
{ "agi", COMMAND_SYNTAX }
{ "function", FUNCTION_SYNTAX },
{ "application", FUNCTION_SYNTAX },
{ "manager", MANAGER_SYNTAX },
{ "managerEvent", MANAGER_EVENT_SYNTAX },
{ "agi", COMMAND_SYNTAX }
};
/*! \internal
@ -1133,40 +1135,67 @@ static enum syntaxtype xmldoc_get_syntax_type(const char *type)
return FUNCTION_SYNTAX;
}
char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
/*!
* \internal
* \brief Build syntax information for an item
* \param node The syntax node to parse
* \param type The source type
* \param name The name of the item that the syntax describes
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \returns A malloc'd character pointer to the syntax of the item
* \returns NULL on failure
*
* \since 11
*/
static char *_ast_xmldoc_build_syntax(struct ast_xml_node *node, const char *type, const char *name)
{
struct ast_xml_node *node;
char *syntax = NULL;
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node) {
return NULL;
}
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (!strcasecmp(ast_xml_node_get_name(node), "syntax")) {
break;
}
}
if (node) {
switch (xmldoc_get_syntax_type(type)) {
case FUNCTION_SYNTAX:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
break;
case COMMAND_SYNTAX:
syntax = xmldoc_get_syntax_cmd(node, name, 1);
break;
case MANAGER_SYNTAX:
syntax = xmldoc_get_syntax_manager(node, name);
break;
default:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
}
if (!node) {
return syntax;
}
switch (xmldoc_get_syntax_type(type)) {
case FUNCTION_SYNTAX:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
break;
case COMMAND_SYNTAX:
syntax = xmldoc_get_syntax_cmd(node, name, 1);
break;
case MANAGER_SYNTAX:
syntax = xmldoc_get_syntax_manager(node, name, "Action");
break;
case MANAGER_EVENT_SYNTAX:
syntax = xmldoc_get_syntax_manager(node, name, "Event");
break;
default:
syntax = xmldoc_get_syntax_fun(node, name, "parameter", 1, 1);
}
return syntax;
}
char *ast_xmldoc_build_syntax(const char *type, const char *name, const char *module)
{
struct ast_xml_node *node;
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node) {
return NULL;
}
return _ast_xmldoc_build_syntax(node, type, name);
}
/*! \internal
* \brief Parse a <para> element.
* \param node The <para> element pointer.
@ -1439,25 +1468,27 @@ static int xmldoc_parse_variablelist(struct ast_xml_node *node, const char *tabs
return ret;
}
char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
/*!
* \internal
* \brief Build seealso information for an item
* \param node The seealso node to parse
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \returns A malloc'd character pointer to the seealso information of the item
* \returns NULL on failure
*
* \since 11
*/
static char *_ast_xmldoc_build_seealso(struct ast_xml_node *node)
{
struct ast_str *outputstr;
char *output;
struct ast_xml_node *node;
struct ast_str *outputstr;
const char *typename;
const char *content;
int first = 1;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
return NULL;
}
/* get the application/function root node. */
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node || !ast_xml_node_get_children(node)) {
return NULL;
}
/* Find the <see-also> node. */
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
if (!strcasecmp(ast_xml_node_get_name(node), "see-also")) {
@ -1512,6 +1543,26 @@ char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *m
return output;
}
char *ast_xmldoc_build_seealso(const char *type, const char *name, const char *module)
{
char *output;
struct ast_xml_node *node;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
return NULL;
}
/* get the application/function root node. */
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node || !ast_xml_node_get_children(node)) {
return NULL;
}
output = _ast_xmldoc_build_seealso(node);
return output;
}
/*! \internal
* \brief Parse a <enum> node.
* \brief fixnode An ast_xml_node pointer to the <enum> node.
@ -1738,21 +1789,26 @@ static void xmldoc_parse_parameter(struct ast_xml_node *fixnode, const char *tab
ast_free(internaltabs);
}
char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
/*!
* \internal
* \brief Build the arguments for an item
* \param node The arguments node to parse
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \returns A malloc'd character pointer to the arguments for the item
* \returns NULL on failure
*
* \since 11
*/
static char *_ast_xmldoc_build_arguments(struct ast_xml_node *node)
{
struct ast_xml_node *node;
struct ast_str *ret = ast_str_create(128);
char *retstr = NULL;
struct ast_str *ret;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
ast_free(ret);
return NULL;
}
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node || !ast_xml_node_get_children(node)) {
ast_free(ret);
ret = ast_str_create(128);
if (!ret) {
return NULL;
}
@ -1786,6 +1842,23 @@ char *ast_xmldoc_build_arguments(const char *type, const char *name, const char
return retstr;
}
char *ast_xmldoc_build_arguments(const char *type, const char *name, const char *module)
{
struct ast_xml_node *node;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
return NULL;
}
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node || !ast_xml_node_get_children(node)) {
return NULL;
}
return _ast_xmldoc_build_arguments(node);
}
/*! \internal
* \brief Return the string within a node formatted with <para> and <variablelist> elements.
* \param node Parent node where content resides.
@ -1828,36 +1901,23 @@ static struct ast_str *xmldoc_get_formatted(struct ast_xml_node *node, int raw_o
}
/*!
* \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
* \param type Type of element (application, function, ...).
* \param name Name of element (Dial, Echo, Playback, ...).
* \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree node
* \param node The node to obtain the information from
* \param var Name of field to return (synopsis, description, etc).
* \param raw Field only contains text, no other elements inside it.
* \retval NULL On error.
* \retval Field text content on success.
* \since 11
*/
static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
static char *_xmldoc_build_field(struct ast_xml_node *node, const char *var, int raw)
{
struct ast_xml_node *node;
char *ret = NULL;
struct ast_str *formatted;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
return ret;
}
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node) {
ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
return ret;
}
node = ast_xml_find_element(ast_xml_node_get_children(node), var, NULL, NULL);
if (!node || !ast_xml_node_get_children(node)) {
ast_debug(1, "Cannot find variable '%s' in tree '%s'\n", var, name);
ast_debug(1, "Cannot find variable '%s' in tree\n", var);
return ret;
}
@ -1870,16 +1930,280 @@ static char *xmldoc_build_field(const char *type, const char *name, const char *
return ret;
}
/*!
* \brief Get the content of a field (synopsis, description, etc) from an asterisk document tree
* \param type Type of element (application, function, ...).
* \param name Name of element (Dial, Echo, Playback, ...).
* \param var Name of field to return (synopsis, description, etc).
* \param raw Field only contains text, no other elements inside it.
* \retval NULL On error.
* \retval Field text content on success.
*/
static char *xmldoc_build_field(const char *type, const char *name, const char *module, const char *var, int raw)
{
struct ast_xml_node *node;
if (ast_strlen_zero(type) || ast_strlen_zero(name)) {
ast_log(LOG_ERROR, "Tried to look in XML tree with faulty values.\n");
return NULL;
}
node = xmldoc_get_node(type, name, module, documentation_language);
if (!node) {
ast_log(LOG_WARNING, "Couldn't find %s %s in XML documentation\n", type, name);
return NULL;
}
return _xmldoc_build_field(node, var, raw);
}
/* \internal
* \brief Build the synopsis for an item
* \param node The synopsis node
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \returns A malloc'd character pointer to the synopsis information
* \returns NULL on failure
* \since 11
*/
static char *_ast_xmldoc_build_synopsis(struct ast_xml_node *node)
{
return _xmldoc_build_field(node, "synopsis", 1);
}
char *ast_xmldoc_build_synopsis(const char *type, const char *name, const char *module)
{
return xmldoc_build_field(type, name, module, "synopsis", 1);
}
/*!
* \internal
* \brief Build the descripton for an item
* \param node The description node to parse
*
* \note This method exists for when you already have the node. This
* prevents having to lock the documentation tree twice
*
* \returns A malloc'd character pointer to the arguments for the item
* \returns NULL on failure
* \since 11
*/
static char *_ast_xmldoc_build_description(struct ast_xml_node *node)
{
return _xmldoc_build_field(node, "description", 0);
}
char *ast_xmldoc_build_description(const char *type, const char *name, const char *module)
{
return xmldoc_build_field(type, name, module, "description", 0);
}
/*! \internal \brief ast_xml_doc_item ao2 destructor
* \since 11
*/
static void ast_xml_doc_item_destructor(void *obj)
{
struct ast_xml_doc_item *doc = obj;
if (!doc) {
return;
}
ast_free(doc->syntax);
ast_free(doc->seealso);
ast_free(doc->arguments);
ast_free(doc->synopsis);
ast_free(doc->description);
ast_string_field_free_memory(doc);
if (doc->next) {
ao2_ref(doc->next, -1);
doc->next = NULL;
}
}
/*! \internal
* \brief Create an ao2 ref counted ast_xml_doc_item
* \param name The name of the item
* \param type The item's source type
* \since 11
*/
static struct ast_xml_doc_item *ast_xml_doc_item_alloc(const char *name, const char *type)
{
struct ast_xml_doc_item *item;
if (!(item = ao2_alloc(sizeof(*item), ast_xml_doc_item_destructor))) {
ast_log(AST_LOG_ERROR, "Failed to allocate memory for ast_xml_doc_item instance\n");
return NULL;
}
if ( !(item->syntax = ast_str_create(128))
|| !(item->seealso = ast_str_create(128))
|| !(item->arguments = ast_str_create(128))
|| !(item->synopsis = ast_str_create(128))
|| !(item->description = ast_str_create(128))) {
ast_log(AST_LOG_ERROR, "Failed to allocate strings for ast_xml_doc_item instance\n");
goto ast_xml_doc_item_failure;
}
if (ast_string_field_init(item, 64)) {
ast_log(AST_LOG_ERROR, "Failed to initialize string field for ast_xml_doc_item instance\n");
goto ast_xml_doc_item_failure;
}
ast_string_field_set(item, name, name);
ast_string_field_set(item, type, type);
return item;
ast_xml_doc_item_failure:
ao2_ref(item, -1);
return NULL;
}
/*! \internal
* \brief ao2 item hash function for ast_xml_doc_item
* \since 11
*/
static int ast_xml_doc_item_hash(const void *obj, const int flags)
{
const struct ast_xml_doc_item *item = obj;
const char *name = (flags & OBJ_KEY) ? obj : item->name;
return ast_str_case_hash(name);
}
/*! \internal
* \brief ao2 item comparison function for ast_xml_doc_item
* \since 11
*/
static int ast_xml_doc_item_cmp(void *obj, void *arg, int flags)
{
struct ast_xml_doc_item *left = obj;
struct ast_xml_doc_item *right = arg;
const char *match = (flags & OBJ_KEY) ? arg : right->name;
return strcasecmp(left->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
}
/* \internal
* \brief Build an XML documentation item
* \param node The root node for the item
* \param name The name of the item
* \param type The item's source type
*
* \returns NULL on failure
* \returns An ao2 ref counted object
* \since 11
*/
static struct ast_xml_doc_item *xmldoc_build_documentation_item(struct ast_xml_node *node, const char *name, const char *type)
{
struct ast_xml_doc_item *item;
char *syntax;
char *seealso;
char *arguments;
char *synopsis;
char *description;
if (!(item = ast_xml_doc_item_alloc(name, type))) {
return NULL;
}
syntax = _ast_xmldoc_build_syntax(node, type, name);
seealso = _ast_xmldoc_build_seealso(node);
arguments = _ast_xmldoc_build_arguments(node);
synopsis = _ast_xmldoc_build_synopsis(node);
description = _ast_xmldoc_build_description(node);
if (syntax) {
ast_str_set(&item->syntax, 0, "%s", syntax);
}
if (seealso) {
ast_str_set(&item->seealso, 0, "%s", seealso);
}
if (arguments) {
ast_str_set(&item->arguments, 0, "%s", arguments);
}
if (synopsis) {
ast_str_set(&item->synopsis, 0, "%s", synopsis);
}
if (description) {
ast_str_set(&item->description, 0, "%s", description);
}
ast_free(syntax);
ast_free(seealso);
ast_free(arguments);
ast_free(synopsis);
ast_free(description);
return item;
}
struct ao2_container *ast_xmldoc_build_documentation(const char *type)
{
struct ao2_container *docs;
struct ast_xml_doc_item *item = NULL, *root = NULL;
struct ast_xml_node *node = NULL, *instance = NULL;
struct documentation_tree *doctree;
const char *name;
if (!(docs = ao2_container_alloc(127, ast_xml_doc_item_hash, ast_xml_doc_item_cmp))) {
ast_log(AST_LOG_ERROR, "Failed to create container for xml document item instances\n");
return NULL;
}
AST_RWLIST_RDLOCK(&xmldoc_tree);
AST_LIST_TRAVERSE(&xmldoc_tree, doctree, entry) {
/* the core xml documents have priority over thirdparty document. */
node = ast_xml_get_root(doctree->doc);
if (!node) {
break;
}
for (node = ast_xml_node_get_children(node); node; node = ast_xml_node_get_next(node)) {
/* Ignore empty nodes or nodes that aren't of the type requested */
if (!ast_xml_node_get_children(node) || strcasecmp(ast_xml_node_get_name(node), type)) {
continue;
}
name = ast_xml_get_attribute(node, "name");
switch (xmldoc_get_syntax_type(type)) {
case MANAGER_EVENT_SYNTAX:
for (instance = ast_xml_node_get_children(node); instance; instance = ast_xml_node_get_next(instance)) {
struct ast_xml_doc_item *temp;
if (!ast_xml_node_get_children(instance) || strcasecmp(ast_xml_node_get_name(instance), "managerEventInstance")) {
continue;
}
temp = xmldoc_build_documentation_item(instance, name, type);
if (!temp) {
break;
}
if (!item) {
item = temp;
root = item;
} else {
item->next = temp;
item = temp;
}
}
item = root;
break;
default:
item = xmldoc_build_documentation_item(node, name, type);
}
if (item) {
ao2_link(docs, item);
item = NULL;
}
}
}
AST_RWLIST_UNLOCK(&xmldoc_tree);
return docs;
}
#if !defined(HAVE_GLOB_NOMAGIC) || !defined(HAVE_GLOB_BRACE) || defined(DEBUG_NONGNU)
static int xml_pathmatch(char *xmlpattern, int xmlpattern_maxlen, glob_t *globbuf)
{

View File

@ -12,6 +12,7 @@ AWK=@AWK@
BISON=@BISON@
FLEX=@FLEX@
GREP=@GREP@
PYTHON=@PYTHON@
MAKE=@GNU_MAKE@
AR=@AR@
RANLIB=@RANLIB@