]> git.ozlabs.org Git - patchwork/commitdiff
Add 'update' method to pwclient
authorJeremy Kerr <jk@ozlabs.org>
Mon, 8 Sep 2008 23:28:33 +0000 (09:28 +1000)
committerJeremy Kerr <jk@ozlabs.org>
Mon, 8 Sep 2008 23:28:33 +0000 (09:28 +1000)
This requires a new xmlrpc function, 'patch_set'. To do this, we
need HTTP Authentication support, which means changing to a custom
XMLRPC distpatcher that is aware of the Authorization: header.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
apps/patchwork/bin/pwclient.py
apps/patchwork/urls.py
apps/patchwork/views/xmlrpc.py [new file with mode: 0644]
apps/patchwork/xmlrpc.py [deleted file]
apps/settings.py
apps/urls.py
docs/INSTALL

index a50df732bbfd61437bcef5378db06c618db55cba..083108662cf55e229df28a3c41f6c38a67db5359 100755 (executable)
@@ -26,6 +26,7 @@ import getopt
 import string
 import tempfile
 import subprocess
+import base64
 import ConfigParser
 
 # Default Patchwork remote XML-RPC server URL
@@ -78,6 +79,24 @@ class Filter:
         """Return human-readable description of the filter."""
         return str(self.d)
 
+class BasicHTTPAuthTransport(xmlrpclib.Transport):
+
+    def __init__(self, username = None, password = None):
+        self.username = username
+        self.password = password
+        xmlrpclib.Transport.__init__(self)
+
+    def authenticated(self):
+        return self.username != None and self.password != None
+
+    def send_host(self, connection, host):
+        xmlrpclib.Transport.send_host(self, connection, host)
+        if not self.authenticated():
+            return
+        credentials = '%s:%s' % (self.username, self.password)
+        auth = 'Basic ' + base64.encodestring(credentials).strip()
+        connection.putheader('Authorization', auth)
+
 def usage():
     sys.stderr.write("Usage: %s <action> [options]\n\n" % \
                         (os.path.basename(sys.argv[0])))
@@ -235,9 +254,39 @@ def action_apply(rpc, patch_id):
         sys.stderr.write("Error: No patch content found\n")
         sys.exit(1)
 
+def action_update_patch(rpc, patch_id, state = None, commit = None):
+    patch = rpc.patch_get(patch_id)
+    if patch == {}:
+        sys.stderr.write("Error getting information on patch ID %d\n" % \
+                         patch_id)
+        sys.exit(1)
+
+    params = {}
+
+    if state:
+        state_id = state_id_by_name(rpc, state)
+        if state_id == 0:
+            sys.stderr.write("Error: No State found matching %s*\n" % state)
+            sys.exit(1)
+        params['state'] = state_id
+
+    if commit:
+        params['commit_ref'] = commit
+
+    success = False
+    try:
+        success = rpc.patch_set(patch_id, params)
+    except xmlrpclib.Fault, f:
+        sys.stderr.write("Error updating patch: %s\n" % f.faultString)
+
+    if not success:
+        sys.stderr.write("Patch not updated\n")
+
+auth_actions = ['update']
+
 def main():
     try:
-        opts, args = getopt.getopt(sys.argv[2:], 's:p:w:d:n:')
+        opts, args = getopt.getopt(sys.argv[2:], 's:p:w:d:n:c:')
     except getopt.GetoptError, err:
         print str(err)
         usage()
@@ -252,6 +301,8 @@ def main():
     submitter_str = ""
     delegate_str = ""
     project_str = ""
+    commit_str = ""
+    state_str = ""
     url = DEFAULT_URL
 
     config = ConfigParser.ConfigParser()
@@ -266,13 +317,15 @@ def main():
 
     for name, value in opts:
         if name == '-s':
-            filt.add("state", value)
+            state_str = value
         elif name == '-p':
             project_str = value
         elif name == '-w':
             submitter_str = value
         elif name == '-d':
             delegate_str = value
+        elif name == '-c':
+            commit_str = value
         elif name == '-n':
             try:
                 filt.add("max_count", int(value))
