You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by jo...@apache.org on 2013/08/07 19:02:48 UTC

[02/13] git commit: [#6480] Add trac project and wiki importers; refactor bases

[#6480] Add trac project and wiki importers; refactor bases

Signed-off-by: Tim Van Steenburgh <tv...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/63fdf19f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/63fdf19f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/63fdf19f

Branch: refs/heads/cj/6464
Commit: 63fdf19fda232d6b0774ee5c428f3fed1b80cd21
Parents: 512ee4b
Author: Tim Van Steenburgh <tv...@gmail.com>
Authored: Fri Aug 2 22:01:08 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Aug 7 16:47:23 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           |  96 +++++++++++++++-
 ForgeImporters/forgeimporters/google/project.py |  66 +++--------
 ForgeImporters/forgeimporters/google/tasks.py   |   6 -
 .../google/templates/project.html               |  62 -----------
 .../forgeimporters/templates/project_base.html  |  89 ++++++++++++++-
 ForgeImporters/forgeimporters/trac/project.py   |  63 +++++++++++
 .../forgeimporters/trac/templates/project.html  |  32 ++++++
 .../trac/templates/wiki/index.html              |  42 +++++++
 ForgeImporters/forgeimporters/trac/tickets.py   |   2 +-
 ForgeImporters/forgeimporters/trac/wiki.py      | 110 +++++++++++++++++++
 ForgeImporters/setup.py                         |   3 +
 11 files changed, 445 insertions(+), 126 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index 034a580..5652474 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -15,16 +15,46 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import logging
+
 from pkg_resources import iter_entry_points
 
-from tg import expose
+from tg import expose, validate, flash, redirect, config
+from tg.decorators import with_trailing_slash
+from pylons import tmpl_context as c
+from formencode import validators as fev, schema
+
+from allura.lib.decorators import require_post
+from allura.lib.decorators import task
+from allura.lib.security import require_access
+from allura.lib.widgets.forms import NeighborhoodProjectShortNameValidator
+from allura.lib import exceptions
+
 from paste.deploy.converters import aslist
-from formencode import validators as fev
 
 from ming.utils import LazyProperty
 from allura.controllers import BaseController
 
 
+log = logging.getLogger(__name__)
+
+
+class ProjectImportForm(schema.Schema):
+    def __init__(self, source):
+        super(ProjectImportForm, self).__init__()
+        self.add_field('tools', ToolsValidator(source))
+
+    neighborhood = fev.PlainText(not_empty=True)
+    project_name = fev.UnicodeString(not_empty=True, max=40)
+    project_shortname = NeighborhoodProjectShortNameValidator()
+
+
+@task
+def import_tool(importer_name, project_name, mount_point=None, mount_label=None, **kw):
+    importer = ToolImporter.by_name(importer_name)
+    importer.import_tool(c.project, mount_point, mount_label, **kw)
+
+
 class ProjectImporter(BaseController):
     """
     Base class for project importers.
@@ -33,6 +63,14 @@ class ProjectImporter(BaseController):
     :meth:`process()` views described below.
     """
     source = None
+    process_validator = None
+    index_template = None
+
+    def __init__(self, neighborhood, *a, **kw):
+        self.neighborhood = neighborhood
+
+    def _check_security(self):
+        require_access(self.neighborhood, 'register')
 
     @LazyProperty
     def tool_importers(self):
@@ -47,6 +85,8 @@ class ProjectImporter(BaseController):
                 tools[ep.name] = epv()
         return tools
 
+    @with_trailing_slash
+    @expose()
     def index(self, **kw):
         """
         Override and expose this view to present the project import form.
@@ -58,9 +98,12 @@ class ProjectImporter(BaseController):
         This will list the available tool importers.  Other project fields
         (e.g., project_name) should go in the project_fields block.
         """
-        raise NotImplemented
+        return {'importer': self, 'tg_template': self.index_template}
 
-    def process(self, tools=None, **kw):
+    @require_post()
+    @expose()
+    @validate(process_validator, error_handler=index)
+    def process(self, **kw):
         """
         Override and expose this to handle a project import.
 
@@ -68,7 +111,50 @@ class ProjectImporter(BaseController):
         tools installed and redirect to the new project, presumably with a
         message indicating that some data will not be available immediately.
         """
-        raise NotImplemented
+        try:
+            c.project = self.neighborhood.register_project(kw['project_shortname'],
+                    project_name=kw['project_name'])
+        except exceptions.ProjectOverlimitError:
+            flash("You have exceeded the maximum number of projects you are allowed to create", 'error')
+            redirect('.')
+        except exceptions.ProjectRatelimitError:
+            flash("Project creation rate limit exceeded.  Please try again later.", 'error')
+            redirect('.')
+        except Exception:
+            log.error('error registering project: %s', kw['project_shortname'], exc_info=True)
+            flash('Internal Error. Please try again later.', 'error')
+            redirect('.')
+
+        self.after_project_create(c.project, **kw)
+        for importer_name in kw['tools']:
+            import_tool.post(importer_name, **kw)
+
+        flash('Welcome to the %s Project System! '
+              'Your project data will be imported and should show up here shortly.' % config['site_name'])
+        redirect(c.project.script_name + 'admin/overview')
+
+    @expose('json:')
+    @validate(process_validator)
+    def check_names(self, **kw):
+        """
+        Ajax form validation.
+
+        """
+        return c.form_errors
+
+    def after_project_create(self, project, **kw):
+        """
+        Called after project is created.
+
+        Useful for doing extra processing on the project before individual
+        tool imports happen.
+
+        :param project: The newly created project.
+        :param \*\*kw: The keyword arguments that were posted to the controller
+            method that created the project.
+
+        """
+        pass
 
 
 class ToolImporter(object):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/google/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/project.py b/ForgeImporters/forgeimporters/google/project.py
index 7416258..dda41ea 100644
--- a/ForgeImporters/forgeimporters/google/project.py
+++ b/ForgeImporters/forgeimporters/google/project.py
@@ -17,16 +17,12 @@
 
 import logging
 
-from tg import expose, validate, flash, redirect, config
+from formencode import validators as fev
+
+from tg import expose, validate
 from tg.decorators import with_trailing_slash
-from pylons import tmpl_context as c
-from formencode import validators as fev, schema
 
 from allura.lib.decorators import require_post
-from allura.lib.widgets.forms import NeighborhoodProjectShortNameValidator
-from allura.lib.security import require_access
-from allura.lib import helpers as h
-from allura.lib import exceptions
 
 from .. import base
 from . import tasks
@@ -35,15 +31,12 @@ from . import tasks
 log = logging.getLogger(__name__)
 
 
-class GoogleCodeProjectForm(schema.Schema):
-    neighborhood = fev.PlainText(not_empty=True)
+class GoogleCodeProjectForm(base.ProjectImportForm):
     project_name = fev.Regex(r'^[a-z0-9][a-z0-9-]{,61}$',
             not_empty=True,
             messages={
                 'invalid': 'Please use only letters, numbers, and dashes.',
             })
-    project_shortname = NeighborhoodProjectShortNameValidator()
-    tools = base.ToolsValidator('Google Code')
 
 
 class GoogleCodeProjectImporter(base.ProjectImporter):
@@ -55,50 +48,25 @@ class GoogleCodeProjectImporter(base.ProjectImporter):
     import.
     """
     source = 'Google Code'
+    process_validator = GoogleCodeProjectForm(source)
+    index_template = 'jinja:forgeimporters.google:templates/project.html'
 
-    def __init__(self, neighborhood, *a, **kw):
-        super(GoogleCodeProjectImporter, self).__init__(*a, **kw)
-        self.neighborhood = neighborhood
-
-    def _check_security(self):
-        require_access(self.neighborhood, 'register')
+    def after_project_create(self, project, **kw):
+        project.set_tool_data('google-code', project_name=project.name)
+        tasks.import_project_info.post()
 
     @with_trailing_slash
-    @expose('jinja:forgeimporters.google:templates/project.html')
+    @expose(index_template)
     def index(self, **kw):
-        return {'importer': self}
+        return super(self.__class__, self).index(**kw)
 
     @require_post()
     @expose()
-    @validate(GoogleCodeProjectForm(), error_handler=index)
-    def process(self, project_name=None, project_shortname=None, tools=None, **kw):
-        project_name = h.really_unicode(project_name).encode('utf-8')
-        project_shortname = h.really_unicode(project_shortname).encode('utf-8').lower()
-
-        try:
-            c.project = self.neighborhood.register_project(project_shortname,
-                    project_name=project_name)
-        except exceptions.ProjectOverlimitError:
-            flash("You have exceeded the maximum number of projects you are allowed to create", 'error')
-            redirect('.')
-        except exceptions.ProjectRatelimitError:
-            flash("Project creation rate limit exceeded.  Please try again later.", 'error')
-            redirect('.')
-        except Exception as e:
-            log.error('error registering project: %s', project_shortname, exc_info=True)
-            flash('Internal Error. Please try again later.', 'error')
-            redirect('.')
-
-        c.project.set_tool_data('google-code', project_name=project_name)
-        tasks.import_project_info.post(project_name)
-        for importer_name in tools:
-            tasks.import_tool.post(importer_name, project_name)
-
-        flash('Welcome to the %s Project System! '
-              'Your project data will be imported and should show up here shortly.' % config['site_name'])
-        redirect(c.project.script_name + 'admin/overview')
+    @validate(process_validator, error_handler=index)
+    def process(self, **kw):
+        return super(self.__class__, self).process(**kw)
 
     @expose('json:')
-    @validate(GoogleCodeProjectForm())
-    def check_names(self, project_name=None, project_shortname=None, tools=None, **kw):
-        return c.form_errors
+    @validate(process_validator)
+    def check_names(self, **kw):
+        return super(self.__class__, self).check_names(**kw)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/google/tasks.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tasks.py b/ForgeImporters/forgeimporters/google/tasks.py
index 3e6e74d..968d9a9 100644
--- a/ForgeImporters/forgeimporters/google/tasks.py
+++ b/ForgeImporters/forgeimporters/google/tasks.py
@@ -23,7 +23,6 @@ from ming.orm import ThreadLocalORMSession
 from allura.lib.decorators import task
 
 from . import GoogleCodeProjectExtractor
-from ..base import ToolImporter
 
 
 @task
@@ -34,8 +33,3 @@ def import_project_info(project_name):
     extractor.get_license()
     ThreadLocalORMSession.flush_all()
     g.post_event('project_updated')
-
-@task
-def import_tool(importer_name, project_name, mount_point=None, mount_label=None):
-    importer = ToolImporter.by_name(importer_name)
-    importer.import_tool(c.project, project_name, mount_point, mount_label)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/google/templates/project.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/templates/project.html b/ForgeImporters/forgeimporters/google/templates/project.html
index 2cda0d0..172dcf5 100644
--- a/ForgeImporters/forgeimporters/google/templates/project.html
+++ b/ForgeImporters/forgeimporters/google/templates/project.html
@@ -18,68 +18,6 @@
 -#}
 {% extends 'forgeimporters:templates/project_base.html' %}
 
-{% block extra_css %}
-    {{ super() }}
-    <style type="text/css">
-        #project-import-form #project-fields input {
-            width: 88%;
-        }
-
-        .hidden { display: none; }
-    </style>
-{% endblock %}
-
-{% block extra_js %}
-    {{ super() }}
-    <script type="text/javascript">
-        var timers = {};
-        function delay(callback, ms) {
-            clearTimeout(timers[callback]);
-            timers[callback] = setTimeout(callback, ms);
-        }
-
-        var manual = false;
-        function suggest_name() {
-            var $project_shortname = $('#project_shortname');
-            if (!manual) {
-                $project_shortname.val($('#project_name').val());
-            }
-            $project_shortname.trigger('change');
-        }
-
-        function check_names() {
-            var data = {
-                'neighborhood': $('#neighborhood').val(),
-                'project_name': $('#project_name').val(),
-                'project_shortname': $('#project_shortname').val()
-            };
-            $.getJSON('check_names', data, function(result) {
-                $('#project_name_error').addClass('hidden');
-                $('#project_shortname_error').addClass('hidden');
-                for(var field in result) {
-                    $('#'+field+'_error').text(result[field]).removeClass('hidden');
-                }
-            });
-        }
-
-        function update_url() {
-            $('#url-fragment').text($('#project_shortname').val());
-        }
-
-        $(function() {
-            $('#project_name').focus().bind('change keyup', suggest_name);
-
-            $('#project_shortname').bind('change keyup', function(event) {
-                if (event.type == 'keyup') {
-                    manual = true;
-                }
-                update_url();
-                delay(check_names, 500);
-            });
-        });
-    </script>
-{% endblock %}
-
 {% block project_fields %}
     <div class="grid-6">
         <label>Google Project Name</label>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/templates/project_base.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/templates/project_base.html b/ForgeImporters/forgeimporters/templates/project_base.html
index fc654f2..d4db30e 100644
--- a/ForgeImporters/forgeimporters/templates/project_base.html
+++ b/ForgeImporters/forgeimporters/templates/project_base.html
@@ -23,13 +23,97 @@
 {% block title %}{{importer.source}} Project Importer{% endblock %}
 {% block header %}{{importer.source}} Project Importer{% endblock %}
 
-{% block content %}
+{% block extra_css %}
+    {{ super() }}
+    <style type="text/css">
+        #project-import-form #project-fields input {
+            width: 88%;
+        }
+
+        .hidden { display: none; }
+    </style>
+{% endblock %}
+
+{% block extra_js %}
+    {{ super() }}
+    <script type="text/javascript">
+        var timers = {};
+        function delay(callback, ms) {
+            clearTimeout(timers[callback]);
+            timers[callback] = setTimeout(callback, ms);
+        }
+
+        var manual = false;
+        function suggest_name() {
+            var $project_shortname = $('#project_shortname');
+            if (!manual) {
+                $project_shortname.val($('#project_name').val());
+            }
+            $project_shortname.trigger('change');
+        }
+
+        function check_names() {
+            var data = {
+                'neighborhood': $('#neighborhood').val(),
+                'project_name': $('#project_name').val(),
+                'project_shortname': $('#project_shortname').val()
+            };
+            $.getJSON('check_names', data, function(result) {
+                $('#project_name_error').addClass('hidden');
+                $('#project_shortname_error').addClass('hidden');
+                for(var field in result) {
+                    $('#'+field+'_error').text(result[field]).removeClass('hidden');
+                }
+            });
+        }
+
+        function update_url() {
+            $('#url-fragment').text($('#project_shortname').val());
+        }
 
+        $(function() {
+            $('#project_name').focus().bind('change keyup', suggest_name);
+
+            $('#project_shortname').bind('change keyup', function(event) {
+                if (event.type == 'keyup') {
+                    manual = true;
+                }
+                update_url();
+                delay(check_names, 500);
+            });
+        });
+    </script>
+{% endblock %}
+
+{% block content %}
 <form id="project-import-form" method="POST" action="process">
     <input type="hidden" id="neighborhood" name="neighborhood" value="{{importer.neighborhood.name}}"/>
 
     <fieldset id="project-fields">
-        {% block project_fields %}{% endblock %}
+      {% block project_fields %}
+      <div class="grid-6" style="clear:left">
+          <label>Project Name</label>
+      </div>
+      <div class="grid-10">
+          <input id="project_name" name="project_name" value="{{c.form_values['project_name']}}"/>
+          <div id="project_name_error" class="error{% if not c.form_errors['project_name'] %} hidden{% endif %}">
+              {{c.form_errors['project_name']}}
+          </div>
+      </div>
+
+      <div class="grid-6" style="clear:left">
+          <label>URL Name</label>
+      </div>
+      <div class="grid-10">
+          <input id="project_shortname" name="project_shortname" value="{{c.form_values['project_shortname']}}"/>
+          <div id="project_shortname_error" class="error{% if not c.form_errors['project_shortname'] %} hidden{% endif %}">
+              {{c.form_errors['project_shortname']}}
+          </div>
+          <div id="project-url">
+              http://{{request.environ['HTTP_HOST']}}{{importer.neighborhood.url()}}<span id="url-fragment">{{c.form_values['project_shortname']}}</span>
+          </div>
+      </div>
+      {% endblock %}
     </fieldset>
 
     <fieldset id="tool-fields">
@@ -50,5 +134,4 @@
 
     <input type="submit" value="Import"/>
 </form>
-
 {% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/trac/project.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/project.py b/ForgeImporters/forgeimporters/trac/project.py
new file mode 100644
index 0000000..66e8326
--- /dev/null
+++ b/ForgeImporters/forgeimporters/trac/project.py
@@ -0,0 +1,63 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+import logging
+
+from formencode import validators as fev
+
+from tg import expose, validate
+from tg.decorators import with_trailing_slash
+
+from allura.lib.decorators import require_post
+
+from .. import base
+
+
+log = logging.getLogger(__name__)
+
+
+class TracProjectForm(base.ProjectImportForm):
+    trac_url = fev.URL(not_empty=True)
+
+
+class TracProjectImporter(base.ProjectImporter):
+    """
+    Project importer for Trac.
+
+    """
+    source = 'Trac'
+    process_validator = TracProjectForm(source)
+    index_template = 'jinja:forgeimporters.trac:templates/project.html'
+
+    def after_project_create(self, project, **kw):
+        project.set_tool_data('trac', url=kw['trac_url'])
+
+    @with_trailing_slash
+    @expose(index_template)
+    def index(self, **kw):
+        return super(self.__class__, self).index(**kw)
+
+    @require_post()
+    @expose()
+    @validate(process_validator, error_handler=index)
+    def process(self, **kw):
+        return super(self.__class__, self).process(**kw)
+
+    @expose('json:')
+    @validate(process_validator)
+    def check_names(self, **kw):
+        return super(self.__class__, self).check_names(**kw)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/trac/templates/project.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/templates/project.html b/ForgeImporters/forgeimporters/trac/templates/project.html
new file mode 100644
index 0000000..abcfb48
--- /dev/null
+++ b/ForgeImporters/forgeimporters/trac/templates/project.html
@@ -0,0 +1,32 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+{% extends 'forgeimporters:templates/project_base.html' %}
+
+{% block project_fields %}
+    <div class="grid-6">
+        <label>Trac URL</label>
+    </div>
+    <div class="grid-10">
+        <input id="trac_url" name="trac_url" value="{{c.form_values['trac_url']}}"/>
+        <div id="trac_ur_errorl" class="error{% if not c.form_errors['trac_url'] %} hidden{% endif %}">
+            {{c.form_errors['trac_url']}}
+        </div>
+    </div>
+    {{ super() }}
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/trac/templates/wiki/index.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/templates/wiki/index.html b/ForgeImporters/forgeimporters/trac/templates/wiki/index.html
new file mode 100644
index 0000000..6083b9c
--- /dev/null
+++ b/ForgeImporters/forgeimporters/trac/templates/wiki/index.html
@@ -0,0 +1,42 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.0 (the
+       "License"); you may not use this file except in compliance
+       with the License.  You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+{% extends g.theme.master %}
+
+{% block title %}
+{{c.project.name}} / Import Trac Wiki
+{% endblock %}
+
+{% block header %}
+Import wiki from Trac
+{% endblock %}
+
+{% block content %}
+<form action="create" method="post" class="pad">
+  <label for="trac_url">URL of the Trac instance</label>
+  <input name="trac_url" />
+
+  <label for="mount_label">Label</label>
+  <input name="mount_label" value="Source" />
+
+  <label for="mount_point">Mount Point</label>
+  <input name="mount_point" value="source" />
+
+  <input type="submit" />
+</form>
+{% endblock %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/trac/tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tickets.py b/ForgeImporters/forgeimporters/trac/tickets.py
index cc31741..969dfd2 100644
--- a/ForgeImporters/forgeimporters/trac/tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tickets.py
@@ -48,7 +48,7 @@ from allura.scripts.trac_export import (
 
 from forgeimporters.base import ToolImporter
 from forgetracker.tracker_main import ForgeTrackerApp
-from forgetracker.script.import_tracker import import_tracker
+from forgetracker.scripts.import_tracker import import_tracker
 
 
 class TracTicketImportSchema(fe.Schema):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/forgeimporters/trac/wiki.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/wiki.py b/ForgeImporters/forgeimporters/trac/wiki.py
new file mode 100644
index 0000000..2417863
--- /dev/null
+++ b/ForgeImporters/forgeimporters/trac/wiki.py
@@ -0,0 +1,110 @@
+#       Licensed to the Apache Software Foundation (ASF) under one
+#       or more contributor license agreements.  See the NOTICE file
+#       distributed with this work for additional information
+#       regarding copyright ownership.  The ASF licenses this file
+#       to you under the Apache License, Version 2.0 (the
+#       "License"); you may not use this file except in compliance
+#       with the License.  You may obtain a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#       Unless required by applicable law or agreed to in writing,
+#       software distributed under the License is distributed on an
+#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#       KIND, either express or implied.  See the License for the
+#       specific language governing permissions and limitations
+#       under the License.
+
+import argparse
+from datetime import (
+        datetime,
+        timedelta,
+        )
+import tempfile
+
+import formencode as fe
+from formencode import validators as fev
+
+from pylons import tmpl_context as c
+from pylons import app_globals as g
+from tg import (
+        config,
+        expose,
+        redirect,
+        validate,
+        )
+from tg.decorators import (
+        with_trailing_slash,
+        without_trailing_slash,
+        )
+
+from allura.controllers import BaseController
+from allura.lib.decorators import require_post
+from allura.model import ApiTicket
+
+from forgeimporters.base import ToolImporter
+
+from forgewiki.scripts.wiki_from_trac.extractors import WikiExporter
+from forgewiki.scripts.wiki_from_trac.loaders import load_data
+from forgewiki.scripts.wiki_from_trac.wiki_from_trac import WikiFromTrac
+from forgewiki.wiki_main import ForgeWikiApp
+
+
+class TracWikiImportSchema(fe.Schema):
+    trac_url = fev.URL(not_empty=True)
+    mount_point = fev.UnicodeString()
+    mount_label = fev.UnicodeString()
+
+
+class TracWikiImportController(BaseController):
+    @with_trailing_slash
+    @expose('jinja:forgeimporters.trac:templates/wiki/index.html')
+    def index(self, **kw):
+        return {}
+
+    @without_trailing_slash
+    @expose()
+    @require_post()
+    @validate(TracWikiImportSchema(), error_handler=index)
+    def create(self, trac_url, mount_point, mount_label, **kw):
+        app = TracWikiImporter.import_tool(c.project,
+                mount_point=mount_point,
+                mount_label=mount_label,
+                trac_url=trac_url,
+                user=c.user)
+        redirect(app.url())
+
+
+class TracWikiImporter(ToolImporter):
+    target_app = ForgeWikiApp
+    source = 'Trac'
+    controller = TracWikiImportController
+    tool_label = 'Trac Wiki Importer'
+    tool_description = 'Import your wiki from Trac'
+
+    def import_tool(self, project=None, mount_point=None, mount_label=None,
+            trac_url=None, user=None):
+        """ Import Trac wiki into a new Allura Wiki tool.
+
+        """
+        mount_point = mount_point or 'wiki'
+        app = project.install_app(
+                'Wiki',
+                mount_point=mount_point,
+                mount_label=mount_label or 'Wiki',
+                )
+        api_ticket = ApiTicket(user_id=user._id,
+                capabilities={"import": ["Projects", project.shortname]},
+                expires=datetime.utcnow() + timedelta(minutes=60))
+        options = argparse.Namespace()
+        options.api_key = api_ticket.api_key
+        options.secret_key = api_ticket.secret_key
+        options.project = project.shortname
+        options.wiki = mount_point
+        options.base_url = config['base_url']
+        with tempfile.NamedTemporaryFile() as f:
+            WikiExporter(trac_url, options).export(f)
+            f.flush()
+            load_data(f.name, WikiFromTrac.parser(), options)
+        g.post_event('project_updated')
+        return app

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/63fdf19f/ForgeImporters/setup.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/setup.py b/ForgeImporters/setup.py
index 45a08eb..c4776ba 100644
--- a/ForgeImporters/setup.py
+++ b/ForgeImporters/setup.py
@@ -35,8 +35,11 @@ setup(name='ForgeImporters',
       # -*- Entry points: -*-
       [allura.project_importers]
       google-code = forgeimporters.google.project:GoogleCodeProjectImporter
+      trac = forgeimporters.trac.project:TracProjectImporter
 
       [allura.importers]
       google-code-tracker = forgeimporters.google.tracker:GoogleCodeTrackerImporter
       google-code-repo = forgeimporters.google.code:GoogleRepoImporter
+      trac-tickets = forgeimporters.trac.tickets:TracTicketImporter
+      trac-wiki = forgeimporters.trac.wiki:TracWikiImporter
       """,)