summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/xep_0050/stanza.py
blob: 2367c77b99d36dffe940ec16bc517424362cf989 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
"""
    SleekXMPP: The Sleek XMPP Library
    Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
    This file is part of SleekXMPP.

    See the file LICENSE for copying permission.
"""

from sleekxmpp.xmlstream import ElementBase, ET


class Command(ElementBase):

    """
    XMPP's Adhoc Commands provides a generic workflow mechanism for
    interacting with applications. The result is similar to menu selections
    and multi-step dialogs in normal desktop applications. Clients do not
    need to know in advance what commands are provided by any particular
    application or agent. While adhoc commands provide similar functionality
    to Jabber-RPC, adhoc commands are used primarily for human interaction.

    Also see <http://xmpp.org/extensions/xep-0050.html>

    Example command stanzas:
      <iq type="set">
        <command xmlns="http://jabber.org/protocol/commands"
                 node="run_foo"
                 action="execute" />
      </iq>

      <iq type="result">
        <command xmlns="http://jabber.org/protocol/commands"
                 node="run_foo"
                 sessionid="12345"
                 status="executing">
          <actions>
            <complete />
          </actions>
          <note type="info">Information!</note>
          <x xmlns="jabber:x:data">
            <field var="greeting"
                   type="text-single"
                   label="Greeting" />
          </x>
        </command>
      </iq>

    Stanza Interface:
        action    -- The action to perform.
        actions   -- The set of allowable next actions.
        node      -- The node associated with the command.
        notes     -- A list of tuples for informative notes.
        sessionid -- A unique identifier for a command session.
        status    -- May be one of: canceled, completed, or executing.

    Attributes:
        actions      -- A set of allowed action values.
        statuses     -- A set of allowed status values.
        next_actions -- A set of allowed next action names.

    Methods:
        get_action  -- Return the requested action.
        get_actions -- Return the allowable next actions.
        set_actions -- Set the allowable next actions.
        del_actions -- Remove the current set of next actions.
        get_notes   -- Return a list of informative note data.
        set_notes   -- Set informative notes.
        del_notes   -- Remove any note data.
        add_note    -- Add a single note.
    """

    name = 'command'
    namespace = 'http://jabber.org/protocol/commands'
    plugin_attrib = 'command'
    interfaces = set(('action', 'sessionid', 'node',
                      'status', 'actions', 'notes'))
    actions = set(('cancel', 'complete', 'execute', 'next', 'prev'))
    statuses = set(('canceled', 'completed', 'executing'))
    next_actions = set(('prev', 'next', 'complete'))

    def get_action(self):
        """
        Return the value of the action attribute.

        If the Iq stanza's type is "set" then use a default
        value of "execute".
        """
        if self.parent()['type'] == 'set':
            return self._get_attr('action', default='execute')
        return self._get_attr('action')

    def set_actions(self, values):
        """
        Assign the set of allowable next actions.

        Arguments:
            values -- A list containing any combination of:
                        'prev', 'next', and 'complete'
        """
        self.del_actions()
        if values:
            self._set_sub_text('{%s}actions' % self.namespace, '', True)
            actions = self.find('{%s}actions' % self.namespace)
            for val in values:
                if val in self.next_actions:
                    action = ET.Element('{%s}%s' % (self.namespace, val))
                    actions.append(action)

    def get_actions(self):
        """
        Return the set of allowable next actions.
        """
        actions = set()
        actions_xml = self.find('{%s}actions' % self.namespace)
        if actions_xml is not None:
            for action in self.next_actions:
                action_xml = actions_xml.find('{%s}%s' % (self.namespace,
                                                          action))
                if action_xml is not None:
                    actions.add(action)
        return actions

    def del_actions(self):
        """
        Remove all allowable next actions.
        """
        self._del_sub('{%s}actions' % self.namespace)

    def get_notes(self):
        """
        Return a list of note information.

        Example:
            [('info', 'Some informative data'),
             ('warning', 'Use caution'),
             ('error', 'The command ran, but had errors')]
        """
        notes = []
        notes_xml = self.findall('{%s}note' % self.namespace)
        for note in notes_xml:
            notes.append((note.attrib.get('type', 'info'),
                          note.text))
        return notes

    def set_notes(self, notes):
        """
        Add multiple notes to the command result.

        Each note is a tuple, with the first item being one of:
        'info', 'warning', or 'error', and the second item being
        any human readable message.

        Example:
            [('info', 'Some informative data'),
             ('warning', 'Use caution'),
             ('error', 'The command ran, but had errors')]


        Arguments:
            notes -- A list of tuples of note information.
        """
        self.del_notes()
        for note in notes:
            self.add_note(note[1], note[0])

    def del_notes(self):
        """
        Remove all notes associated with the command result.
        """
        notes_xml = self.findall('{%s}note' % self.namespace)
        for note in notes_xml:
            self.xml.remove(note)

    def add_note(self, msg='', ntype='info'):
        """
        Add a single note annotation to the command.

        Arguments:
            msg   -- A human readable message.
            ntype -- One of: 'info', 'warning', 'error'
        """
        xml = ET.Element('{%s}note' % self.namespace)
        xml.attrib['type'] = ntype
        xml.text = msg
        self.xml.append(xml)