@@ -287,11 +340,29 @@ def main():
         sys.stderr.write("Too many arguments specified\n")
         usage()
 
+    (username, password) = (None, None)
+    transport = None
+    if action in auth_actions:
+        if config.has_option('auth', 'username') and \
+                config.has_option('auth', 'password'):
+
+            transport = BasicHTTPAuthTransport( \
+                    config.get('auth', 'username'),
+                    config.get('auth', 'password'))
+
+        else:
+            sys.stderr.write(("The %s action requires authentication, "
+                    "but no username or password\nis configured\n") % action)
+            sys.exit(1)
+
     if project_str:
         filt.add("project", project_str)
 
+    if state_str:
+        filt.add("state", state_str)
+
     try:
-        rpc = xmlrpclib.Server(url)
+        rpc = xmlrpclib.Server(url, transport = transport)
     except:
         sys.stderr.write("Unable to connect to %s\n" % url)
         sys.exit(1)
@@ -336,6 +407,16 @@ def main():
 
         action_apply(rpc, patch_id)
 
+    elif action == 'update':
+        try:
+            patch_id = int(args[0])
+        except:
+            sys.stderr.write("Invalid patch ID given\n")
+            sys.exit(1)
+
+        action_update_patch(rpc, patch_id, state = state_str,
+                commit = commit_str)
+
     else:
         sys.stderr.write("Unknown action '%s'\n" % action)
         usage()
