;;; wiki-remote.el --- edit pages on a remote wiki ;; This file is available under the GNU public license. ;; History ;; The original file is Copyright (C) 2001 Alex Schroeder ;; modified by D. Goel and Jörg ;; F. Wittenberger ;; This file version is the result of a one shot efford to get ;; anything working to replace that modified version of the file, ;; which used to support Askemos development. ;; This means that _only_ the Askemos related code is actually ;; working. There is however support code for other wikis, which ;; would have to be modified as well. ;; BEWARE there is a glitch: when you load a page first time, it'S ;; often empty. Dunno why. Press C-c RET to refesh. The second load ;; usually doesn't fail. ;;; Thanks: ;; Vebjorn Ljosa for CLiki support. ;; This file is not part of GNU Emacs. ;; This 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, or (at your option) any later ;; version. ;; ;; This 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 GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, ;; MA 02111-1307, USA. ;; This package allows you to load and save pages from a remote wiki ;; using w3 functionality. See `wiki-types' for a list of supported ;; wiki engines. Just call `wiki-remote-get' and enter page name and ;; wiki name. In order to save, hit C-c C-c. ;; Thanks to Simon Michael for his zwiki-mode.el which inspired this. ;; Sample key binding for .emacs: ;; (autoload 'wiki-remote-get "wiki-remote" "Find page from Wiki.") ;; (global-set-key "\C-x\C-d" 'wiki-remote-get) ;;; Code: (require 'easy-mmode); for easy-mmode-define-minor-mode (require 'thingatpt); for word-at-point (require 'hexl) (defvar http-get-version "1.0.3") ;;Proxy (defvar http-proxy-host nil "*If nil dont use proxy, else name of proxy server.") (defvar http-proxy-port nil "*Port number of proxy server. Default is 80.") ;; Filtering (defvar http-filter-pre-insert-hook nil "Hook run by the `http-filter'. This is called whenever a chunk of input arrives, before it is inserted into the buffer. If you want to modify the string that gets inserted, modify the variable `string' which is dynamically bound to what will get inserted in the end. The string will be inserted at the process-mark, which you can get by calling \(process-mark proc). `proc' is dynamically bound to the process, and the current buffer is the very buffer where the string will be inserted. Note that `http-unchunk' does not deal gracefully with changed input -- so only change `string' if you know that `http-unchunk' will not run.") (defvar http-filter-post-insert-hook '(http-headers http-unchunk) "Hook run by the `http-filter'. This is called whenever a chunk of input arrives, after it has been inserted, but before the process-mark has moved. Therefore, the new text lies between the process-mark and point. You can get the value of the process-mark by calling \(process-mark proc). Please take care to leave point at the right place, eg. by wrapping your code in a `save-excursion'.") (defun http-filter (proc string) "Filter function for HTTP buffers. See `http-filter-pre-insert-hook' and `http-filter-post-insert-hook' for places where you can do your own stuff such as HTML rendering." (with-current-buffer (process-buffer proc) (let ((moving (= (point) (process-mark proc)))) (save-excursion ;; Insert the text, advancing the process marker. (goto-char (process-mark proc)) (run-hooks 'http-filter-pre-insert-hook) (insert string) (run-hooks 'http-filter-post-insert-hook) (set-marker (process-mark proc) (point))) (if moving (goto-char (process-mark proc)))))) ;; URL encoding for parameters (defun http-url-encode (str content-type) "URL encode STR using CONTENT-TYPE as the coding system." (apply 'concat (mapcar (lambda (c) (if (or (and (>= c ?a) (<= c ?z)) (and (>= c ?A) (<= c ?Z)) (and (>= c ?0) (<= c ?9))) (string c) (format "%%%02x" c))) (encode-coding-string str content-type)))) ;; Dealing with headers (defvar http-status-code nil "The status code returned for the current buffer. This is set by the function `http-headers'.") (defvar http-reason-phrase nil "The reason phrase returned for the `http-status-code'. This is set by the function `http-headers'.") (defvar http-headers nil "An alist of the headers that have been parsed and removed from the buffer. The headers are stored as an alist. This is set by the function `http-headers'.") (defun http-headers () "Check the current buffer for headers. If any are found, they are stored in the variable `http-headers'." (save-excursion (goto-char (point-min)) (when (looking-at "HTTP/[0-9.]+ \\([0-9]+\\) \\(.*\\)\n") (set (make-local-variable 'http-status-code) (string-to-number (match-string 1))) (set (make-local-variable 'http-reason-phrase) (match-string 2)) (when (search-forward "\n\n") (set (make-local-variable 'http-headers) (mapcar (lambda (line) (if (string-match ": " line) (cons (substring line 0 (match-beginning 0)) (substring line (match-end 0))) line)) (split-string (buffer-substring (point-min) (point)) "\n"))) (delete-region (point-min) (point)) (message http-reason-phrase) http-status-code)))) ;; Dealing with the chunked encoding (defvar http-unchunk-chunk-size 0 "Size of the current unfinished chunk.") (make-variable-buffer-local 'http-unchunk-chunk-size) (defun http-unchunk () "Undo the chunking transfer encoding. The counter http-unchunk-chunk-size says how much content we still have to expect. Whenever a chunk-size is received, it is increased, whenever we insert text into the buffer, it is decreased. This function assumes that we are at the beginning of the buffer when chunking starts -- as we should be, if `http-headers' runs before us." (when (string= "chunked" (cdr (assoc "Transfer-Encoding" http-headers))) (save-excursion (catch 'done (goto-char (process-mark proc)) (while (< (point) (point-max)) (when (> http-unchunk-chunk-size 0) (when (> (- (buffer-size) (point)) http-unchunk-chunk-size) (setq http-unchunk-chunk-size (- http-unchunk-chunk-size (- (buffer-size) (point)))) (throw 'done t)) (forward-char http-unchunk-chunk-size) (setq http-unchunk-chunk-size 0)) (when (looking-at "\\([0-9a-f]\\).*?\r\n") (setq http-unchunk-chunk-size (hexl-hex-string-to-integer (match-string 1))) (replace-match "") (when (= 0 http-unchunk-chunk-size) (setq http-headers (delete (assoc "Transfer-Encoding" http-headers) http-headers)) (throw 'done t))) ;; hm, need to deal with the case that we are still here: ;; this still leaves some output at the end. ;;(message "How can we still be here in http-unchunk?") (if (< (point) (point-max)) (forward-char 1) (throw 'done t))))))) ;; Debugging (defvar http-log-function 'ignore "Function to call for log messages.") (defun http-log (str) "Log STR using `http-log-function'. The default value just ignores STR." (funcall http-log-function str)) (defun http-get-debug (url &optional headers version) "Debug the call to `http-get'." (interactive "sURL: ") (let* ((http-log-function (lambda (str) (save-excursion ;; dynamic binding -- buf from http-get is used (set-buffer buf) (insert str)))) proc) (when (get-buffer "*Debug HTTP-GET*") (kill-buffer "*Debug HTTP-GET*")) (setq proc (http-get url headers nil version)) (set (make-local-variable 'http-filter-pre-insert-hook) nil) (set (make-local-variable 'http-filter-post-insert-hook) nil) (rename-buffer "*Debug HTTP-GET*"))) ;;;###autoload (defun http-exec (buffer-setup method url &optional auth input headers version bufname content-type ) "Get URL in a buffer, and return the process. You can get the buffer associated with this process using `process-buffer'. The optional HEADERS are an alist where each element has the form \(NAME . VALUE). Both must be strings and will be passed along with the request. With optional argument SENTINEL, the buffer is not shown. It is the responsability of the sentinel to show it, if appropriate. A sentinel function takes two arguments, process and message. It is called when the process is killed, for example. This is useful when specifying a non-persistent connection. By default, connections are persistent. Add \(\"Connection\" . \"close\") to HEADERS in order to specify a non-persistent connection. Usually you do not need to specify a sentinel, and `ignore' is used instead, to prevent a message being printed when the connection is closed. If you want to filter the content as it arrives, bind `http-filter-pre-insert-hook' and `http-filter-post-insert-hook'. The optional argument VERSION specifies the HTTP version to use. It defaults to version 1.0, such that the connection is automatically closed when the entire document has been downloaded. This will then call SENTINEL, if provided. If no sentinel is provided, `ignore' will be used in order to prevent a message in the buffer when the process is killed. CONTENT-TYPE is a coding system to use for the encoding of the url param value. Its upper case print name will be used for the server. Possible values are `iso-8859-1' or `euc-jp' and others. The coding system of the process is set to `unix', because we need to distinguish between \\r and \\n. To correctly decode the text later, use `decode-coding-region' and get the coding system to use from `http-headers'." (interactive "sURL: ") (setq version (or version 1.1)) (let* ((user (and auth (car auth))) (passwd (and auth (cdr auth))) host file port proc buf command start-line (message-headers "")) (if (string-match "http://\\([^/:]+\\)\\(:[0-9]+\\)?\\(/.*\\)" url) (setq host (match-string 1 url) port (or (match-string 2 url) ":80") file (match-string 3 url)) (if (string-match "http://\\([^:]+\\):\\([^@]+\\)@\\([^/:]+\\)\\(:\\([0-9]+\\)\\)?\\(/.*\\)" url) (setq user (match-string 1 url) passwd (match-string 2 url) host (match-string 3 url) port (or (match-string 4 url) ":80") file (match-string 6 url)) (error "Cannot parse URL %s" url))) (unless bufname (setq bufname (format " *HTTP %s %s *" method url))) (setq port (string-to-number (substring port 1 (length port))) buf (get-buffer-create bufname) proc (open-network-stream (concat "HTTP " method " " url) buf (if http-proxy-host http-proxy-host host) (if http-proxy-port http-proxy-port port) )) (set-buffer buf) (erase-buffer) ;; (kill-all-local-variables) (if content-type (setq file (replace-regexp-in-string "=[^&]+" (lambda (param) (concat "=" (http-url-encode (substring param 1) content-type) )) file ) ) ) (cond ((= version 1.1) (setq start-line (format "%s %s%s HTTP/1.1\r\nHost: %s%s" method (if http-proxy-host (concat "http://" host "") "") file host (if (not (= port 80)) (concat ":" (number-to-string port)) "")))) ((= version 1.0) (setq start-line (format "%s %s%s HTTP/1.0" method (if http-proxy-host (concat "http://" host "") "") file))) (t (error "HTTP Version %S is not supported" version))) (setq message-headers (mapconcat (lambda (pair) (concat (car pair) ": " (cdr pair))) (if user (cons (cons "Authorization" (concat "Basic " (base64-encode-string (concat user ":" passwd)))) headers) headers) "\r\n")) (setq command (concat start-line (if (string-equal message-headers "") "" "\r\n") message-headers "\r\n\r\n" input)) (http-log (format "Connecting to %s %d\nCommand:\n%s\n" host port command)) (set (make-local-variable 'http-command) (list method url auth input headers)) (set (make-local-variable 'http-setup) buffer-setup) (set-process-sentinel proc 'http-process-senitel) (set-process-coding-system proc 'binary) ; we need \r\n in the headers! ; (set-process-filter proc 'http-filter) (set-marker (process-mark proc) (point-max)) (process-send-string proc command))) (defvar http-command nil) (make-local-variable 'http-command) (defun http-auth-info () (and http-command (caddr http-command))) (defvar http-setup nil) (make-local-variable 'http-setup) (defun http-process-senitel (process status) (let ((setup nil) (buf (process-buffer process))) (if (save-excursion (set-buffer buf) ;; Pop the setup closure. (setq setup http-setup) (goto-char (point-min)) (while (search-forward "\r\n" nil t) (replace-match "\n")) (http-headers) (cond ((= http-status-code 200) t) ((= http-status-code 401) (let* ((user (read-string "User: " (caaddr http-command))) (passwd (if (and (string= user (caaddr http-command)) (cdaddr http-command)) (cdaddr http-command) (read-passwd "Password: ")))) (set (make-local-variable 'http-command) (append (list (car http-command) (cadr http-command) (cons user passwd)) (cdddr http-command))) (apply 'http-exec setup http-command)) nil)) (process-buffer process)) ;; Tail call the continuation upon success. (when setup (apply (car setup) buf (cdr setup)))))) (defun http-get (url &optional auth headers buffer-setup) "Get URL in a buffer, and return the buffer." (apply 'http-exec buffer-setup "GET" url auth nil headers nil)) (defun http-post (url input &optional auth headers buffer-setup) "Get URL in a buffer, and return the buffer." (apply 'http-exec buffer-setup "POST" url auth input headers nil)) ;; Customization (defgroup wiki-remote nil "Options concerning the editing of pages on remote wikis. These pages are loaded and saved via the Internet; possibly via the http or via ftp -- that depens upon the type of wiki engine running the remote site." :group 'wiki) (defcustom wiki-remote-setup-function 'ignore "Function to setup the wiki buffer when editing a remote wiki page. Note that these functions should not be major modes. A major mode will kill all local variables, something that might prevent some wiki types from working." :type '(choice (const ignore) (const wiki-mode) (const emacs-wiki-mode) (function)) :group 'wiki-remote) (defcustom wiki-remote-types '((usemod usemod-get-page usemod-put-page) (zwiki zwiki-get-page zwiki-put-page) (cliki cliki-get-page cliki-put-page) (askemos askemos-get-page askemos-put-page) (askemos-debug askemos-get-body askemos-put-body)) "A list of wiki types supported and the functions used. Each element has the form (TYPE GET PUT). TYPE is a symbol used in `wiki-remote-alist' to denote the wiki engine. GET is a function must accept the parameters PAGENAME and URL, and it should return a buffer with the requested plain text in it. PUT is a function that must accept paramaters PAGENAME, URL, CONTENT, and save the content as the new pagename. Usually both GET and PUT will use `url-retrieve' to do their job." :type '(repeat (list :tag "Type" (symbol :tag "Name") (function :tag " Get") (function :tag " Put"))) :group 'wiki-remote) (defcustom wiki-remote-alist '(("Local" "http://localhost:7081/" askemos-debug) ("Emacs" "http://www.emacswiki.org/cgi-bin/wiki.pl" usemod) ("UseMod" "http://www.usemod.com/cgi-bin/wiki.pl" usemod) ("Meatball" "http://www.usemod.com/cgi-bin/mb.pl" usemod) ("Zwiki" "http://joyful.com/zwiki/" zwiki) ("Cliki" "http://ww.telent.net/cliki/" cliki) ("Askemos.org" "http://www.askemos.org:7081/A849640f672ed0df0958abc0712110f3c/" askemos) ("Garkin" "http://localhost:7091/" askemos-debug) ("Ajax" "http://localhost:7181/" askemos-debug) ("Askemos" "http://localhost:7081/Askemos/" askemos) ("vswg" "http://localhost:7181/vswg/" askemos) ("nunu" "http://localhost:7181/nunu/" askemos) ("stylelib" "http://localhost:7181/stylelib/" askemos) ("privates" "http://localhost:7181/privates/" askemos)) "An alist where each element has the form (NAME URL TYPE). NAME is the name of the wiki. The URL is the base URL for a wiki. If the URL of a page is http://www.usemod.com/cgi-bin/wiki.pl?RecentChanges then the URL you want is http://www.usemod.com/cgi-bin/wiki.pl. TYPE is a symbol. It must be a symbol from `wiki-remote-types'." :type `(repeat (list :tag "Wiki" (string :tag "Name") (string :tag " URL") (choice :tag "Type" ,@(mapcar (lambda (i) (list 'const (car i))) wiki-remote-types)))) :group 'wiki-remote) (defvar wiki-remote-debug nil "*Debug wiki-remote operations.") ;; Accessor functions (defun wiki-remote-name () "Query the user for a remote wiki name. See also the variable `wiki-remote-name'." (completing-read "Wiki: " wiki-remote-alist)) (defun wiki-remote-name-by-url (url) "Determine the wiki name given the URL See also the variable `wiki-remote-alist'." (let ((alist (mapcar (lambda (wiki) (cons (nth 1 wiki) (nth 0 wiki))) wiki-remote-alist))) (cdr (assoc url alist)))) (defun wiki-remote-url (name) "Return URL for NAME, based on `wiki-remote-alist'." (nth 1 (assoc name wiki-remote-alist))) ; (wiki-remote-url (wiki-remote-name)) (defun wiki-remote-type (name) "Return wiki type for NAME, based on `wiki-remote-alist'." (nth 2 (assoc name wiki-remote-alist))) ; (wiki-remote-type (wiki-remote-name)) (defun wiki-remote-get-function (type) "Return get function for wiki TYPE, based on `wiki-remote-types'." (nth 1 (assq type wiki-remote-types))) ; (wiki-remote-get-function 'usemod) (defun wiki-remote-put-function (type) "Return put function for wiki TYPE, based on `wiki-remote-types'." (nth 2 (assq type wiki-remote-types))) ; (wiki-remote-put-function 'usemod) ;; Code (defvar wiki-remote-name nil "Name of the wiki currently visited. This should be a name from `wiki-remote-alist'.") (make-variable-buffer-local 'wiki-remote-name) ;; Minor mode (defvar wiki-remote-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c C-c") 'wiki-remote-put) (define-key map (kbd "C-c RET") 'wiki-remote-refresh) map) "Keymap used by `wiki-remote-mode'.") (easy-mmode-define-minor-mode wiki-remote-mode "In `wiki-remote-mode' you can put the page visited back to wiki server using \\[wiki-remote-put]. You can get new pages from the remote server using \\[wiki-remote-get]. Use `wiki-remote-mode-on-hook' to add customizations." nil " Remote" wiki-remote-mode-map) ;; Interfacing with wiki-mode (add-hook 'wiki-mode-on-hook 'wiki-remote-maybe) (defun wiki-remote-maybe () "If the page is a remote page, set `wiki-follow-name-action'." (when wiki-remote-mode (set (make-local-variable 'wiki-follow-name-action) 'wiki-remote-get-nondirectory))) (defun wiki-remote-get-nondirectory (page) "Strip current directory from PAGE and call `wiki-remote-get'." (wiki-remote-get (file-name-nondirectory page))) (defun wiki-remote-setup-get (buf page name point) "The actual setup code, close around valiables and parameters." (let ((cmd (progn (set-buffer buf) http-command))) (set-buffer (get-buffer-create page)) (erase-buffer) (insert-buffer buf) (kill-buffer buf) (normal-mode) (funcall wiki-remote-setup-function) ;; FIXME there should be a better way to keep passwords in emacs. ;; I just don't know any. (set (make-local-variable 'http-command) cmd) (set (make-variable-buffer-local 'wiki-remote-name) name)) (wiki-remote-mode) (set-buffer-modified-p nil) (switch-to-buffer page) (buffer-enable-undo) (goto-char point)) ;; Main function to call (defun wiki-remote-get (page &optional name) "Get wiki PAGE from wiki NAME. If optional argument URL is not given, it defaults to `wiki-url'. This variable is buffer local and holds the URL of the current buffer. The default will therefore get pages from the same buffer as the current page." (interactive (let* ((str (word-at-point)) (val (read-from-minibuffer (if str (format "Get page (default %s): " str) "Get page: ") str))) (list val))) (let* ((old (get-buffer page)) (buf (get-buffer-create page)) (oldname (or name wiki-remote-name (wiki-remote-name))) (cmd http-command)) (set-buffer buf) (switch-to-buffer buf) (set (make-variable-buffer-local 'wiki-remote-name) oldname) (set (make-variable-buffer-local 'http-command) cmd) (wiki-remote-refresh))) (defun wiki-remote-refresh () "Refresh the current page." (interactive) (save-excursion (let ((name wiki-remote-name) (page (buffer-name))) (funcall (wiki-remote-get-function (or (wiki-remote-type name) (error "Wiki name `%s' not defined." name))) page (wiki-remote-url name) (list 'wiki-remote-setup-get page name (point)))))) (defun wiki-remote-setup-put (buf page) "Setup after put." (when (not (wiki-remote-simple-verify-page buf page)) (error "Save could not be verified")) (kill-buffer buf) (set-buffer page) (set-buffer-modified-p nil)) (defun wiki-remote-put (&optional name) "Save current buffer back to the remote wiki. The remote wiki can be specified via the optional argument NAME. If NAME is not given, `wiki-remote-name' is used instead." (interactive) (let* ((name (or name wiki-remote-name (wiki-remote-name))) (url (wiki-remote-url name)) (type (wiki-remote-type name)) (page (buffer-name)) (put-func (wiki-remote-put-function type)) (content (buffer-substring-no-properties (point-min) (point-max)))) (funcall put-func page url content (list 'wiki-remote-setup-put page)))) (defun wiki-remote-simple-verify-page (buf page) "Verify that buffer BUF actually contains PAGE. This should catch all errors caused by the wiki engine that do not result in a HTTP error." (save-excursion (set-buffer buf) (goto-char (point-min)) (search-forward page nil t))) (defun wiki-remote-massage (old new from to) "Massage buffer replacing the regexp OLD by string NEW. Do this only between FROM and TO. To may be a number or a marker." (let ((start from) (end (copy-marker to))) (goto-char start) (while (re-search-forward old end t) (replace-match new)))) ;; w3m support (defun wiki-remote-get-w3m () "Edit the current URL displayed in w3m." (interactive) (if (not w3m-current-url) (error "Not in a w3m buffer") (let ((params (split-string w3m-current-url "?")) page url) (if (not (= 2 (length params))) (error "Not a wiki URL of the form http://some.host/script?page") (setq page (nth 1 params) url (nth 0 params)) (wiki-remote-get page (wiki-remote-name-by-url url)))))) ;; UseMod support (defvar usemod-data nil "Data to store between reading and writing the page.") (make-variable-buffer-local 'usemod-data) (defun usemod-get-page (page url) "Return a buffer with the plain text contents of PAGE at URL." (let* ((data (usemod-get-data page url)) (text (cdr (assq 'text data))) (buf (get-buffer-create page))) (set-buffer buf) (erase-buffer) (insert text) (setq usemod-data data) buf)) (defun usemod-refresh (page &optional name) "Refresh the current page." (interactive (let* ((str (or (file-name-nondirectory (buffer-file-name)) (buffer-name))) (val (read-from-minibuffer (format "Get page (default %s): " str) str))) (list val))) (let* ((name (or name wiki-remote-name (wiki-remote-name))) (url (wiki-remote-url name))) (set (make-variable-buffer-local 'wiki-remote-name) name) (setq usemod-data (usemod-get-data page url))) (wiki-remote-mode 1)) (defun usemod-diff-current () "Diff current buffer with meta data." (interactive) (when (null usemod-data) (error "Meta data missing: Must get this page before diffing")) (when (not (buffer-file-name)) (error "Save this buffer to a file, first")) (save-buffer) (let ((text (cdr (assq 'text usemod-data))) (file (make-temp-name "/tmp/usemod"))) (with-temp-buffer (insert text) (write-file file)) (diff file (buffer-file-name)))) (defun usemod-get-data (page url) "Do the ugly work." (message "Reading %s at %s" page url) (save-excursion (let ((buf (cdr (url-retrieve (concat url "?action=edit&id=" (url-hexify-string page)))))) (when (null buf) (error "Could not retrieve %s" url)) (when wiki-remote-debug (switch-to-buffer-other-window buf)) (set-buffer buf) (let* ((oldtime (progn (goto-char (point-min)) (search-forward-regexp "NAME=\"oldtime\" VALUE=\"\\([0-9]+\\)\"") (match-string 1))) (oldconflict (progn (goto-char (point-min)) (search-forward-regexp "NAME=\"oldconflict\" VALUE=\"\\([0-9]+\\)\"") (match-string 1))) (text (progn (goto-char (point-min)) (let ((start (search-forward-regexp "NAME=\"text\".*>")) end) (search-forward "") (setq end (copy-marker (match-beginning 0))) (mapcar (lambda (pair) (wiki-remote-massage (car pair) (cdr pair) start end)) '((">" . ">") ("<" . "<") (""" . "\"") ("" . ""))) (buffer-substring start end))))) `((text . ,text) (title . ,page) (oldtime . ,oldtime) (oldconflict . ,oldconflict)))))) (defun usemod-put-page (page url content) "Save buffer to wiki at URL with new CONTENT. The PAGE name is ignored because the title is already stored in `usemod-data'. If that variable is nil, saving is not possible. Queries for summary interactively and uses that as a summary line when saving." (when (null usemod-data) (error "Meta data missing: Must get this page before editing")) (let ((url-request-method "POST") (url-request-data (concat "title=" (url-hexify-string page) "&text=" (url-hexify-string content) "&summary=" (url-hexify-string (read-from-minibuffer "Summary: " "*")) (if (y-or-n-p "Is this a small edit? ") "&recent_edit=on" "") "&oldtime=" (cdr (assq 'oldtime usemod-data)) "&oldconflict=" (cdr (assq 'oldconflict usemod-data)))) ;; Actually we don't want to see the result of the redirection! ;; Therefore, just ignore it. (url-inhibit-mime-parsing t) (request url)) (message "Saving...") (let ((buf (cdr (url-retrieve request)))) (message "Verifying...") (when wiki-remote-debug (switch-to-buffer-other-window buf)) (when (not (wiki-remote-simple-verify-page buf page)) (error "Save could not be verified")))) (message "Getting new meta data...") (setq usemod-data (usemod-get-data page url)) (when wiki-remote-debug (switch-to-buffer-other-window (cdr (assq 'buf usemod-data))))) ;; Askemos' NuNu support (defun askemos-get-page (page url senitel) "Return a buffer with the plain text contents of PAGE at URL." (http-get (concat url page "?template=text") (http-auth-info) nil ; headers senitel)) (defun askemos-put-page (page url content senitel) "Save buffer to wiki at URL with new CONTENT." (interactive) (unless (buffer-modified-p) (error "No changes need to be saved")) (let ((buffer-to-be-saved (current-buffer))) (save-excursion (let ((url-request-method "POST") (url-request-data (concat "---separator\nContent-Disposition: form-data; name=\"text\"\n\n" content "\n---separator\nContent-Disposition: form-data; name=\"action\"\n\n" "write" "\n---separator--\n")) (url-request-extra-headers '(("Content-type" . "multipart/form-data; boundary=-separator"))) (request (concat url page))) (http-post request url-request-data (http-auth-info) url-request-extra-headers senitel))))) (defun askemos-get-body (page url senitel) "Return a buffer with the plain text contents of PAGE at URL." (http-get (concat url page "?xmlns=mind&mode=edit&template=text") (http-auth-info) nil ; headers senitel)) (defun askemos-put-body (page url content senitel) "Save buffer to wiki at URL with new CONTENT." (interactive) (unless (buffer-modified-p) (error "No changes need to be saved")) (let ((buffer-to-be-saved (current-buffer))) (save-excursion (let ((url-request-method "POST") (url-request-data (concat "---separator\nContent-Disposition: form-data; name=\"text\"\n\n" content "\n---separator\nContent-Disposition: form-data; name=\"xmlns\"\n\n" "mind" "\n---separator\nContent-Disposition: form-data; name=\"action\"\n\n" "change" "\n---separator--\n")) (url-request-extra-headers '(("Content-type" . "multipart/form-data; boundary=-separator"))) (request (concat url page))) (http-post request url-request-data (http-auth-info) url-request-extra-headers senitel))))) ;; Zwiki support (defun zwiki-get-page (page url) "Return a buffer with the plain text contents of PAGE at URL." (cdr (url-retrieve (concat url page "/text")))) (defun zwiki-put-page (page url content) "Save buffer to wiki at URL with new CONTENT." (interactive) (unless (buffer-modified-p) (error "No changes need to be saved")) (let ((url-request-method "POST") (url-request-data (concat "---separator\nContent-Disposition: form-data; name=\"text\"\n\n" content "\n---separator--\n")) (url-request-extra-headers '(("Content-type" . "multipart/form-data; boundary=-separator"))) (request (concat url page "/edit"))) (let ((buf (cdr (url-retrieve request)))) (when wiki-remote-debug (switch-to-buffer-other-window buf)) (when (not (wiki-remote-simple-verify-page buf page)) (error "Save could not be verified"))))) ;; Cliki support (defvar cliki-author-name nil "Name of author; for the Recent Changes page.") (defvar cliki-data nil "Data to store between reading and writing the page.") (make-variable-buffer-local 'cliki-data) (defun cliki-get-page (page url) "Return a buffer with the plain text contents of PAGE at URL." (let* ((data (cliki-get-data page url)) (text (cdr (assq 'text data))) (buf (get-buffer-create page))) (set-buffer buf) (erase-buffer) (insert text) (setq cliki-data data) buf)) (defun cliki-get-data (page url) "Do the ugly work." (message "Reading %s at %s" page url) (save-excursion (let ((buf (cdr (url-retrieve (concat url (url-hexify-string page) "?edit"))))) (when (null buf) (error "Could not retrieve %s" url)) (when wiki-remote-debug (switch-to-buffer-other-window buf)) (set-buffer buf) (let* ((page (progn (goto-char (point-min)) (let ((start (search-forward "CLiki : Edit ``")) end) (search-forward "''") (setq end (copy-marker (match-beginning 0))) (buffer-substring start end)))) (text (progn (goto-char (point-min)) (let ((start (search-forward "") (setq end (copy-marker (match-beginning 0))) (mapcar (lambda (pair) (wiki-remote-massage (car pair) (cdr pair) start end)) '((">" . ">") ("<" . "<") (""" . "\"") ("" . ""))) (buffer-substring start end))))) `((text . ,text) (title . ,page)))))) (defun cliki-put-page (page url content) "Save buffer to wiki at URL with new CONTENT. The PAGE name is ignored because the title is already stored in `cliki-data'. If that variable is nil, saving is not possible. Queries for summary interactively and uses that as a summary line when saving." (when (null cliki-data) (error "Meta data missing: Must get this page before editing")) (let ((url-request-method "POST") (url-request-data (concat "name=" (or cliki-author-name (setf cliki-author-name (read-from-minibuffer "Your name: " ""))) "&text=" (url-hexify-string content) "&summary=" (url-hexify-string (read-from-minibuffer "Summary: " "*")))) ;; Actually we don't want to see the result of the redirection! ;; Therefore, just ignore it. (url-inhibit-mime-parsing t) (request (concat url (url-hexify-string page)))) (message "Saving...") (let ((buf (cdr (url-retrieve request)))) (message "Verifying...") (when wiki-remote-debug (switch-to-buffer-other-window buf)) (when (not (wiki-remote-simple-verify-page buf page)) (error "Save could not be verified")))) (message "Getting new meta data...") (setq cliki-data (cliki-get-data page url)) (when wiki-remote-debug (switch-to-buffer-other-window (cdr (assq 'buf cliki-data))))) (provide 'wiki-remote) ;;; wiki-remote.el ends here