=== modified file 'dashboard_app/extension.py'
--- dashboard_app/extension.py	2011-07-12 02:32:26 +0000
+++ dashboard_app/extension.py	2011-07-12 03:06:00 +0000
@@ -1,4 +1,23 @@
+# Copyright (C) 2010 Linaro Limited
+#
+# Author: Zygmunt Krynicki <zygmunt.krynicki@linaro.org>
+#
+# This file is part of Launch Control.
+#
+# Launch Control is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License version 3
+# as published by the Free Software Foundation
+#
+# Launch Control 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 Affero General Public License
+# along with Launch Control.  If not, see <http://www.gnu.org/licenses/>.
+
 import os
+
 from lava_server.extension import LavaServerExtension
 
 
@@ -22,9 +41,14 @@
 
     @property
     def version(self):
+        from dashboard_app import __version__
         import versiontools
-        import dashboard_app 
-        return versiontools.format_version(dashboard_app.__version__)
+        return versiontools.format_version(__version__)
+
+    @property
+    def api_class(self):
+        from dashboard_app.xmlrpc import DashboardAPI
+        return DashboardAPI
 
     def contribute_to_settings(self, settings_module):
         super(DashboardExtension, self).contribute_to_settings(settings_module)
@@ -55,4 +79,3 @@
             # properly, having permissions to login, permissions to view proper data)
             settings_module['DATABASES']['dataview'] = dict(settings_module['DATABASES']['default'])
             settings_module['DATABASES']['dataview']['USER'] += "_dataview"
-

=== modified file 'dashboard_app/tests/other/dashboard_api.py'
--- dashboard_app/tests/other/dashboard_api.py	2011-05-30 16:52:46 +0000
+++ dashboard_app/tests/other/dashboard_api.py	2011-07-07 11:38:01 +0000
@@ -31,38 +31,6 @@
 from dashboard_app.xmlrpc import errors
 
 
-class DashboardAPITests(DashboardXMLRPCViewsTestCase):
-
-    def test_xml_rpc_help_returns_200(self):
-        response = self.client.get(self.endpoint_path)
-        self.assertEqual(response.status_code, 200)
-
-    def test_help_page_lists_all_methods(self):
-        from dashboard_app.views import DashboardDispatcher as dispatcher
-        expected_methods = []
-        for name in dispatcher.system_listMethods():
-            expected_methods.append({
-                'name': name,
-                'signature': dispatcher.system_methodSignature(name),
-                'help': dispatcher.system_methodHelp(name)
-                })
-        response = self.client.get(self.endpoint_path)
-        self.assertEqual(response.context['methods'], expected_methods)
-
-    def test_get_request_shows_help(self):
-        response = self.client.get(self.endpoint_path)
-        self.assertTemplateUsed(response, "dashboard_app/api.html")
-
-    def test_empty_post_request_shows_help(self):
-        response = self.client.post(self.endpoint_path)
-        self.assertTemplateUsed(response, "dashboard_app/api.html")
-
-    def test_version(self):
-        from dashboard_app import __version__
-        self.assertEqual(self.xml_rpc_call('version'),
-                ".".join(map(str, __version__)))
-
-
 class DashboardAPIStreamsTests(DashboardXMLRPCViewsTestCase):
 
     scenarios = [

=== modified file 'dashboard_app/tests/other/dataview.py'
--- dashboard_app/tests/other/dataview.py	2011-06-23 10:09:42 +0000
+++ dashboard_app/tests/other/dataview.py	2011-07-07 11:38:53 +0000
@@ -19,11 +19,12 @@
 import unittest
 
 from mocker import Mocker, expect
+from testtools import TestCase
 
 from dashboard_app.dataview import DataView
 
 
-class DataViewHandlerTests(unittest.TestCase):
+class DataViewHandlerTests(TestCase):
 
     text = """
     <data-view name="foo">
@@ -43,6 +44,7 @@
     """
 
     def setUp(self):
+        super(DataViewHandlerTests, self).setUp()
         self.dataview = DataView.load_from_xml(self.text) 
 
     def test_name_parsed_ok(self):
@@ -74,7 +76,7 @@
         self.assertEqual(arg.help, "sorting order")
 
 
-class DataViewConnectionTests(unittest.TestCase):
+class DataViewConnectionTests(TestCase):
 
     def test_get_connection_without_sandbox(self):
         # Mock connections['dataview'] to raise ConnectionDoesNotExist

=== modified file 'dashboard_app/views.py'
--- dashboard_app/views.py	2011-07-12 03:00:26 +0000
+++ dashboard_app/views.py	2011-07-12 03:06:00 +0000
@@ -42,23 +42,9 @@
     TestResult,
     TestRun,
 )
-from dashboard_app.xmlrpc import DashboardAPI
 from dashboard_app.bread_crumbs import BreadCrumb, BreadCrumbTrail
 
 
-def _get_dashboard_dispatcher():
-    """
-    Build dashboard XML-RPC dispatcher.
-    """
-    dispatcher = DjangoXMLRPCDispatcher()
-    dispatcher.register_instance(DashboardAPI())
-    dispatcher.register_introspection_functions()
-    return dispatcher
-
-
-DashboardDispatcher = _get_dashboard_dispatcher()
-
-
 def _get_queryset(klass):
     """
     Returns a QuerySet from a Model, Manager, or QuerySet. Created to make
@@ -97,41 +83,11 @@
         raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
 
 
-# Original inspiration from:
-# Brendan W. McAdams <brendan.mcadams@thewintergrp.com>
-def xml_rpc_handler(request, dispatcher):
-    """
-    XML-RPC handler.
-
-    If post data is defined, it assumes it's XML-RPC and tries to
-    process as such. Empty POST request and GET requests assumes you're
-    viewing from a browser and tells you about the service.
-    """
-    if len(request.POST):
-        raw_data = request.raw_post_data
-        response = HttpResponse(mimetype="application/xml")
-        result = dispatcher._marshaled_dispatch(raw_data)
-        response.write(result)
-        response['Content-length'] = str(len(response.content))
-        return response
-    else:
-        methods = [{
-            'name': method,
-            'signature': dispatcher.system_methodSignature(method),
-            'help': dispatcher.system_methodHelp(method)}
-            for method in dispatcher.system_listMethods()]
-        return render_to_response(
-            'dashboard_app/api.html', {
-                'methods': methods,
-                'dashboard_url': "http://{domain}".format(
-                    domain = Site.objects.get_current().domain)
-            }, RequestContext(request)
-        )
-
-
 @csrf_exempt
 def dashboard_xml_rpc_handler(request):
-    return xml_rpc_handler(request, DashboardDispatcher)
+    from dashboard_app.xmlrpc import legacy_mapper
+    from linaro_django_xmlrpc.views import handler
+    return handler(request, legacy_mapper)
 
 
 @BreadCrumb("Dashboard")

=== modified file 'dashboard_app/xmlrpc.py'
--- dashboard_app/xmlrpc.py	2011-07-04 02:35:35 +0000
+++ dashboard_app/xmlrpc.py	2011-07-07 11:39:05 +0000
@@ -26,6 +26,8 @@
 
 from django.contrib.auth.models import User
 from django.db import IntegrityError, DatabaseError
+from linaro_django_xmlrpc.models import ExposedAPI
+from linaro_django_xmlrpc.models import Mapper
 
 from dashboard_app import __version__
 from dashboard_app.dataview import DataView, DataViewRepository
@@ -48,7 +50,7 @@
     NOT_IMPLEMENTED = 501
 
 
-class DashboardAPI(object):
+class DashboardAPI(ExposedAPI):
     """
     Dashboard API object.
 
@@ -139,17 +141,16 @@
             - team streams are accessible by team members
 
         """
-        user = None
         try:
             logging.debug("Getting bundle stream")
-            bundle_stream = BundleStream.objects.accessible_by_principal(user).get(pathname=pathname)
+            bundle_stream = BundleStream.objects.accessible_by_principal(self.user).get(pathname=pathname)
         except BundleStream.DoesNotExist:
             logging.debug("Bundle stream does not exists, aborting")
             raise xmlrpclib.Fault(errors.NOT_FOUND,
                     "Bundle stream not found")
         try:
             logging.debug("Creating bundle object")
-            bundle = Bundle.objects.create_with_content(bundle_stream, user, content_filename, content)
+            bundle = Bundle.objects.create_with_content(bundle_stream, self.user, content_filename, content)
         except (IntegrityError, ValueError) as exc:
             logging.debug("Raising xmlrpclib.Fault(errors.CONFLICT)")
             raise xmlrpclib.Fault(errors.CONFLICT, str(exc))
@@ -203,10 +204,9 @@
             - personal streams are accessible by owners
             - team streams are accessible by team members
         """
-        user = None
         try:
             bundle = Bundle.objects.get(content_sha1=content_sha1)
-            if not bundle.bundle_stream.is_accessible_by(user):
+            if not bundle.bundle_stream.is_accessible_by(self.user):
                 raise Bundle.DoesNotExist()
         except Bundle.DoesNotExist:
             raise xmlrpclib.Fault(errors.NOT_FOUND,
@@ -259,8 +259,7 @@
             - personal streams are accessible by owners
             - team streams are accessible by team members
         """
-        user = None
-        bundle_streams = BundleStream.objects.accessible_by_principal(user)
+        bundle_streams = BundleStream.objects.accessible_by_principal(self.user)
         return [{
             'pathname': bundle_stream.pathname,
             'name': bundle_stream.name,
@@ -323,9 +322,8 @@
             - personal streams are accessible by owners
             - team streams are accessible by team members
         """
-        user = None
         try:
-            bundle_stream = BundleStream.objects.accessible_by_principal(user).get(pathname=pathname)
+            bundle_stream = BundleStream.objects.accessible_by_principal(self.user).get(pathname=pathname)
         except BundleStream.DoesNotExist:
             raise xmlrpclib.Fault(errors.NOT_FOUND, "Bundle stream not found")
         return [{
@@ -431,6 +429,7 @@
             except IntegrityError:
                 raise xmlrpclib.Fault(errors.CONFLICT, "Stream with the specified pathname already exists")
         else:
+            # TODO: Make this constraint unnecessary
             raise xmlrpclib.Fault(errors.FORBIDDEN, "Only anonymous streams can be constructed")
         return bundle_stream.pathname
 
@@ -583,3 +582,10 @@
                     "type": item[1]
                 } for item in columns]
             }
+
+
+
+# Mapper used by the legacy URL
+legacy_mapper = Mapper()
+legacy_mapper.register_introspection_methods()
+legacy_mapper.register(DashboardAPI, '')

=== modified file 'setup.py'
--- setup.py	2011-07-01 11:43:06 +0000
+++ setup.py	2011-07-07 11:38:01 +0000
@@ -54,6 +54,7 @@
         'lava-server >= 0.2.1',
         'linaro-dashboard-bundle >= 1.5.2',
         'linaro-django-pagination >= 2.0.2',
+        'linaro-django-xmlrpc >= 0.3.2',
         'linaro-json >= 2.0.1',  # TODO: use json-schema-validator
         'pygments >= 1.2',
         'south >= 0.7.3',