index f7c942a3882fdfa5089132239848a0d55a191a1e..ef1f2adc88d557823f4ac20006a21ff6ca44e331 100644 (file)
@@ -58,6 +58,7 @@ urlpatterns = patterns('',
 
 if settings.ENABLE_XMLRPC:
     urlpatterns += patterns('',
+        (r'xmlrpc/$', 'patchwork.views.xmlrpc.xmlrpc'),
         (r'^pwclient.py/$', 'patchwork.views.pwclient'),
         (r'^project/(?P<project_id>[^/]+)/pwclientrc/$',
              'patchwork.views.pwclientrc'),
diff --git a/apps/patchwork/views/xmlrpc.py b/apps/patchwork/views/xmlrpc.py
new file mode 100644 (file)
index 0000000..f493cf7
--- /dev/null
@@ -0,0 +1,407 @@
+# Patchwork - automated patch tracking system
+# Copyright (C) 2008 Jeremy Kerr <jk@ozlabs.org>
+#
+# This file is part of the Patchwork package.
+#
+# Patchwork 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 of the License, or
+# (at your option) any later version.
+#
+# Patchwork 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 Patchwork; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Patchwork XMLRPC interface
+#
+
+from django.core.exceptions import ImproperlyConfigured
+from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
+from django.http import HttpResponse, HttpResponseRedirect, \
+     HttpResponseServerError
+from django.conf import settings
+from django.core import urlresolvers
+from django.shortcuts import render_to_response
+from django.contrib.auth import authenticate
+from patchwork.models import Patch, Project, Person, Bundle, State
+
+import sys
+import base64
+import xmlrpclib
+
+class PatchworkXMLRPCDispatcher(SimpleXMLRPCDispatcher):
+    def __init__(self):
+        if sys.version_info[:3] >= (2,5,):
+            SimpleXMLRPCDispatcher.__init__(self, allow_none=False,
+                    encoding=None)
+        else:
+            SimpleXMLRPCDispatcher.__init__(self)
+
+        # map of name => (auth, func)
+        self.func_map = {}
+
+
+    def register_function(self, fn, auth_required):
+        self.func_map[fn.__name__] = (auth_required, fn)
+
+
+    def _user_for_request(self, request):
+        if not request.META.has_key('HTTP_AUTHORIZATION'):
+            raise Exception("No authentication credentials given")
+
+        str = request.META.get('HTTP_AUTHORIZATION').strip()
+        if not str.startswith('Basic '):
+            raise Exception("Authentication scheme not supported")
+
+        str = str[len('Basic '):].strip()
+
+        try:
+            decoded = base64.decodestring(str)
+            username, password = decoded.split(':', 1)
+        except:
+            raise Exception("Invalid authentication credentials")
+
+        return authenticate(username = username, password = password)
+
+
+    def _dispatch(self, request, method, params):
+        if method not in self.func_map.keys():
+            raise Exception('method "%s" is not supported' % method)
+
+        auth_required, fn = self.func_map[method]
+
+        if auth_required:
+            user = self._user_for_request(request)
+            if not user:
+                raise Exception("Invalid username/password")
+
+            params = (user,) + params
+
+        return fn(*params)
+
+    def _marshaled_dispatch(self, request):
+        try:
+            params, method = xmlrpclib.loads(request.raw_post_data)
+
+            response = self._dispatch(request, method, params)
+            # wrap response in a singleton tuple
+            response = (response,)
+            response = xmlrpclib.dumps(response, methodresponse=1,
+                           allow_none=self.allow_none, encoding=self.encoding)
+        except xmlrpclib.Fault, fault:
+            response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
+                                       encoding=self.encoding)
+        except:
+            # report exception back to server
+            response = xmlrpclib.dumps(
+                xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)),
+                encoding=self.encoding, allow_none=self.allow_none,
+                )
+
+        return response
+
+dispatcher = PatchworkXMLRPCDispatcher()
+
+# XMLRPC view function
+def xmlrpc(request):
+    if request.method != 'POST':
+        return HttpResponseRedirect(
+                urlresolvers.reverse('patchwork.views.help',
+                    kwargs = {'path': 'pwclient/'}))
+
+    response = HttpResponse()
+    try:
+        ret = dispatcher._marshaled_dispatch(request)
+        response.write(ret)
+    except Exception, e:
+        return HttpResponseServerError()
+
+    return response
+
+# decorator for XMLRPC methods. Setting login_required to true will call
+# the decorated function with a non-optional user as the first argument.
+def xmlrpc_method(login_required = False):
+    def wrap(f):
+        dispatcher.register_function(f, login_required)
+        return f
+
+    return wrap
+
+
+
+# We allow most of the Django field lookup types for remote queries
+LOOKUP_TYPES = ["iexact", "contains", "icontains", "gt", "gte", "lt",
+                "in", "startswith", "istartswith", "endswith",
+                "iendswith", "range", "year", "month", "day", "isnull" ]
+
+#######################################################################
+# Helper functions
+#######################################################################
+
+def project_to_dict(obj):
+    """Return a trimmed down dictionary representation of a Project
+    object which is OK to send to the client."""
+    return \
+        {
+         'id'           : obj.id,
+         'linkname'     : obj.linkname,
+         'name'         : obj.name,
+        }
+
+def person_to_dict(obj):
+    """Return a trimmed down dictionary representation of a Person
+    object which is OK to send to the client."""
+    return \
+        {
+         'id'           : obj.id,
+         'email'        : obj.email,
+         'name'         : obj.name,
+         'user'         : str(obj.user),
+        }
+
+def patch_to_dict(obj):
+    """Return a trimmed down dictionary representation of a Patch
+    object which is OK to send to the client."""
+    return \
+        {
+         'id'           : obj.id,
+         'date'         : str(obj.date),
+         'filename'     : obj.filename(),
+         'msgid'        : obj.msgid,
+         'name'         : obj.name,
+         'project'      : str(obj.project),
+         'project_id'   : obj.project_id,
+         'state'        : str(obj.state),
+         'state_id'     : obj.state_id,
+         'submitter'    : str(obj.submitter),
+         'submitter_id' : obj.submitter_id,
+         'delegate'     : str(obj.delegate),
+         'delegate_id'  : max(obj.delegate_id, 0),
+         'commit_ref'   : max(obj.commit_ref, ''),
+        }
+
+def bundle_to_dict(obj):
+    """Return a trimmed down dictionary representation of a Bundle
+    object which is OK to send to the client."""
+    return \
+        {
+         'id'           : obj.id,
+         'name'         : obj.name,
+         'n_patches'    : obj.n_patches(),
+         'public_url'   : obj.public_url(),
+        }
+
+def state_to_dict(obj):
+    """Return a trimmed down dictionary representation of a State
+    object which is OK to send to the client."""
+    return \
+        {
+         'id'           : obj.id,
+         'name'         : obj.name,
+        }
+
+#######################################################################
+# Public XML-RPC methods
+#######################################################################
+
+@xmlrpc_method(False)
+def pw_rpc_version():
+    """Return Patchwork XML-RPC interface version."""
+    return 1
+
+@xmlrpc_method(False)
+def project_list(search_str="", max_count=0):
+    """Get a list of projects matching the given filters."""
+    try:
+        if len(search_str) > 0:
+            projects = Project.objects.filter(linkname__icontains = search_str)
+        else:
+            projects = Project.objects.all()
+
+        if max_count > 0:
+            return map(project_to_dict, projects)[:max_count]
+        else:
+            return map(project_to_dict, projects)
+    except:
+        return []
+
+@xmlrpc_method(False)
+def project_get(project_id):
+    """Return structure for the given project ID."""
+    try:
+        project = Project.objects.filter(id = project_id)[0]
+        return project_to_dict(project)
+    except:
+        return {}
+
+@xmlrpc_method(False)
+def person_list(search_str="", max_count=0):
+    """Get a list of Person objects matching the given filters."""
+    try:
+        if len(search_str) > 0:
+            people = (Person.objects.filter(name__icontains = search_str) |
+                Person.objects.filter(email__icontains = search_str))
+        else:
+            people = Person.objects.all()
+
+        if max_count > 0:
+            return map(person_to_dict, people)[:max_count]
+        else:
+            return map(person_to_dict, people)
+
+    except:
+        return []
+
+@xmlrpc_method(False)
+def person_get(person_id):
+    """Return structure for the given person ID."""
+    try:
+        person = Person.objects.filter(id = person_id)[0]
+        return person_to_dict(person)
+    except:
+        return {}
+
+@xmlrpc_method(False)
+def patch_list(filter={}):
+    """Get a list of patches matching the given filters."""
+    try:
+        # We allow access to many of the fields.  But, some fields are
+        # filtered by raw object so we must lookup by ID instead over
+        # XML-RPC.
+        ok_fields = [
+            "id",
+            "name",
+            "project_id",
+            "submitter_id",
+            "delegate_id",
+            "state_id",
+            "date",
+            "commit_ref",
+            "hash",
+            "msgid",
+            "name",
+            "max_count",
+            ]
+
+        dfilter = {}
+        max_count = 0
+
+        for key in filter:
+            parts = key.split("__")
+            if parts[0] not in ok_fields:
+                # Invalid field given
+                return []
+            if len(parts) > 1:
+                if LOOKUP_TYPES.count(parts[1]) == 0:
+                    # Invalid lookup type given
+                    return []
+
+            if parts[0] == 'project_id':
+                dfilter['project'] = Project.objects.filter(id =
+                                        filter[key])[0]
+            elif parts[0] == 'submitter_id':
+                dfilter['submitter'] = Person.objects.filter(id =
+                                        filter[key])[0]
+            elif parts[0] == 'state_id':
+                dfilter['state'] = State.objects.filter(id =
+                                        filter[key])[0]
+            elif parts[0] == 'max_count':
+                max_count = filter[key]
+            else:
+                dfilter[key] = filter[key]
+
+        patches = Patch.objects.filter(**dfilter)
+
+        if max_count > 0:
+            return map(patch_to_dict, patches)[:max_count]
+        else:
+            return map(patch_to_dict, patches)
+
+    except:
+        return []
+
+@xmlrpc_method(False)
+def patch_get(patch_id):
+    """Return structure for the given patch ID."""
+    try:
+        patch = Patch.objects.filter(id = patch_id)[0]
+        return patch_to_dict(patch)
+    except:
+        return {}
+
+@xmlrpc_method(False)
+def patch_get_mbox(patch_id):
+    """Return mbox string for the given patch ID."""
+    try:
+        patch = Patch.objects.filter(id = patch_id)[0]
+        return patch.mbox().as_string()
+    except:
+        return ""
+
+@xmlrpc_method(False)
+def patch_get_diff(patch_id):
+    """Return diff for the given patch ID."""
+    try:
+        patch = Patch.objects.filter(id = patch_id)[0]
+        return patch.content
+    except:
+        return ""
+
+@xmlrpc_method(True)
+def patch_set(user, patch_id, params):
+    """Update a patch with the key,value pairs in params. Only some parameters
+       can be set"""
+    try:
+        ok_params = ['state', 'commit_ref', 'archived']
+
+        patch = Patch.objects.get(id = patch_id)
+
+        if not patch.is_editable(user):
+            raise Exception('No permissions to edit this patch')
+
+        for (k, v) in params.iteritems():
+            if k not in ok_params:
+                continue
+
+            if k == 'state':
+                patch.state = State.objects.get(id = v)
+
+            else:
+                setattr(patch, k, v)
+
+        patch.save()
+
+        return True
+
+    except:
+        raise
+
+@xmlrpc_method(False)
+def state_list(search_str="", max_count=0):
+    """Get a list of state structures matching the given search string."""
+    try:
+        if len(search_str) > 0:
+            states = State.objects.filter(name__icontains = search_str)
+        else:
+            states = State.objects.all()
+
+        if max_count > 0:
+            return map(state_to_dict, states)[:max_count]
+        else:
+            return map(state_to_dict, states)
+    except:
+        return []
+
+@xmlrpc_method(False)
+def state_get(state_id):
+    """Return structure for the given state ID."""
+    try:
+        state = State.objects.filter(id = state_id)[0]
+        return state_to_dict(state)
+    except:
+        return {}
diff --git a/apps/patchwork/xmlrpc.py b/apps/patchwork/xmlrpc.py
deleted file mode 100644 (file)
index fb64a7d..0000000
+++ /dev/null
@@ -1,258 +0,0 @@
-# Patchwork - automated patch tracking system
-# Copyright (C) 2008 Nate Case <ncase@xes-inc.com>
-#
-# This file is part of the Patchwork package.
-#
-# Patchwork 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 of the License, or
-# (at your option) any later version.
-#
-# Patchwork 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 Patchwork; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-#
-# The XML-RPC interface provides a watered down, read-only interface to
-# the Patchwork database.  It's intended to be safe to export to the public
-# Internet.  A small subset of the object data is included, and the type
-# of requests/queries you can do is limited by the methods
-# that we export.
-
-from patchwork.models import Patch, Project, Person, Bundle, State
-
-# We allow most of the Django field lookup types for remote queries
-LOOKUP_TYPES = ["iexact", "contains", "icontains", "gt", "gte", "lt",
-                "in", "startswith", "istartswith", "endswith",
-                "iendswith", "range", "year", "month", "day", "isnull" ]
-
-#######################################################################
-# Helper functions
-#######################################################################
-
-def project_to_dict(obj):
-    """Return a trimmed down dictionary representation of a Project
-    object which is OK to send to the client."""
-    return \
-        {
-         'id'           : obj.id,
-         'linkname'     : obj.linkname,
-         'name'         : obj.name,
-        }
-
-def person_to_dict(obj):
-    """Return a trimmed down dictionary representation of a Person
-    object which is OK to send to the client."""
-    return \
-        {
-         'id'           : obj.id,
-         'email'        : obj.email,
-         'name'         : obj.name,
-         'user'         : str(obj.user),
-        }
-
-def patch_to_dict(obj):
-    """Return a trimmed down dictionary representation of a Patch
-    object which is OK to send to the client."""
-    return \
-        {
-         'id'           : obj.id,
-         'date'         : str(obj.date),
-         'filename'     : obj.filename(),
-         'msgid'        : obj.msgid,
-         'name'         : obj.name,
-         'project'      : str(obj.project),
-         'project_id'   : obj.project_id,
-         'state'        : str(obj.state),
-         'state_id'     : obj.state_id,
-         'submitter'    : str(obj.submitter),
-         'submitter_id' : obj.submitter_id,
-         'delegate'     : str(obj.delegate),
-         'delegate_id'  : max(obj.delegate_id, 0),
-         'commit_ref'   : max(obj.commit_ref, ''),
-        }
-
-def bundle_to_dict(obj):
-    """Return a trimmed down dictionary representation of a Bundle
-    object which is OK to send to the client."""
-    return \
-        {
-         'id'           : obj.id,
-         'name'         : obj.name,
-         'n_patches'    : obj.n_patches(),
-         'public_url'   : obj.public_url(),
-        }
-
-def state_to_dict(obj):
-    """Return a trimmed down dictionary representation of a State
-    object which is OK to send to the client."""
-    return \
-        {
-         'id'           : obj.id,
-         'name'         : obj.name,
-        }
-
-#######################################################################
-# Public XML-RPC methods
-#######################################################################
-
-def pw_rpc_version():
-    """Return Patchwork XML-RPC interface version."""
-    return 1
-
-def project_list(search_str="", max_count=0):
-    """Get a list of projects matching the given filters."""
-    try:
-        if len(search_str) > 0:
-            projects = Project.objects.filter(linkname__icontains = search_str)
-        else:
-            projects = Project.objects.all()
-
-        if max_count > 0:
-            return map(project_to_dict, projects)[:max_count]
-        else:
-            return map(project_to_dict, projects)
-    except:
-        return []
-
-def project_get(project_id):
-    """Return structure for the given project ID."""
-    try:
-        project = Project.objects.filter(id = project_id)[0]
-        return project_to_dict(project)
-    except:
-        return {}
-
-def person_list(search_str="", max_count=0):
-    """Get a list of Person objects matching the given filters."""
-    try:
-        if len(search_str) > 0:
-            people = (Person.objects.filter(name__icontains = search_str) |
-                Person.objects.filter(email__icontains = search_str))
-        else:
-            people = Person.objects.all()
-
-        if max_count > 0:
-            return map(person_to_dict, people)[:max_count]
-        else:
-            return map(person_to_dict, people)
-
-    except:
-        return []
-
-def person_get(person_id):
-    """Return structure for the given person ID."""
-    try:
-        person = Person.objects.filter(id = person_id)[0]
-        return person_to_dict(person)
-    except:
-        return {}
-
-def patch_list(filter={}):
-    """Get a list of patches matching the given filters."""
-    try:
-        # We allow access to many of the fields.  But, some fields are
-        # filtered by raw object so we must lookup by ID instead over
-        # XML-RPC.
-        ok_fields = [
-            "id",
-            "name",
-            "project_id",
-            "submitter_id",
-            "delegate_id",
-            "state_id",
-            "date",
-            "commit_ref",
-            "hash",
-            "msgid",
-            "name",
-            "max_count",
-            ]
-
-        dfilter = {}
-        max_count = 0
-
-        for key in filter:
-            parts = key.split("__")
-            if parts[0] not in ok_fields:
-                # Invalid field given
-                return []
-            if len(parts) > 1:
-                if LOOKUP_TYPES.count(parts[1]) == 0:
-                    # Invalid lookup type given
-                    return []
-
-            if parts[0] == 'project_id':
-                dfilter['project'] = Project.objects.filter(id =
-                                        filter[key])[0]
-            elif parts[0] == 'submitter_id':
-                dfilter['submitter'] = Person.objects.filter(id =
-                                        filter[key])[0]
-            elif parts[0] == 'state_id':
-                dfilter['state'] = State.objects.filter(id =
-                                        filter[key])[0]
-            elif parts[0] == 'max_count':
-                max_count = filter[key]
-            else:
-                dfilter[key] = filter[key]
-
-        patches = Patch.objects.filter(**dfilter)
-
-        if max_count > 0:
-            return map(patch_to_dict, patches)[:max_count]
-        else:
-            return map(patch_to_dict, patches)
-
-    except:
-        return []
-
-def patch_get(patch_id):
-    """Return structure for the given patch ID."""
-    try:
-        patch = Patch.objects.filter(id = patch_id)[0]
-        return patch_to_dict(patch)
-    except:
-        return {}
-
-def patch_get_mbox(patch_id):
-    """Return mbox string for the given patch ID."""
-    try:
-        patch = Patch.objects.filter(id = patch_id)[0]
-        return patch.mbox().as_string()
-    except:
-        return ""
-
-def patch_get_diff(patch_id):
-    """Return diff for the given patch ID."""
-    try:
-        patch = Patch.objects.filter(id = patch_id)[0]
-        return patch.content
-    except:
-        return ""
-
-def state_list(search_str="", max_count=0):
-    """Get a list of state structures matching the given search string."""
-    try:
-        if len(search_str) > 0:
-            states = State.objects.filter(name__icontains = search_str)
-        else:
-            states = State.objects.all()
-
-        if max_count > 0:
-            return map(state_to_dict, states)[:max_count]
-        else:
-            return map(state_to_dict, states)
-    except:
-        return []
-
-def state_get(state_id):
-    """Return structure for the given state ID."""
-    try:
-        state = State.objects.filter(id = state_id)[0]
-        return state_to_dict(state)
-    except:
-        return {}
index f70ac2a5a14e0a09fd9ac7fa217a90d61c39e65d..83aeeb4215a8fc15646cd22434c81635ea57b161 100644 (file)
@@ -101,25 +101,8 @@ ACCOUNT_ACTIVATION_DAYS = 7
 # Set to True to enable the Patchwork XML-RPC interface
 ENABLE_XMLRPC = False
 
