DokuWiki

It's better when it's simple

User Tools

Site Tools


tips:edit_dokuwiki_with_text_editors_using_fuse_and_python

Here's some Python source that implements basic FUSE support for editing Wiki pages. It's horribly incomplete and almost fully untested, but appears to work for my testcases. Feel free to extend. No warranty, explicitly reject any possible claims of copyright, etc. Have fun!

#!/usr/bin/env python
 
# This file was originally xmp.py
# Now, only the header survives.
 
import os, sys
from errno import *
from stat import *
from urllib2 import *
from urllib import urlencode
from cookielib import CookieJar
from array import array
from BeautifulSoup import BeautifulStoneSoup
import fcntl
 
import fuse
from fuse import Fuse
 
 
if not hasattr(fuse, '__version__'):
    raise RuntimeError, \
        "your fuse-py doesn't know of fuse.__version__, probably it's too old."
 
fuse.fuse_python_api = (0, 2)
 
# We use a custom file class
fuse.feature_assert('stateful_files', 'has_init')
 
def path2url(path):
	murl = path[1:].split("/")
	return "http://"+"/".join(murl[:-1])+"/doku.php?id="+murl[-1][1:]+"&do=edit"
 
def between(text, a, b):
	apos = text.find(a)
	if apos == -1: return None
	text = text[apos + len(a):]
	bpos = text.find(b)
	if bpos == -1: return None
	return text[:bpos]
 
def acquire(url):
	data = urlopen(url).read()
	res = array('c')
	res.fromstring((BeautifulStoneSoup(data, fromEncoding="utf-8", convertEntities = BeautifulStoneSoup.HTML_ENTITIES).find('textarea', id='wiki__text').contents[0][1:]).encode("utf-8"))
	return res
 
def post(url, posturl, data):
	cook = CookieJar()
	op = build_opener(HTTPCookieProcessor(cook))
	page = op.open(url).read()
	bs = BeautifulStoneSoup(page, convertEntities = BeautifulStoneSoup.HTML_ENTITIES)
	values={}
	def copy(name):
		values[name] = bs.find('input', attrs={'name': name})['value']
	copy('sectok')
	copy('id')
	copy('date')
	copy('prefix')
	copy('suffix')
	copy('changecheck')
	values['wikitext'] = data
	values['do[save]'] = 'Save'
	values['summary'] = ''
	values['minoredit'] = None
	return op.open(Request(posturl, urlencode(values))).read()
 
# was Xmp, shamelessly ripped off
class DokuFucker(Fuse):
 
    def __init__(self, *args, **kw):
 
        Fuse.__init__(self, *args, **kw)
	self.file_class = self.DokuPage
	self.removed = {}
 
    def getattr(self, path):
	# print "O hai '{0}' has {1}".format(path, path.find("/?"))
	if (path.find("/?") != -1):
		t = fuse.Stat()
		t.st_mode = S_IFREG | 0444
		t.st_dev = 0
		t.st_blksize = 0
		t.st_nlink = 1
		# masked for recreation
		if path in self.removed:
			t.st_size = 0
			del self.removed[path]
		else: t.st_size = len(acquire(path2url(path)))
		return t
	else:
		t = fuse.Stat()
		t.st_mode = S_IFDIR | 0755
		t.st_dev = 0
		t.st_blksize = 0
		t.st_nlink = 2
		t.st_size = 0
		return t
 
    def readdir(self, path, offset):
	if path == "/":
		yield fuse.Direntry(".")
		yield fuse.Direntry("..")
		yield fuse.Direntry("test2")
 
    def chmod(self, path, mode):
        return 0 # No-op
 
    def chown(self, path, user, group):
        return 0 # No-op
 
    def truncate(self, path, len):
	# print "Trying to truncate {0} to {1}".format(path, len)
	self.removed[path] = True
	return 0
 
    def unlink(self, path):
	self.removed[path] = True
 
    class DokuPage(object):
 
        def __init__(self, path, flags, *mode):
		# print "Path: " + path
		murl = path[1:].split("/")
		self.url = "http://"+"/".join(murl[:-1])+"/doku.php?id="+murl[-1][1:]+"&do=edit"
		self.posturl = "http://"+"/".join(murl[:-1])+"/doku.php"
		if path in server.removed:
			self.data = ''
			del server.removed[path]
		else:
			self.data = acquire(self.url)
		self.modified = False
		# print "URL: {0}; length {1}".format(self.url, len(self.data))
 
        def read(self, length, offset):
		if offset > len(self.data): return ""
		if offset <= len(self.data) and offset + length > len(self.data): length = len(self.data) - offset
		# print "Read {0} at {1} from {2}".format(length, offset, len(self.data))
		return self.data[offset:offset+length].tostring()
 
        def write(self, buf, offset):
		# if we already have data and try to overwrite it
		# or if we don't have data but try to start at an offset
		# bad things happen
		if (bool(len(self.data)) ^ bool(offset)) or len(self.data) != offset:
			if not offset:
				print "It is my hope that this is the editor starting over. "
				print "Based on that hope I will now erase my internal datastore. "
				print "If I'm wrong, you get data loss. "
				print "So you'd better hope I'm right. >_>"
				self.data = array('c')
			else:
				print "This mode cannot be supported safely! {0} into {1} at {2} ".format(len(buf), len(self.data), offset)
				print "Evidently, my hope that modern editors don't do this, was misplaced. "
				return -EINVAL
 
		self.modified = True
		self.data.extend(buf);
		return len(buf)
 
	def flush(self):
		# POST now
		if self.modified:
			res = post(self.url, self.posturl, self.data.tostring())
 
server = DokuFucker(version="%prog " + fuse.__version__)
server.parse(values=server, errex=1)
server.main()
tips/edit_dokuwiki_with_text_editors_using_fuse_and_python.txt · Last modified: 2010-04-11 09:19 by 79.213.76.201

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki