1 ;; jabber-widget.el - display various kinds of forms
3 ;; Copyright (C) 2003, 2004, 2007 - Magnus Henoch - mange@freemail.hu
4 ;; Copyright (C) 2002, 2003, 2004 - tom berger - object@intelectronica.net
6 ;; This file is a part of jabber.el.
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program; if not, write to the Free Software
20 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 (require 'jabber-util
)
25 (require 'jabber-disco
)
27 (defvar jabber-widget-alist nil
28 "Alist of widgets currently used")
30 (defvar jabber-form-type nil
31 "Type of form. One of:
32 'x-data, jabber:x:data
33 'register, as used in jabber:iq:register and jabber:iq:search")
35 (defvar jabber-submit-to nil
36 "JID of the entity to which form data is to be sent")
38 (add-to-list 'jabber-advertised-features
"jabber:x:data")
40 (define-widget 'jid
'string
42 :value-to-internal
(lambda (widget value
)
43 (let ((displayname (jabber-jid-rostername value
)))
45 (format "%s <%s>" displayname value
)
47 :value-to-external
(lambda (widget value
)
48 (if (string-match "<\\([^>]+\\)>[ \t]*$" value
)
49 (match-string 1 value
)
51 :complete-function
'jid-complete
)
53 (defun jid-complete ()
54 "Perform completion on JID preceding point."
56 ;; mostly stolen from widget-color-complete
57 (let* ((prefix (buffer-substring-no-properties (widget-field-start widget
)
59 (list (append (mapcar #'symbol-name
*jabber-roster
*)
61 (mapcar #'(lambda (item)
62 (when (jabber-jid-rostername item
)
63 (format "%s <%s>" (jabber-jid-rostername item
)
66 (completion (try-completion prefix list
)))
67 (cond ((eq completion t
)
68 (message "Exact match."))
70 (error "Can't find completion for \"%s\"" prefix
))
71 ((not (string-equal prefix completion
))
72 (insert-and-inherit (substring completion
(length prefix
))))
74 (message "Making completion list...")
75 (with-output-to-temp-buffer "*Completions*"
76 (display-completion-list (all-completions prefix list nil
)
78 (message "Making completion list...done")))))
81 (defun jabber-init-widget-buffer (submit-to)
82 "Setup buffer-local variables for widgets."
83 (make-local-variable 'jabber-widget-alist
)
84 (make-local-variable 'jabber-submit-to
)
85 (setq jabber-widget-alist nil
)
86 (setq jabber-submit-to submit-to
)
87 (setq buffer-read-only nil
)
88 ;; XXX: This is because data from other queries would otherwise be
89 ;; appended to this buffer, which would fail since widget buffers
90 ;; are read-only... or something like that. Maybe there's a
94 (defun jabber-render-register-form (query &optional default-username
)
95 "Display widgets from <query/> element in jabber:iq:{register,search} namespace.
96 DEFAULT-USERNAME is the default value for the username field."
97 (make-local-variable 'jabber-widget-alist
)
98 (setq jabber-widget-alist nil
)
99 (make-local-variable 'jabber-form-type
)
100 (setq jabber-form-type
'register
)
102 (if (jabber-xml-get-children query
'instructions
)
103 (widget-insert "Instructions: " (car (jabber-xml-node-children (car (jabber-xml-get-children query
'instructions
)))) "\n"))
104 (if (jabber-xml-get-children query
'registered
)
105 (widget-insert "You are already registered. You can change your details here.\n"))
108 (let ((possible-fields
109 ;; taken from JEP-0077
110 '((username .
"Username")
112 (password .
"Password")
114 (first .
"First name")
117 (address .
"Address")
121 (phone .
"Telephone")
123 (date .
"Birth date"))))
124 (dolist (field (jabber-xml-node-children query
))
125 (let ((entry (assq (jabber-xml-node-name field
) possible-fields
)))
127 (widget-insert (cdr entry
) "\t")
128 ;; Special case: when registering a new account, the default
129 ;; username is the one specified in jabber-username. Things
130 ;; will break if the user changes that name, though...
131 (let ((default-value (or (when (eq (jabber-xml-node-name field
) 'username
)
134 (setq jabber-widget-alist
137 (widget-create 'editable-field
138 :secret
(if (eq (car entry
) 'password
)
140 (or (car (jabber-xml-node-children
141 field
)) default-value
)))
142 jabber-widget-alist
)))
143 (widget-insert "\n"))))))
145 (defun jabber-parse-register-form ()
146 "Return children of a <query/> tag containing information entered in the widgets of the current buffer."
148 (lambda (widget-cons)
149 (list (car widget-cons
)
151 (widget-value (cdr widget-cons
))))
152 jabber-widget-alist
))
154 (defun jabber-render-xdata-form (x &optional defaults
)
155 "Display widgets from <x/> element in jabber:x:data namespace.
156 DEFAULTS is an alist associating variable names with default values.
157 DEFAULTS takes precedence over values specified in the form."
158 (make-local-variable 'jabber-widget-alist
)
159 (setq jabber-widget-alist nil
)
160 (make-local-variable 'jabber-form-type
)
161 (setq jabber-form-type
'xdata
)
163 (let ((title (car (jabber-xml-node-children (car (jabber-xml-get-children x
'title
))))))
165 (widget-insert (jabber-propertize title
'face
'jabber-title-medium
) "\n\n")))
166 (let ((instructions (car (jabber-xml-node-children (car (jabber-xml-get-children x
'instructions
))))))
167 (if (stringp instructions
)
168 (widget-insert "Instructions: " instructions
"\n\n")))
170 (dolist (field (jabber-xml-get-children x
'field
))
171 (let* ((var (jabber-xml-get-attribute field
'var
))
172 (label (jabber-xml-get-attribute field
'label
))
173 (type (jabber-xml-get-attribute field
'type
))
174 (required (jabber-xml-get-children field
'required
))
175 (values (jabber-xml-get-children field
'value
))
176 (options (jabber-xml-get-children field
'option
))
177 (desc (car (jabber-xml-get-children field
'desc
)))
178 (default-value (assoc var defaults
)))
179 ;; "required" not implemented yet
182 ((string= type
"fixed")
183 (widget-insert (car (jabber-xml-node-children (car values
)))))
185 ((string= type
"text-multi")
187 (widget-insert (or label var
) ":\n"))
188 (push (cons (cons var type
)
189 (widget-create 'text
(or (cdr default-value
)
190 (mapconcat #'(lambda (val)
191 (car (jabber-xml-node-children val
)))
194 jabber-widget-alist
))
196 ((string= type
"list-single")
198 (widget-insert (or label var
) ":\n"))
199 (push (cons (cons var type
)
200 (apply 'widget-create
202 :value
(or (cdr default-value
)
203 (car (xml-node-children (car values
))))
204 (mapcar (lambda (option)
205 `(item :tag
,(jabber-xml-get-attribute option
'label
)
206 :value
,(car (jabber-xml-node-children (car (jabber-xml-get-children option
'value
))))))
208 jabber-widget-alist
))
210 ((string= type
"boolean")
211 (push (cons (cons var type
)
212 (widget-create 'checkbox
214 :value
(if default-value
217 (member (car (xml-node-children (car values
))) '("1" "true")))))))
220 (widget-insert " " (or label var
) "\n")))
222 (t ; in particular including text-single and text-private
224 (widget-insert (or label var
) ": "))
225 (setq jabber-widget-alist
227 (cons (cons var type
)
228 (widget-create 'editable-field
229 :secret
(if (string= type
"text-private") ?
* nil
)
230 (or (cdr default-value
)
231 (car (jabber-xml-node-children (car values
)))
233 jabber-widget-alist
))))
234 (when (and desc
(car (jabber-xml-node-children desc
)))
235 (widget-insert "\n" (car (jabber-xml-node-children desc
))))
236 (widget-insert "\n"))))
238 (defun jabber-parse-xdata-form ()
239 "Return an <x/> tag containing information entered in the widgets of the current buffer."
240 `(x ((xmlns .
"jabber:x:data")
243 (lambda (widget-cons)
244 (let ((values (jabber-xdata-value-convert (widget-value (cdr widget-cons
)) (cdar widget-cons
))))
245 ;; empty fields are not included
247 `(field ((var .
,(caar widget-cons
)))
250 (list 'value nil value
))
252 jabber-widget-alist
)))
254 (defun jabber-xdata-value-convert (value type
)
255 "Convert VALUE from form used by widget library to form required by JEP-0004.
256 Return a list of strings, each of which to be included as cdata in a <value/> tag."
258 ((string= type
"boolean")
259 (if value
(list "1") (list "0")))
260 ((string= type
"text-multi")
261 (split-string value
"[\n\r]"))
262 (t ; in particular including text-single, text-private and list-single
263 (if (zerop (length value
))
267 (defun jabber-render-xdata-search-results (xdata)
268 "Render search results in x:data form."
270 (let ((title (car (jabber-xml-get-children xdata
'title
))))
272 (insert (jabber-propertize (car (jabber-xml-node-children title
)) 'face
'jabber-title-medium
) "\n")))
274 (if (jabber-xml-get-children xdata
'reported
)
275 (jabber-render-xdata-search-results-multi xdata
)
276 (jabber-render-xdata-search-results-single xdata
)))
278 (defun jabber-render-xdata-search-results-multi (xdata)
279 "Render multi-record search results."
282 (let ((reported (car (jabber-xml-get-children xdata
'reported
)))
284 (dolist (field (jabber-xml-get-children reported
'field
))
286 ;; Clever algorithm for estimating width based on field type goes here.
292 (list (cons (jabber-xml-get-attribute field
'var
)
293 (list 'label
(jabber-xml-get-attribute field
'label
)
294 'type
(jabber-xml-get-attribute field
'type
)
296 (setq column
(+ column width
))
297 (if (string= (jabber-xml-get-attribute field
'type
) "jid-single")
298 (setq jid-fields
(1+ jid-fields
))))))
300 (dolist (field-cons fields
)
301 (indent-to (plist-get (cdr field-cons
) 'column
) 1)
302 (insert (jabber-propertize (plist-get (cdr field-cons
) 'label
) 'face
'bold
)))
306 (dolist (item (jabber-xml-get-children xdata
'item
))
308 (let ((start-of-line (point))
311 ;; The following code assumes that the order of the <field/>s in each
312 ;; <item/> is the same as in the <reported/> tag.
313 (dolist (field (jabber-xml-get-children item
'field
))
314 (let ((field-plist (cdr (assoc (jabber-xml-get-attribute field
'var
) fields
)))
315 (value (car (jabber-xml-node-children (car (jabber-xml-get-children field
'value
))))))
317 (indent-to (plist-get field-plist
'column
) 1)
319 ;; Absent values are sometimes "", sometimes nil. insert
322 ;; If there is only one JID field, let the whole row
323 ;; have the jabber-jid property. If there are many JID
324 ;; fields, the string belonging to each field has that
326 (if (string= (plist-get field-plist
'type
) "jid-single")
327 (if (not (eq jid-fields
1))
328 (insert (jabber-propertize value
'jabber-jid value
))
334 (put-text-property start-of-line
(point)
338 (defun jabber-render-xdata-search-results-single (xdata)
339 "Render single-record search results."
340 (dolist (field (jabber-xml-get-children xdata
'field
))
341 (let ((label (jabber-xml-get-attribute field
'label
))
342 (type (jabber-xml-get-attribute field
'type
))
343 (values (mapcar #'(lambda (val)
344 (car (jabber-xml-node-children val
)))
345 (jabber-xml-get-children field
'value
))))
346 ;; XXX: consider type
347 (insert (jabber-propertize (concat label
": ") 'face
'bold
))
349 (insert (apply #'concat values
) "\n"))))
351 (defun jabber-xdata-formtype (x)
352 "Return the form type of the xdata form in X, by JEP-0068.
353 Return nil if no form type is specified."
354 (catch 'found-formtype
355 (dolist (field (jabber-xml-get-children x
'field
))
356 (when (and (string= (jabber-xml-get-attribute field
'var
) "FORM_TYPE")
357 (string= (jabber-xml-get-attribute field
'type
) "hidden"))
358 (throw 'found-formtype
(car (jabber-xml-node-children
359 (car (jabber-xml-get-children field
'value
)))))))))
361 (provide 'jabber-widget
)
363 ;;; arch-tag: da3312f3-1970-41d5-a974-14b8d76156b8