-XMLRPC_METHODS = (
-    # List methods to be exposed in the form (<method path>, <xml-rpcname>,)
-    ('patchwork.xmlrpc.pw_rpc_version', 'pw_rpc_version',),
-    ('patchwork.xmlrpc.patch_list',     'patch_list',),
-    ('patchwork.xmlrpc.patch_get',      'patch_get',),
-    ('patchwork.xmlrpc.patch_get_mbox', 'patch_get_mbox',),
-    ('patchwork.xmlrpc.patch_get_diff', 'patch_get_diff',),
-    ('patchwork.xmlrpc.project_list',   'project_list',),
-    ('patchwork.xmlrpc.project_get',    'project_get',),
-    ('patchwork.xmlrpc.person_list',    'person_list',),
-    ('patchwork.xmlrpc.person_get',     'person_get',),
-    ('patchwork.xmlrpc.state_list',     'state_list',),
-    ('patchwork.xmlrpc.state_get',      'state_get',),
-)
-
 try:
     from local_settings import *
-    if ENABLE_XMLRPC:
-        INSTALLED_APPS = INSTALLED_APPS + ('django_xmlrpc',)
 except ImportError, ex:
     import sys
     sys.stderr.write(\
index 9886fd7c9841f2635e3569b6ee49ad61fd0ff2a2..5c4ac570717eef79f1b850395b2ea7c6bddae94e 100644 (file)
@@ -49,7 +49,3 @@ urlpatterns = patterns('',
         {'document_root': '/srv/patchwork/htdocs/images'}),
 )
 
-if settings.ENABLE_XMLRPC:
-    urlpatterns += patterns('',
-                   (r'xmlrpc/$', 'django_xmlrpc.views.handle_xmlrpc'),
-    )
index cba69d55d664d532a13245224d8cba1ab824f25e..8f3aab93f6fe48a57b69b482734ac74f35c81882 100644 (file)
@@ -62,17 +62,6 @@ in brackets):
          cd ../../apps
          ln -s ../lib/packages/django-registration ./registration
 
-       (OPTIONAL) If you want to enable the Patchwork XML-RPC interface,
-        which is required for pwclient to work, you'll need to set up the
-        django_xmlrpc package:
-
-         cd lib/packages/
-         wget \
-             http://django-xmlrpc.googlecode.com/files/django_xmlrpc-0.1.tar.gz
-         tar -zxf django_xmlrpc-0.1.tar.gz
-         cd ../../apps
-         ln -s ../lib/packages/django_xmlrpc ./django_xmlrpc
-
        The settings.py file contains default settings for patchwork, you'll
        need to configure settings for your own setup.