You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/04/16 17:43:51 UTC

[01/22] allura git commit: [#7850] Ability to close discussion on a ticket.

Repository: allura
Updated Branches:
  refs/heads/ib/6017 03c8bcce3 -> ad7c5f9cc (forced update)


[#7850] Ability to close discussion on a ticket.


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

Branch: refs/heads/ib/6017
Commit: f42961eb2d54c1daaa0e4710fe32090adc65e25f
Parents: 80736cc
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Wed Apr 1 13:37:36 2015 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Thu Apr 2 17:24:36 2015 -0400

----------------------------------------------------------------------
 ForgeTracker/forgetracker/model/ticket.py       | 38 ++++++++++---
 .../templates/tracker/search_help.html          |  2 +
 .../forgetracker/templates/tracker/ticket.html  |  3 ++
 .../templates/tracker_widgets/ticket_form.html  |  9 +++-
 .../forgetracker/tests/functional/test_root.py  | 57 ++++++++++++++++++++
 ForgeTracker/forgetracker/tracker_main.py       | 22 +++++---
 .../forgetracker/widgets/ticket_form.py         |  3 ++
 7 files changed, 120 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/model/ticket.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 8218ec7..06626a1 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -62,7 +62,7 @@ from allura.model import (
 )
 from allura.model.timeline import ActivityObject
 from allura.model.notification import MailFooter
-from allura.model.types import MarkdownCache
+from allura.model.types import MarkdownCache, EVERYONE
 
 from allura.lib import security
 from allura.lib.search import search_artifact, SearchError
@@ -360,6 +360,10 @@ class Globals(MappedClass):
         if private:
             values['private'] = asbool(private)
 
+        discussion_disabled = post_data.get('discussion_disabled')
+        if discussion_disabled:
+            values['disabled_discussion'] = asbool(discussion_disabled)
+
         custom_values = {}
         custom_fields = {}
         for cf in self.custom_fields or []:
@@ -384,16 +388,17 @@ class Globals(MappedClass):
                             get_label(k),
                             new_user.display_name,
                             old_user.display_name)
-                elif k == 'private':
-                    def private_text(val):
+                elif k == 'private' or k == 'discussion_disabled':
+                    def _text(val):
                         if val:
                             return 'Yes'
                         else:
                             return 'No'
+
                     message += get_change_text(
                         get_label(k),
-                        private_text(v),
-                        private_text(getattr(ticket, k)))
+                        _text(v),
+                        _text(getattr(ticket, k)))
                 else:
                     message += get_change_text(
                         get_label(k),
@@ -681,6 +686,7 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
             text=self.description,
             snippet_s=self.summary,
             private_b=self.private,
+            discussion_disabled_b=self.discussion_disabled,
             votes_up_i=self.votes_up,
             votes_down_i=self.votes_down,
             votes_total_i=(self.votes_up - self.votes_down),
@@ -827,7 +833,26 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
             self.acl = []
     private = property(_get_private, _set_private)
 
-    def commit(self):
+    def _set_discussion_disabled(self, is_disabled):
+        """ Sets a ticket's discussion thread ACL to control a users ability to post.
+
+        :param is_disabled: If True, an explicit deny will be created on the discussion thread ACL.
+        """
+        if is_disabled:
+            _deny_post = lambda role, perms: [ACE.deny(role, perm) for perm in perms]
+            self.discussion_thread.acl = _deny_post(EVERYONE, ('post', 'unmoderated_post'))
+        else:
+            self.discussion_thread.acl = []
+
+    def _get_discussion_disabled(self):
+        """ Checks the discussion thread ACL to determine if users are allowed to post."""
+        thread = Thread.query.get(ref_id=self.index_id())
+        if thread:
+            return bool(thread.acl)
+        return False
+    discussion_disabled = property(_get_discussion_disabled, _set_discussion_disabled)
+
+    def commit(self, **kwargs):
         VersionedArtifact.commit(self)
         monitoring_email = self.app.config.options.get('TicketMonitoringEmail')
         if self.version > 1:
@@ -1093,6 +1118,7 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
                         self.assigned_to_id) or None,
                     status=self.status,
                     private=self.private,
+                    discussion_disabled=self.discussion_disabled,
                     attachments=[dict(bytes=attach.length,
                                       url=h.absurl(
                                           attach.url(

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/templates/tracker/search_help.html
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/templates/tracker/search_help.html b/ForgeTracker/forgetracker/templates/tracker/search_help.html
index 8ec03bb..6f6bd28 100644
--- a/ForgeTracker/forgetracker/templates/tracker/search_help.html
+++ b/ForgeTracker/forgetracker/templates/tracker/search_help.html
@@ -45,6 +45,7 @@
     <li>Status of the ticket - status</li>
     <li>Title of the ticket - summary</li>
     <li>Private ticket - private</li>
+    <li>Discussion Disabled ticket - discussion_disabled</li>
     <li>Votes up/down of the ticket - votes_up/votes_down (if enabled in tool options)</li>
     <li>Votes total of the ticket - votes_total</li>
     <li>Imported legacy id - import_id</li>
@@ -90,6 +91,7 @@
     <li>Status of the ticket - status_s</li>
     <li>Title of the ticket - snippet_s</li>
     <li>Private ticket - private_b</li>
+    <li>Discussion disabled ticket - discussion_disabled_b</li>
     {% if c.app.globals.custom_fields %}
     <li>Custom fields:
         <ul>

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/templates/tracker/ticket.html
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/templates/tracker/ticket.html b/ForgeTracker/forgetracker/templates/tracker/ticket.html
index 2d3dd30..27331c8 100644
--- a/ForgeTracker/forgetracker/templates/tracker/ticket.html
+++ b/ForgeTracker/forgetracker/templates/tracker/ticket.html
@@ -136,6 +136,9 @@
     <div class="grid-4">
       <label class="simple">Private:</label>
       {{'Yes' if ticket.private else 'No'}}
+        {% if ticket.discussion_disabled %}
+            <label class="simple"><span class="closed">Discussion Disabled</span></label>
+        {% endif %}
     </div>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html b/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
index 63a448d..25f82a1 100644
--- a/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
+++ b/ForgeTracker/forgetracker/templates/tracker_widgets/ticket_form.html
@@ -51,8 +51,13 @@
     <label class="cr">Labels:</label>
     {{widget.display_field_by_name('labels')|safe}}
   </div>
-  <div class="grid-6">
-    {{widget.display_field_by_name('private')}}
+    <div class="grid-6">
+        {{widget.display_field_by_name('private')}}
+        {% if h.has_access(ticket, 'edit') %}
+        {# Only users with the ability to edit will be able to see the Discussion Disable option.
+            This also serves to hide the checkbox when creating new tickets #}
+            <div>{{widget.display_field_by_name('discussion_disabled')}}</div>
+        {% endif %}
   </div>
   <div style="clear:both">&nbsp;</div>
   <div class="grid-6">

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index b2c5c11..4a2e598 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -625,6 +625,63 @@ class TestFunctionalController(TrackerTestController):
         response = self.app.get('/bugs/1/')
         assert_true('<li><strong>private</strong>: No --&gt; Yes</li>' in response)
 
+    def test_discussion_disabled_ticket(self):
+        response = self.new_ticket(summary='test discussion disabled ticket').follow()
+        # New tickets will not show discussion disabled
+        assert_not_in('<span class="closed">Discussion Disabled</span>', response)
+
+        ticket_params = {
+            'ticket_form.summary': 'test discussion disabled ticket',
+            'ticket_form.description': '',
+            'ticket_form.status': 'open',
+            'ticket_form._milestone': '1.0',
+            'ticket_form.assigned_to': '',
+            'ticket_form.labels': '',
+            'ticket_form.comment': 'no more comments allowed',
+            'ticket_form.discussion_disabled': 'on',
+        }
+
+        # Disable Discussion
+        response = self.app.post('/bugs/1/update_ticket_from_widget', ticket_params).follow()
+        assert_in('<li><strong>discussion</strong>: enabled --&gt; disabled</li>', response)
+        assert_in('<span class="closed">Discussion Disabled</span>', response)
+        assert_in('edit_post_form reply', response)  # Make sure admin can still comment
+
+        # Unauthorized user cannot comment or even see form fields
+        env = dict(username='*anonymous')
+        r = self.app.get('/p/test/bugs/1', extra_environ=env)
+        assert_not_in('edit_post_form reply', r)
+
+        env = dict(username='test-admin')
+        r = self.app.get('/p/test/bugs/1', extra_environ=env)
+
+        # Test re-enabling discussions
+        ticket_params['ticket_form.discussion_disabled'] = 'off'
+        response = self.app.post('/bugs/1/update_ticket_from_widget', ticket_params).follow()
+        assert_in('<li><strong>discussion</strong>: disabled --&gt; enabled</li>', response)
+        assert_not_in('<span class="closed">Discussion Disabled</span>', response)
+
+        # Test solr search
+        M.MonQTask.run_ready()
+        ThreadLocalORMSession.flush_all()
+        # At this point, there is one ticket and it has discussion_disabled set to False
+        r = self.app.get('/bugs/search/?q=discussion_disabled_b:False')
+        assert_in('1 results', r)
+        assert_in('test discussion disabled ticket', r)
+
+        # Set discussion_disabled to True and search again
+        ticket_params['ticket_form.discussion_disabled'] = 'on'
+        self.app.post('/bugs/1/update_ticket_from_widget', ticket_params)
+        M.MonQTask.run_ready()
+        ThreadLocalORMSession.flush_all()
+        r = self.app.get('/bugs/search/?q=discussion_disabled_b:True')
+        assert_in('1 results', r)
+        assert_in('test discussion disabled ticket', r)
+
+        # Make sure there are no other tickets or false positives for good measure.
+        r = self.app.get('/bugs/search/?q=discussion_disabled_b:False')
+        assert_in('0 results', r)
+
     @td.with_tool('test', 'Tickets', 'doc-bugs')
     def test_two_trackers(self):
         summary = 'test two trackers'

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index aedf6ff..b846f42 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -127,6 +127,8 @@ def get_label(name):
         return 'Owner'
     if name == 'private':
         return 'Private'
+    if name == 'discussion_disabled':
+        return 'Discussion Disabled'
 
 
 def get_change_text(name, new_value, old_value):
@@ -371,9 +373,9 @@ class ForgeTrackerApp(Application):
         });""" % {'app_url': c.app.url}
 
     def has_custom_field(self, field):
-        '''Checks if given custom field is defined. (Custom field names
-        must start with '_'.)
-        '''
+        """Checks if given custom field is defined.
+        (Custom field names must start with '_'.)
+        """
         for f in self.globals.custom_fields:
             if f['name'] == field:
                 return True
@@ -424,7 +426,7 @@ class ForgeTrackerApp(Application):
                custom_fields=dict())
 
     def uninstall(self, project):
-        "Remove all the tool's artifacts from the database"
+        """Remove all the tool's artifacts from the database"""
         app_config_id = {'app_config_id': c.app.config._id}
         TM.TicketAttachment.query.remove(app_config_id)
         TM.Ticket.query.remove(app_config_id)
@@ -834,12 +836,12 @@ class RootController(BaseController, FeedController):
 
     @expose('jinja:allura:templates/markdown_syntax.html')
     def markdown_syntax(self):
-        'Static page explaining markdown.'
+        """Static page explaining markdown."""
         return dict()
 
     @expose('jinja:allura:templates/markdown_syntax_dialog.html')
     def markdown_syntax_dialog(self):
-        'Static page explaining markdown.'
+        """Static page explaining markdown."""
         return dict()
 
     @expose()
@@ -1408,10 +1410,18 @@ class TicketController(BaseController, FeedController):
             else:
                 self.ticket.assigned_to_id = None
             changes['assigned_to'] = self.ticket.assigned_to
+
+        # Register a key with the changelog -->
+        # Update the ticket property from the post_data -->
+        # Set the value of the changelog key again in case it has changed.
         changes['private'] = 'Yes' if self.ticket.private else 'No'
         self.ticket.private = post_data.get('private', False)
         changes['private'] = 'Yes' if self.ticket.private else 'No'
 
+        changes['discussion'] = 'disabled' if self.ticket.discussion_disabled else 'enabled'
+        self.ticket.discussion_disabled = post_data.get('discussion_disabled', False)
+        changes['discussion'] = 'disabled' if self.ticket.discussion_disabled else 'enabled'
+
         if 'attachment' in post_data:
             attachment = post_data['attachment']
             self.ticket.add_multiple_attachments(attachment)

http://git-wip-us.apache.org/repos/asf/allura/blob/f42961eb/ForgeTracker/forgetracker/widgets/ticket_form.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/widgets/ticket_form.py b/ForgeTracker/forgetracker/widgets/ticket_form.py
index d107aeb..476d9b3 100644
--- a/ForgeTracker/forgetracker/widgets/ticket_form.py
+++ b/ForgeTracker/forgetracker/widgets/ticket_form.py
@@ -117,6 +117,9 @@ class GenericTicketForm(ew.SimpleForm):
             ew.Checkbox(name='private', label='Mark as Private',
                         validator=v.AnonymousValidator(),
                         attrs={'class': 'unlabeled'}),
+            ew.Checkbox(name='discussion_disabled', label='Discussion Disabled',
+                        validator=fev.StringBool(),
+                        attrs={'class': 'unlabeled'}),
             ew.InputField(name='attachment', label='Attachment', field_type='file', attrs={
                           'multiple': 'True'}, validator=fev.FieldStorageUploadConverter(if_missing=None)),
             ffw.MarkdownEdit(name='comment', label='Comment',


[05/22] allura git commit: [#7865] add test

Posted by je...@apache.org.
[#7865] add test


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

Branch: refs/heads/ib/6017
Commit: 7ee773459f12a85f7d207c83a2a7825264dfc6fc
Parents: 8504385
Author: Dave Brondsema <da...@brondsema.net>
Authored: Wed Apr 1 10:46:50 2015 -0400
Committer: Heith Seewald <hs...@slashdotmedia.com>
Committed: Fri Apr 3 15:43:49 2015 -0400

----------------------------------------------------------------------
 ForgeGit/forgegit/tests/functional/test_controllers.py | 13 +++++++++++++
 1 file changed, 13 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/7ee77345/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 6a81b87..dacec3e 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -415,6 +415,19 @@ class TestRootController(_TestCase):
         assert_not_in('<span>bad</span>', r)
         assert_in('<span>README</span>', r)
 
+    def test_set_checkout_url(self):
+        r = self.app.get('/p/test/admin/src-git/checkout_url')
+        r.form['external_checkout_url'].value = 'http://foo.bar/baz'
+        r.form['merge_disabled'].checked = True
+        r = r.form.submit()
+        assert_equal(json.loads(self.webflash(r))['message'],
+                     "External checkout URL successfully changed. One-click merge disabled.")
+        # for some reason c.app.config.options has old values still
+        app_config = M.AppConfig.query.get(_id=c.app.config._id)
+        assert_equal(app_config.options['external_checkout_url'], 'http://foo.bar/baz')
+        assert_equal(app_config.options['merge_disabled'], True)
+
+
 
 class TestRestController(_TestCase):
 


[19/22] allura git commit: [#6017] ticket:751 Clear Ming's cache for attachments to generate a changelog

Posted by je...@apache.org.
[#6017] ticket:751 Clear Ming's cache for attachments to generate a changelog


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

Branch: refs/heads/ib/6017
Commit: 677fd677338c3a2c62c4674273f68ca2cc1186ad
Parents: 130ccec
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Apr 8 10:10:02 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 10:43:47 2015 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tracker_main.py | 6 ++++++
 1 file changed, 6 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/677fd677/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 2b4e807..7558eab 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -38,6 +38,7 @@ from bson.errors import InvalidId
 from webhelpers import feedgenerator as FG
 
 from ming import schema
+from ming.odm import session
 from ming.orm.ormsession import ThreadLocalORMSession
 from ming.utils import LazyProperty
 
@@ -1434,6 +1435,11 @@ class TicketController(BaseController, FeedController):
             attachment = post_data['attachment']
             changes['attachments'] = attachments_info(self.ticket.attachments)
             self.ticket.add_multiple_attachments(attachment)
+            # flush new attachments to db
+            session(self.ticket.attachment_class()).flush()
+            # self.ticket.attachments is ming's LazyProperty, we need to reset
+            # it's cache to fetch updated attachments here:
+            self.ticket.__dict__.pop('attachments')
             changes['attachments'] = attachments_info(self.ticket.attachments)
         for cf in c.app.globals.custom_fields or []:
             if 'custom_fields.' + cf.name in post_data:


[21/22] allura git commit: [#6017] ticket:756 Send notification when attachment is deleted

Posted by je...@apache.org.
[#6017] ticket:756 Send notification when attachment is deleted


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

Branch: refs/heads/ib/6017
Commit: b5a084fd7832afe989e5722340c44477f088285a
Parents: 9546f5a
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Apr 16 14:51:43 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 14:51:43 2015 +0000

----------------------------------------------------------------------
 Allura/allura/controllers/attachments.py  | 19 ++++----
 ForgeTracker/forgetracker/tracker_main.py | 60 ++++++++++++++++++++------
 2 files changed, 57 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/b5a084fd/Allura/allura/controllers/attachments.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/attachments.py b/Allura/allura/controllers/attachments.py
index 4ab3b92..a648873 100644
--- a/Allura/allura/controllers/attachments.py
+++ b/Allura/allura/controllers/attachments.py
@@ -73,17 +73,20 @@ class AttachmentController(BaseController):
             raise exc.HTTPNotFound
         return attachment
 
+    def handle_post(self, delete, **kw):
+        require_access(self.artifact, self.edit_perm)
+        if delete:
+            self.attachment.delete()
+            try:
+                if self.thumbnail:
+                    self.thumbnail.delete()
+            except exc.HTTPNotFound:
+                pass
+
     @expose()
     def index(self, delete=False, **kw):
         if request.method == 'POST':
-            require_access(self.artifact, self.edit_perm)
-            if delete:
-                self.attachment.delete()
-                try:
-                    if self.thumbnail:
-                        self.thumbnail.delete()
-                except exc.HTTPNotFound:
-                    pass
+            self.handle_post(delete, **kw)
             redirect(request.referer)
         embed = False
         if self.attachment.content_type and self.attachment.content_type.startswith('image/'):

http://git-wip-us.apache.org/repos/asf/allura/blob/b5a084fd/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index f8a5680..9fc39d8 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -148,6 +148,33 @@ def get_change_text(name, new_value, old_value):
         comment=None)
 
 
+def attachments_info(attachments):
+    text = []
+    for attach in attachments:
+        text.append("{} ({}; {})".format(
+            attach.filename,
+            h.do_filesizeformat(attach.length),
+            attach.content_type))
+    return "\n".join(text)
+
+
+def render_changes(changes, comment=None):
+    """
+    Render ticket changes given instanse of :class changelog:
+
+    Returns tuple (post_text, notification_text)
+    """
+    template = pkg_resources.resource_filename(
+        'forgetracker', 'data/ticket_changed_tmpl')
+    render = partial(
+        h.render_genshi_plaintext,
+        template,
+        changelist=changes.get_changed())
+    post_text = render(comment=None)
+    notification_text = render(comment=comment) if comment else None
+    return post_text, notification_text
+
+
 def _my_trackers(user, current_tracker_app_config):
     '''Collect all 'Tickets' instances in all user's projects
     for which user has admin permissions.
@@ -1390,15 +1417,6 @@ class TicketController(BaseController, FeedController):
 
     @require_post()
     def _update_ticket(self, post_data):
-        def attachments_info(attachments):
-            text = []
-            for attach in attachments:
-                text.append("{} ({}; {})".format(
-                    attach.filename,
-                    h.do_filesizeformat(attach.length),
-                    attach.content_type))
-            return "\n".join(text)
-
         require_access(self.ticket, 'update')
         changes = changelog()
         comment = post_data.pop('comment', None)
@@ -1469,11 +1487,7 @@ class TicketController(BaseController, FeedController):
                 self.ticket.custom_fields[cf.name] = value
                 changes[cf.label] = cf_val(cf)
 
-        tpl = pkg_resources.resource_filename(
-            'forgetracker', 'data/ticket_changed_tmpl')
-        render = partial(h.render_genshi_plaintext, tpl, changelist=changes.get_changed())
-        post_text = render(comment=None)
-        notification_text = render(comment=comment) if comment else None
+        post_text, notification_text = render_changes(changes, comment)
         thread = self.ticket.discussion_thread
         thread.add_post(text=post_text, is_meta=True,
                         notification_text=notification_text)
@@ -1552,6 +1566,24 @@ class AttachmentController(att.AttachmentController):
     AttachmentClass = TM.TicketAttachment
     edit_perm = 'update'
 
+    def handle_post(self, delete, **kw):
+        old_attachments = attachments_info(self.artifact.attachments)
+        super(AttachmentController, self).handle_post(delete, **kw)
+        if delete:
+            session(self.artifact.attachment_class()).flush()
+            # self.artifact.attachments is ming's LazyProperty, we need to reset
+            # it's cache to fetch updated attachments here:
+            self.artifact.__dict__.pop('attachments')
+            new_attachments = attachments_info(self.artifact.attachments)
+            changes = changelog()
+            changes['attachments'] = old_attachments
+            changes['attachments'] = new_attachments
+            post_text, notification = render_changes(changes)
+            self.artifact.discussion_thread.add_post(
+                text=post_text,
+                is_meta=True,
+                notification_text=notification)
+
 
 class AttachmentsController(att.AttachmentsController):
     AttachmentControllerClass = AttachmentController


[06/22] allura git commit: [#7865] add per-tool option to disable one-click merging

Posted by je...@apache.org.
[#7865] add per-tool option to disable one-click merging


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

Branch: refs/heads/ib/6017
Commit: 85043855baeb1cc23e462ed2678295526b173efa
Parents: 3a1c6cc
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Mar 31 18:35:31 2015 -0400
Committer: Heith Seewald <hs...@slashdotmedia.com>
Committed: Fri Apr 3 15:43:49 2015 -0400

----------------------------------------------------------------------
 Allura/allura/lib/repository.py                | 23 +++++++++++++++++----
 Allura/allura/model/repository.py              |  2 ++
 Allura/allura/templates/repo/checkout_url.html | 12 +++++++++++
 3 files changed, 33 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/85043855/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index 8fd414f..31e1395 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -20,10 +20,11 @@ from urllib import quote
 
 from pylons import tmpl_context as c, app_globals as g
 from pylons import request
-from tg import expose, redirect, flash, validate
+from tg import expose, redirect, flash, validate, config
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from webob import exc
 from bson import ObjectId
+from paste.deploy.converters import asbool
 
 from ming.utils import LazyProperty
 
@@ -262,20 +263,34 @@ class RepoAdminController(DefaultAdminController):
     @without_trailing_slash
     @expose('jinja:allura:templates/repo/checkout_url.html')
     def checkout_url(self):
-        return dict(app=self.app)
+        return dict(app=self.app,
+                    merge_allowed=not asbool(config.get('scm.merge.{}.disabled'.format(self.app.config.tool_name))),
+                    )
 
     @without_trailing_slash
     @expose()
     @require_post()
     @validate({'external_checkout_url': v.NonHttpUrl})
     def set_checkout_url(self, **post_data):
+        flash_msgs = []
         external_checkout_url = (post_data.get('external_checkout_url') or '').strip()
         if 'external_checkout_url' not in c.form_errors:
             if (self.app.config.options.get('external_checkout_url') or '') != external_checkout_url:
                 self.app.config.options.external_checkout_url = external_checkout_url
-                flash("External checkout URL successfully changed")
+                flash_msgs.append("External checkout URL successfully changed.")
         else:
-            flash("Invalid external checkout URL: %s" % c.form_errors['external_checkout_url'], "error")
+            flash_msgs.append("Invalid external checkout URL: %s." % c.form_errors['external_checkout_url'])
+
+        merge_disabled = bool(post_data.get('merge_disabled'))
+        if merge_disabled != self.app.config.options.get('merge_disabled', False):
+            self.app.config.options.merge_disabled = merge_disabled
+            flash_msgs.append('One-click merge {}.'.format('disabled' if merge_disabled else 'enabled'))
+
+        if flash_msgs:
+            message = ' '.join(flash_msgs)
+            flash(message,
+                  'error' if 'Invalid' in message else 'ok')
+
         redirect(c.project.url() + 'admin/tools')
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/85043855/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 7933a27..96c9358 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -831,6 +831,8 @@ class MergeRequest(VersionedArtifact, ActivityObject):
             return False
         if not h.has_access(c.app, 'write'):
             return False
+        if self.app.config.options.get('merge_disabled'):
+            return False
         return True
 
     def can_merge(self):

http://git-wip-us.apache.org/repos/asf/allura/blob/85043855/Allura/allura/templates/repo/checkout_url.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/checkout_url.html b/Allura/allura/templates/repo/checkout_url.html
index 8eaafd9..450d912 100644
--- a/Allura/allura/templates/repo/checkout_url.html
+++ b/Allura/allura/templates/repo/checkout_url.html
@@ -26,6 +26,18 @@
         Override the checkout URL with an external one.  This is useful if this repository is a mirror
         of another, canonical repository.
     </div>
+    {% if merge_allowed %}
+        <div class="grid-13">&nbsp;</div>
+        <div class="grid-9">
+            <label>
+            <input type="checkbox" name="merge_disabled" {% if app.config.options.get('merge_disabled') %}checked="checked"{% endif %}/>
+            Disable one-click merge via web.
+            </label>
+        </div>
+        <div class="grid-13">
+            You may want to disable one-click merge, if this repository is a mirror and not the primary repository.
+        </div>
+    {% endif %}
     <div class="grid-13">&nbsp;</div>
     <hr>
     <div class="grid-13">&nbsp;</div>


[12/22] allura git commit: [#7864] update pyflakes to avoid invalid warnings from @property.setter dupe fn name; fix new pyflakes warnings

Posted by je...@apache.org.
[#7864] update pyflakes to avoid invalid warnings from @property.setter dupe fn name; fix new pyflakes warnings


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

Branch: refs/heads/ib/6017
Commit: 567b1d41befe2571e0f7cdd02f061282b1427ffd
Parents: c72837d
Author: Dave Brondsema <da...@brondsema.net>
Authored: Wed Apr 8 16:04:12 2015 -0400
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Apr 15 11:04:03 2015 +0000

----------------------------------------------------------------------
 Allura/allura/command/show_models.py        | 4 ++--
 Allura/allura/controllers/repository.py     | 8 ++++----
 Allura/allura/controllers/site_admin.py     | 6 +++---
 Allura/allura/lib/helpers.py                | 4 ++--
 Allura/allura/model/project.py              | 2 +-
 Allura/allura/tests/test_commands.py        | 7 +++----
 Allura/allura/tests/unit/test_repo.py       | 6 +++---
 ForgeSVN/forgesvn/model/svn.py              | 3 +--
 ForgeTracker/forgetracker/import_support.py | 4 ++--
 ForgeTracker/forgetracker/tracker_main.py   | 6 +++---
 requirements.txt                            | 2 +-
 11 files changed, 25 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/command/show_models.py
----------------------------------------------------------------------
diff --git a/Allura/allura/command/show_models.py b/Allura/allura/command/show_models.py
index 36aa29e..d941e9e 100644
--- a/Allura/allura/command/show_models.py
+++ b/Allura/allura/command/show_models.py
@@ -359,8 +359,8 @@ def dump_cls(depth, cls):
 
 def dfs(root, graph, depth=0):
     yield depth, root
-    for c in graph[root][1]:
-        for r in dfs(c, graph, depth + 1):
+    for node in graph[root][1]:
+        for r in dfs(node, graph, depth + 1):
             yield r
 
 

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index 34210ce..b36449c 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -254,8 +254,8 @@ class RepoRootController(BaseController, FeedController):
         columns = []
 
         def find_column(columns):
-            for i, c in enumerate(columns):
-                if c is None:
+            for i, col in enumerate(columns):
+                if col is None:
                     return i
             columns.append(None)
             return len(columns) - 1
@@ -757,8 +757,8 @@ def topo_sort(children, parents, dates, head_ids):
         visited.add(next)
         yield next
         for p in parents[next]:
-            for c in children[p]:
-                if c not in visited:
+            for child in children[p]:
+                if child not in visited:
                     break
             else:
                 to_visit.append(p)

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index a85680b..ee47274 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -275,18 +275,18 @@ class SiteAdminController(object):
                     _id = obj['id'].split('#')[1]
                     obj['object'] = mongo_objects.get(_id)
                 # Some objects can be deleted, but still have index in solr, should skip those
-                objects = [obj for obj in objects if obj.get('object')]
+                objects = [o for o in objects if o.get('object')]
 
         def convert_fields(obj):
             # throw the type away (e.g. '_s' from 'url_s')
             result = {}
-            for k,v in obj.iteritems():
+            for k,val in obj.iteritems():
                 name = k.rsplit('_', 1)
                 if len(name) == 2:
                     name = name[0]
                 else:
                     name = k
-                result[name] = v
+                result[name] = val
             return result
 
         return {

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/lib/helpers.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index 3cd14fb..36932a3 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -583,8 +583,8 @@ def twophase_transaction(*engines):
     txns = []
     to_rollback = []
     try:
-        for c in connections:
-            txn = c.begin_twophase()
+        for conn in connections:
+            txn = conn.begin_twophase()
             txns.append(txn)
             to_rollback.append(txn)
         yield

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/model/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index b66ecec..db460a1 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -551,7 +551,7 @@ class Project(SearchIndexable, MappedClass, ActivityNode, ActivityObject):
             try:
                 App = ac.load()
             # If so, we don't want it listed
-            except KeyError as e:
+            except KeyError:
                 log.exception('AppConfig %s references invalid tool %s',
                               ac._id, ac.tool_name)
                 continue

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/tests/test_commands.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_commands.py b/Allura/allura/tests/test_commands.py
index 1c298c5..f6800b7 100644
--- a/Allura/allura/tests/test_commands.py
+++ b/Allura/allura/tests/test_commands.py
@@ -193,11 +193,10 @@ class TestEnsureIndexCommand(object):
         cmd._update_indexes(collection, indexes)
 
         collection_call_order = {}
-        for i, call in enumerate(collection.mock_calls):
-            method_name = call[0]
+        for i, call_ in enumerate(collection.mock_calls):
+            method_name = call_[0]
             collection_call_order[method_name] = i
-        assert collection_call_order['ensure_index'] < collection_call_order[
-            'drop_index'], collection.mock_calls
+        assert collection_call_order['ensure_index'] < collection_call_order['drop_index'], collection.mock_calls
 
     def test_update_indexes_unique_changes(self):
         collection = Mock(name='collection')

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/Allura/allura/tests/unit/test_repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/unit/test_repo.py b/Allura/allura/tests/unit/test_repo.py
index 7411db5..9008571 100644
--- a/Allura/allura/tests/unit/test_repo.py
+++ b/Allura/allura/tests/unit/test_repo.py
@@ -42,9 +42,9 @@ class TestCommitRunBuilder(unittest.TestCase):
             M.repository.CommitDoc.make(dict(
                 _id=str(i)))
             for i in range(10)]
-        for p, c in zip(commits, commits[1:]):
-            p.child_ids = [c._id]
-            c.parent_ids = [p._id]
+        for p, com in zip(commits, commits[1:]):
+            p.child_ids = [com._id]
+            com.parent_ids = [p._id]
         for ci in commits:
             ci.m.save()
         self.commits = commits

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/ForgeSVN/forgesvn/model/svn.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/model/svn.py b/ForgeSVN/forgesvn/model/svn.py
index 4594a2f..cd410e3 100644
--- a/ForgeSVN/forgesvn/model/svn.py
+++ b/ForgeSVN/forgesvn/model/svn.py
@@ -360,8 +360,7 @@ class SVNImplementation(M.RepositoryImplementation):
             if not oid.startswith(prefix):
                 break
             seen_oids.add(oid)
-        return [
-            oid for oid in oids if oid not in seen_oids]
+        return [o for o in oids if o not in seen_oids]
 
     def refresh_commit_info(self, oid, seen_object_ids, lazy=True):
         from allura.model.repository import CommitDoc

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/ForgeTracker/forgetracker/import_support.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/import_support.py b/ForgeTracker/forgetracker/import_support.py
index c1865e9..affe94b 100644
--- a/ForgeTracker/forgetracker/import_support.py
+++ b/ForgeTracker/forgetracker/import_support.py
@@ -282,8 +282,8 @@ class ImportSupport(object):
         for a in artifacts:
             users.add(a['submitter'])
             users.add(a['assigned_to'])
-            for c in a['comments']:
-                users.add(c['submitter'])
+            for com in a['comments']:
+                users.add(com['submitter'])
         return users
 
     def find_unknown_users(self, users):

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index b846f42..4f20d88 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -57,7 +57,7 @@ from allura.lib.widgets import form_fields as ffw
 from allura.lib.widgets.subscriptions import SubscribeForm
 from allura.lib.plugin import ImportIdConverter
 from allura.controllers import AppDiscussionController, AppDiscussionRestController
-from allura.controllers import attachments as ac
+from allura.controllers import attachments as att
 from allura.controllers import BaseController
 from allura.controllers.feed import FeedArgs, FeedController
 
@@ -1531,12 +1531,12 @@ class TicketController(BaseController, FeedController):
         }
 
 
-class AttachmentController(ac.AttachmentController):
+class AttachmentController(att.AttachmentController):
     AttachmentClass = TM.TicketAttachment
     edit_perm = 'update'
 
 
-class AttachmentsController(ac.AttachmentsController):
+class AttachmentsController(att.AttachmentsController):
     AttachmentControllerClass = AttachmentController
 
 NONALNUM_RE = re.compile(r'\W+')

http://git-wip-us.apache.org/repos/asf/allura/blob/567b1d41/requirements.txt
----------------------------------------------------------------------
diff --git a/requirements.txt b/requirements.txt
index 6de0f60..5664528 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -74,7 +74,7 @@ datadiff==1.1.5
 ipython==1.2.1
 mock==1.0.1
 nose==1.3.4
-pyflakes==0.5.0
+pyflakes==0.8.1
 WebTest==1.4.0
 testfixtures==3.0.0
 q==2.3


[07/22] allura git commit: [#7865] factor out a merge_allowed method and check for sitewide config to disable it

Posted by je...@apache.org.
[#7865] factor out a merge_allowed method and check for sitewide config to disable it


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

Branch: refs/heads/ib/6017
Commit: 3a1c6cc0b154bdecc8ccf04ddce365a6cfff91b3
Parents: d503b14
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Mar 31 17:12:46 2015 -0400
Committer: Heith Seewald <hs...@slashdotmedia.com>
Committed: Fri Apr 3 15:43:49 2015 -0400

----------------------------------------------------------------------
 Allura/allura/controllers/repository.py                |  3 +--
 Allura/allura/model/repository.py                      | 13 ++++++++++++-
 Allura/allura/templates/repo/merge_request.html        |  2 +-
 Allura/development.ini                                 |  4 ++++
 ForgeGit/forgegit/tests/functional/test_controllers.py |  4 ++++
 5 files changed, 22 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/3a1c6cc0/Allura/allura/controllers/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/repository.py b/Allura/allura/controllers/repository.py
index c08ec99..34210ce 100644
--- a/Allura/allura/controllers/repository.py
+++ b/Allura/allura/controllers/repository.py
@@ -445,8 +445,7 @@ class MergeRequestController(object):
     @expose()
     @require_post()
     def merge(self):
-        require_access(c.app, 'write')
-        if self.req.status != 'open' or not self.req.can_merge():
+        if not self.req.merge_allowed(c.user) or not self.req.can_merge():
             raise exc.HTTPNotFound
         self.req.merge()
         redirect(self.req.url())

http://git-wip-us.apache.org/repos/asf/allura/blob/3a1c6cc0/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 362f725..7933a27 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -33,7 +33,7 @@ from itertools import chain
 from difflib import SequenceMatcher, unified_diff
 
 import tg
-from paste.deploy.converters import asint
+from paste.deploy.converters import asint, asbool
 from pylons import tmpl_context as c
 from pylons import app_globals as g
 import pymongo
@@ -822,6 +822,17 @@ class MergeRequest(VersionedArtifact, ActivityObject):
                 self.request_number, self.project.name, self.app.repo.name))
         return result
 
+    def merge_allowed(self, user):
+        if not c.app.forkable:
+            return False
+        if self.status != 'open':
+            return False
+        if asbool(tg.config.get('scm.merge.{}.disabled'.format(self.app.config.tool_name))):
+            return False
+        if not h.has_access(c.app, 'write'):
+            return False
+        return True
+
     def can_merge(self):
         if not self.app.forkable:
             return False

http://git-wip-us.apache.org/repos/asf/allura/blob/3a1c6cc0/Allura/allura/templates/repo/merge_request.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 1520270..928a0db 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -57,7 +57,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
 
     <div>{{g.markdown.convert(req.description)}}</div>
 
-    {% if req.status == 'open' and c.app.forkable and h.has_access(c.app, 'write')() %}
+    {% if req.merge_allowed(c.user) %}
       {% set can_merge = req.can_merge() %}
       <div class="grid-19">
         <form action="merge" method="POST">

http://git-wip-us.apache.org/repos/asf/allura/blob/3a1c6cc0/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index bda8d37..b6c3154 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -243,6 +243,10 @@ scm.repos.tarball.zip_binary = /usr/bin/zip
 ;scm.import.retry_count = 50
 ;scm.import.retry_sleep_secs = 5
 
+; One-click merge is enabled by default, but can be turned off on for each type of repo
+;scm.merge.git.disabled = true
+;scm.merge.hg.disabled = true
+
 bulk_export_path = /tmp/bulk_export/{nbhd}/{project}
 bulk_export_filename = {project}-backup-{date:%Y-%m-%d-%H%M%S}.zip
 bulk_export_download_instructions = Sample instructions for {project}

http://git-wip-us.apache.org/repos/asf/allura/blob/3a1c6cc0/ForgeGit/forgegit/tests/functional/test_controllers.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/functional/test_controllers.py b/ForgeGit/forgegit/tests/functional/test_controllers.py
index 772ac05..6a81b87 100644
--- a/ForgeGit/forgegit/tests/functional/test_controllers.py
+++ b/ForgeGit/forgegit/tests/functional/test_controllers.py
@@ -542,6 +542,10 @@ class TestFork(_TestCase):
         assert 'git merge {}'.format(c_id) in merge_instructions
         assert_in('less than 1 minute ago', r.html.findAll('p')[0].getText())
 
+        merge_form = r.html.find('form', action='merge')
+        assert merge_form
+        assert_in('Merge request has no conflicts. You can merge automatically.', merge_form.getText())
+
     def test_merge_request_detail_noslash(self):
         self._request_merge()
         r = self.app.get('/p/test/src-git/merge-requests/1', status=302)


[09/22] allura git commit: Make svn changed paths order deterministic

Posted by je...@apache.org.
Make svn changed paths order deterministic


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

Branch: refs/heads/ib/6017
Commit: 747cad09101ce4b92622a042ead4c1727b4170cd
Parents: 33dcb08
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Apr 9 18:49:05 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 9 18:49:05 2015 +0300

----------------------------------------------------------------------
 ForgeSVN/forgesvn/model/svn.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/747cad09/ForgeSVN/forgesvn/model/svn.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/model/svn.py b/ForgeSVN/forgesvn/model/svn.py
index cf70b7d..4594a2f 100644
--- a/ForgeSVN/forgesvn/model/svn.py
+++ b/ForgeSVN/forgesvn/model/svn.py
@@ -22,6 +22,7 @@ import string
 import logging
 import subprocess
 import time
+import operator as op
 from subprocess import Popen, PIPE
 from hashlib import sha1
 from cStringIO import StringIO
@@ -801,7 +802,7 @@ class SVNImplementation(M.RepositoryImplementation):
             return result
         if len(log_info) == 0:
             return result
-        paths = log_info[0].changed_paths
+        paths = sorted(log_info[0].changed_paths, key=op.itemgetter('path'))
         result['total'] = len(paths)
         for p in paths[start:end]:
             if p['copyfrom_path'] is not None:


[10/22] allura git commit: [#7864] remove annoying ew.render logging, by configuring logging before initializing EW

Posted by je...@apache.org.
[#7864] remove annoying ew.render logging, by configuring logging before initializing EW


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

Branch: refs/heads/ib/6017
Commit: fa548be3fae8c90b7d7ef889b11604fac169cd58
Parents: 225dc73
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Apr 7 15:36:38 2015 -0400
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Apr 15 11:04:02 2015 +0000

----------------------------------------------------------------------
 AlluraTest/alluratest/controller.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/fa548be3/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 0f13c5d..4a8f983 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -88,10 +88,10 @@ def setup_basic_test(config=None, app_name=DFL_APP_NAME):
         conf_dir = tg.config.here
     except AttributeError:
         conf_dir = os.getcwd()
-    ew.TemplateEngine.initialize({})
     test_file = os.path.join(conf_dir, get_config_file(config))
     cmd = SetupCommand('setup-app')
     cmd.run([test_file])
+    ew.TemplateEngine.initialize({})
 
     # run all tasks, e.g. indexing from bootstrap operations
     while M.MonQTask.run_ready('setup'):


[13/22] allura git commit: [#7864] remove Property with nested get/setters, since sys.settrace causes pydev/PyCharm debugger not to work

Posted by je...@apache.org.
[#7864] remove Property with nested get/setters, since sys.settrace causes pydev/PyCharm debugger not to work


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

Branch: refs/heads/ib/6017
Commit: c72837df2062465c9f75bcde6bb747df38acf243
Parents: fa548be
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Apr 7 15:37:53 2015 -0400
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Apr 15 11:04:03 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/decorators.py  | 18 ---------
 ForgeBlog/forgeblog/main.py      | 40 +++++++++---------
 ForgeWiki/forgewiki/wiki_main.py | 76 +++++++++++++++++------------------
 3 files changed, 58 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/c72837df/Allura/allura/lib/decorators.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/decorators.py b/Allura/allura/lib/decorators.py
index d472758..cb5cfc5 100644
--- a/Allura/allura/lib/decorators.py
+++ b/Allura/allura/lib/decorators.py
@@ -188,24 +188,6 @@ class log_action(object):  # pragma no cover
         return extra
 
 
-def Property(function):
-    '''Decorator to easily assign descriptors based on sub-function names
-    See <http://code.activestate.com/recipes/410698-property-decorator-for-python-24/>
-    '''
-    keys = 'fget', 'fset', 'fdel'
-    func_locals = {'doc': function.__doc__}
-
-    def probeFunc(frame, event, arg):
-        if event == 'return':
-            locals = frame.f_locals
-            func_locals.update(dict((k, locals.get(k)) for k in keys))
-            sys.settrace(None)
-        return probeFunc
-    sys.settrace(probeFunc)
-    function()
-    return property(**func_locals)
-
-
 def getattr_(obj, name, default_thunk):
     "Similar to .setdefault in dictionaries."
     try:

http://git-wip-us.apache.org/repos/asf/allura/blob/c72837df/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 93cd01b..160b94e 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -39,7 +39,7 @@ from allura.app import Application, SitemapEntry
 from allura.app import DefaultAdminController
 from allura.lib import helpers as h
 from allura.lib.search import search_app
-from allura.lib.decorators import require_post, Property
+from allura.lib.decorators import require_post
 from allura.lib.security import has_access, require_access
 from allura.lib import widgets as w
 from allura.lib.widgets.subscriptions import SubscribeForm
@@ -106,25 +106,25 @@ class ForgeBlogApp(Application):
         self.admin = BlogAdminController(self)
         self.api_root = RootRestController()
 
-    @Property
-    def external_feeds_list():
-        def fget(self):
-            globals = BM.Globals.query.get(app_config_id=self.config._id)
-            if globals is not None:
-                external_feeds = globals.external_feeds
-            else:
-                external_feeds = self.default_external_feeds
-            return external_feeds
-
-        def fset(self, new_external_feeds):
-            globals = BM.Globals.query.get(app_config_id=self.config._id)
-            if globals is not None:
-                globals.external_feeds = new_external_feeds
-            elif len(new_external_feeds) > 0:
-                globals = BM.Globals(
-                    app_config_id=self.config._id, external_feeds=new_external_feeds)
-            if globals is not None:
-                session(globals).flush()
+    @property
+    def external_feeds_list(self):
+        globals = BM.Globals.query.get(app_config_id=self.config._id)
+        if globals is not None:
+            external_feeds = globals.external_feeds
+        else:
+            external_feeds = self.default_external_feeds
+        return external_feeds
+
+    @external_feeds_list.setter
+    def external_feeds_list(self, new_external_feeds):
+        globals = BM.Globals.query.get(app_config_id=self.config._id)
+        if globals is not None:
+            globals.external_feeds = new_external_feeds
+        elif len(new_external_feeds) > 0:
+            globals = BM.Globals(
+                app_config_id=self.config._id, external_feeds=new_external_feeds)
+        if globals is not None:
+            session(globals).flush()
 
     def main_menu(self):
         return [SitemapEntry(self.config.options.mount_label, '.')]

http://git-wip-us.apache.org/repos/asf/allura/blob/c72837df/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index ecbddef..a5b3d3e 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -35,7 +35,7 @@ from allura import model as M
 from allura.lib import helpers as h
 from allura.app import Application, SitemapEntry, DefaultAdminController
 from allura.lib.search import search_app
-from allura.lib.decorators import require_post, Property
+from allura.lib.decorators import require_post
 from allura.lib.security import require_access, has_access
 from allura.controllers import AppDiscussionController, BaseController, AppDiscussionRestController
 from allura.controllers import DispatchIndex
@@ -129,25 +129,25 @@ class ForgeWikiApp(Application):
             log.exception('Error getting artifact %s', topic)
         self.handle_artifact_message(page, message)
 
-    @Property
-    def root_page_name():
-        def fget(self):
-            globals = WM.Globals.query.get(app_config_id=self.config._id)
-            if globals is not None:
-                page_name = globals.root
-            else:
-                page_name = self.default_root_page_name
-            return page_name
-
-        def fset(self, new_root_page_name):
-            globals = WM.Globals.query.get(app_config_id=self.config._id)
-            if globals is not None:
-                globals.root = new_root_page_name
-            elif new_root_page_name != self.default_root_page_name:
-                globals = WM.Globals(
-                    app_config_id=self.config._id, root=new_root_page_name)
-            if globals is not None:
-                session(globals).flush(globals)
+    @property
+    def root_page_name(self):
+        globals = WM.Globals.query.get(app_config_id=self.config._id)
+        if globals is not None:
+            page_name = globals.root
+        else:
+            page_name = self.default_root_page_name
+        return page_name
+
+    @root_page_name.setter
+    def root_page_name(self, new_root_page_name):
+        globals = WM.Globals.query.get(app_config_id=self.config._id)
+        if globals is not None:
+            globals.root = new_root_page_name
+        elif new_root_page_name != self.default_root_page_name:
+            globals = WM.Globals(
+                app_config_id=self.config._id, root=new_root_page_name)
+        if globals is not None:
+            session(globals).flush(globals)
 
     def default_root_page_text(self):
         return """Welcome to your wiki!
@@ -159,29 +159,29 @@ The wiki uses [Markdown](%s) syntax.
 [[members limit=20]]
 """ % (self.url + 'markdown_syntax/')
 
-    @Property
-    def show_discussion():
-        def fget(self):
-            return self.config.options.get('show_discussion', True)
+    @property
+    def show_discussion(self):
+        return self.config.options.get('show_discussion', True)
 
-        def fset(self, show):
-            self.config.options['show_discussion'] = bool(show)
+    @show_discussion.setter
+    def show_discussion(self, show):
+        self.config.options['show_discussion'] = bool(show)
 
-    @Property
-    def show_left_bar():
-        def fget(self):
-            return self.config.options.get('show_left_bar', True)
+    @property
+    def show_left_bar(self):
+        return self.config.options.get('show_left_bar', True)
 
-        def fset(self, show):
-            self.config.options['show_left_bar'] = bool(show)
+    @show_left_bar.setter
+    def show_left_bar(self, show):
+        self.config.options['show_left_bar'] = bool(show)
 
-    @Property
-    def show_right_bar():
-        def fget(self):
-            return self.config.options.get('show_right_bar', True)
+    @property
+    def show_right_bar(self):
+        return self.config.options.get('show_right_bar', True)
 
-        def fset(self, show):
-            self.config.options['show_right_bar'] = bool(show)
+    @show_right_bar.setter
+    def show_right_bar(self, show):
+        self.config.options['show_right_bar'] = bool(show)
 
     def main_menu(self):
         '''Apps should provide their entries to be added to the main nav


[14/22] allura git commit: [#7864] add new test data files to rat-excludes.txt

Posted by je...@apache.org.
[#7864] add new test data files to rat-excludes.txt


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

Branch: refs/heads/ib/6017
Commit: 89981f0ae4fef4f1d3821359a195068bd9a5cd41
Parents: 567b1d4
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Apr 15 14:22:18 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Apr 15 14:22:18 2015 +0000

----------------------------------------------------------------------
 rat-excludes.txt | 2 ++
 1 file changed, 2 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/89981f0a/rat-excludes.txt
----------------------------------------------------------------------
diff --git a/rat-excludes.txt b/rat-excludes.txt
index 2d62d50..6be4d29 100644
--- a/rat-excludes.txt
+++ b/rat-excludes.txt
@@ -39,6 +39,8 @@ ForgeGit/forgegit/data/post-receive_tmpl
 ForgeSVN/forgesvn/tests/data/
 ForgeImporters/forgeimporters/tests/data/google/empty-issue.html
 ForgeImporters/forgeimporters/tests/data/google/test-issue.html
+ForgeImporters/forgeimporters/tests/data/google/test-issue-first-page.html
+ForgeImporters/forgeimporters/tests/data/google/test-issue-prev-page.html
 ForgeImporters/forgeimporters/trac/tests/data/test-list.csv
 ForgeImporters/forgeimporters/trac/tests/data/test-list.html
 ForgeTracker/forgetracker/widgets/resources/js/jquery.multiselect.min.js


[17/22] allura git commit: [#6017] ticket:751 Add attachment information to ticket changelog entries

Posted by je...@apache.org.
[#6017] ticket:751 Add attachment information to ticket changelog entries


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

Branch: refs/heads/ib/6017
Commit: 130cceccd0c4ce69e0b37cb1977df5bb2b33c20b
Parents: a3f7d44
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Tue Apr 7 17:22:16 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 10:43:47 2015 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tracker_main.py | 10 ++++++++++
 1 file changed, 10 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/130ccecc/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 4f20d88..2b4e807 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1389,6 +1389,14 @@ class TicketController(BaseController, FeedController):
 
     @require_post()
     def _update_ticket(self, post_data):
+        def attachments_info(attachments):
+            text = ''
+            for attach in attachments:
+                text = "%s %s (%s; %s) " % (
+                    text, attach.filename,
+                    h.do_filesizeformat(attach.length), attach.content_type)
+            return text
+
         require_access(self.ticket, 'update')
         changes = changelog()
         comment = post_data.pop('comment', None)
@@ -1424,7 +1432,9 @@ class TicketController(BaseController, FeedController):
 
         if 'attachment' in post_data:
             attachment = post_data['attachment']
+            changes['attachments'] = attachments_info(self.ticket.attachments)
             self.ticket.add_multiple_attachments(attachment)
+            changes['attachments'] = attachments_info(self.ticket.attachments)
         for cf in c.app.globals.custom_fields or []:
             if 'custom_fields.' + cf.name in post_data:
                 value = post_data['custom_fields.' + cf.name]


[11/22] allura git commit: [#7864] handle multiple pages of Google Code comments

Posted by je...@apache.org.
[#7864] handle multiple pages of Google Code comments

The new test .html files are based off of the existing test-issue.html and are very similar.

Some tests were moved around to accomodate different test setup for those using iter_comments and
affected by the changes.


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

Branch: refs/heads/ib/6017
Commit: 225dc73c51dce1de27de0514cf58f222d3f1a351
Parents: 747cad0
Author: Dave Brondsema <da...@brondsema.net>
Authored: Tue Apr 7 10:22:46 2015 -0400
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Wed Apr 15 11:04:02 2015 +0000

----------------------------------------------------------------------
 .../forgeimporters/google/__init__.py           |  30 +-
 .../data/google/test-issue-first-page.html      | 548 +++++++++++++++++++
 .../tests/data/google/test-issue-prev-page.html | 431 +++++++++++++++
 .../tests/data/google/test-issue.html           |   7 +
 .../tests/google/functional/test_tracker.py     |   7 +-
 .../tests/google/test_extractor.py              | 207 ++++---
 6 files changed, 1148 insertions(+), 82 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/google/__init__.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/__init__.py b/ForgeImporters/forgeimporters/google/__init__.py
index b848151..302544e 100644
--- a/ForgeImporters/forgeimporters/google/__init__.py
+++ b/ForgeImporters/forgeimporters/google/__init__.py
@@ -304,9 +304,9 @@ class GoogleCodeProjectExtractor(ProjectExtractor):
         return self.page.find(id='hc0').find('span', 'date').get('title')
 
     def get_issue_mod_date(self):
-        comments = self.page.findAll('div', 'issuecomment')
+        comments = list(self.iter_comments())
         if comments:
-            last_update = Comment(comments[-1], self.project_name)
+            last_update = comments[-1]
             return last_update.created_date
         else:
             return self.get_issue_created_date()
@@ -346,8 +346,30 @@ class GoogleCodeProjectExtractor(ProjectExtractor):
         return 0
 
     def iter_comments(self):
-        for comment in self.page.findAll('div', 'issuecomment'):
-            yield Comment(comment, self.project_name)
+        # first, get all pages if there are multiple pages of comments
+        looking_for_comment_pages = True
+        comment_page_urls = [self.url]
+        while looking_for_comment_pages:
+            first_comment = self.page.find('div', 'vt issuecomment')
+            looking_for_comment_pages = False
+            if first_comment and 'cursor_off' not in first_comment['class']:
+                # this is not a real comment, just forward/back links
+                for link in first_comment.findAll('a'):
+                    if link.text.startswith('Older'):
+                        prev_comments_page = urljoin(self.url, link['href'])
+                        comment_page_urls.insert(0, prev_comments_page)
+                        looking_for_comment_pages = True
+                        self.get_page(prev_comments_page)  # prep for next iteration of loop
+
+        # then go through those to get the actual comments
+        for comment_page_url in comment_page_urls:
+            self.get_page(comment_page_url)
+            # regular comments have cursor_off class
+            for comment in self.page.findAll('div', 'cursor_off vt issuecomment'):
+                yield Comment(comment, self.project_name)
+
+
+
 
 
 class UserLink(object):

http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/tests/data/google/test-issue-first-page.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/data/google/test-issue-first-page.html b/ForgeImporters/forgeimporters/tests/data/google/test-issue-first-page.html
new file mode 100644
index 0000000..4fc25eb
--- /dev/null
+++ b/ForgeImporters/forgeimporters/tests/data/google/test-issue-first-page.html
@@ -0,0 +1,548 @@
+<!DOCTYPE html>
+<!--
+
+An issue with a link to another page of comments (google paginates after 500 comments, we simulate with less)
+test-issue-prev-page.html is the test file for that other page of comments
+
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+<meta name="ROBOTS" content="NOARCHIVE" />
+<link rel="icon" type="image/vnd.microsoft.icon" href="http://www.gstatic.com/codesite/ph/images/phosting.ico" />
+<script type="text/javascript">
+
+
+
+
+ var codesite_token = null;
+
+
+ var CS_env = {"loggedInUserEmail":null,"relativeBaseUrl":"","projectHomeUrl":"/p/allura-google-importer","assetVersionPath":"http://www.gstatic.com/codesite/ph/3783617020303179221","assetHostPath":"http://www.gstatic.com/codesite/ph","domainName":null,"projectName":"allura-google-importer","token":null,"profileUrl":null};
+ var _gaq = _gaq || [];
+ _gaq.push(
+ ['siteTracker._setAccount', 'UA-18071-1'],
+ ['siteTracker._trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
+ })();
+
+ </script>
+<title>Issue 6 -
+ allura-google-importer -
+
+ Test Issue -
+ Import Google Code projects to an Allura forge - Google Project Hosting
+ </title>
+<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/core.css" />
+<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/ph_detail.css" />
+<!--[if IE]>
+ <link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/d_ie.css" >
+<![endif]-->
+<style type="text/css">
+ .menuIcon.off { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -42px }
+ .menuIcon.on { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -28px }
+ .menuIcon.down { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 0; }
+
+
+ .attachments { width:33%; border-top:2px solid #999; padding-top: 3px; margin-left: .7em;}
+ .attachments table { margin-bottom: 0.75em; }
+ .attachments table tr td { padding: 0; margin: 0; font-size: 95%; }
+ .preview { border: 2px solid #c3d9ff; padding: 1px; }
+ .preview:hover { border: 2px solid blue; }
+ .label { white-space: nowrap; }
+ .derived { font-style:italic }
+ .cursor_on .author {
+ background: url(http://www.gstatic.com/codesite/ph/images/show-arrow.gif) no-repeat 2px;
+ }
+ .hiddenform {
+ display: none;
+ }
+
+
+ </style>
+</head>
+<body class="t3">
+<script type="text/javascript">
+ window.___gcfg = {lang: 'en'};
+ (function()
+ {var po = document.createElement("script");
+ po.type = "text/javascript"; po.async = true;po.src = "https://apis.google.com/js/plusone.js";
+ var s = document.getElementsByTagName("script")[0];
+ s.parentNode.insertBefore(po, s);
+ })();
+</script>
+<div class="headbg">
+<div id="gaia">
+<span>
+<a href="#" id="projects-dropdown" onclick="return false;"><u>My favorites</u> <small>&#9660;</small></a>
+ | <a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6" onclick="_CS_click('/gb/ph/signin');"><u>Sign in</u></a>
+</span>
+</div>
+<div class="gbh" style="left: 0pt;"></div>
+<div class="gbh" style="right: 0pt;"></div>
+<div style="height: 1px"></div>
+<!--[if lte IE 7]>
+<div style="text-align:center;">
+Your version of Internet Explorer is not supported. Try a browser that
+contributes to open source, such as <a href="http://www.firefox.com">Firefox</a>,
+<a href="http://www.google.com/chrome">Google Chrome</a>, or
+<a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame</a>.
+</div>
+<![endif]-->
+<table style="padding:0px; margin: 0px 0px 10px 0px; width:100%" cellpadding="0" cellspacing="0" itemscope="itemscope" itemtype="http://schema.org/CreativeWork">
+<tr style="height: 58px;">
+<td id="plogo">
+<link itemprop="url" href="/p/allura-google-importer" />
+<a href="/p/allura-google-importer/">
+<img src="/p/allura-google-importer/logo?cct=1374769571" alt="Logo" itemprop="image" />
+</a>
+</td>
+<td style="padding-left: 0.5em">
+<div id="pname">
+<a href="/p/allura-google-importer/"><span itemprop="name">allura-google-importer</span></a>
+</div>
+<div id="psum">
+<a id="project_summary_link" href="/p/allura-google-importer/"><span itemprop="description">Import Google Code projects to an Allura forge</span></a>
+</div>
+</td>
+<td style="white-space:nowrap;text-align:right; vertical-align:bottom;">
+<form action="/hosting/search">
+<input size="30" name="q" value="" type="text" />
+<input type="submit" name="projectsearch" value="Search projects" />
+</form>
+</td></tr>
+</table>
+</div>
+<div id="mt" class="gtb">
+<a href="/p/allura-google-importer/" class="tab ">Project&nbsp;Home</a>
+<a href="/p/allura-google-importer/wiki/TestPage?tm=6" class="tab ">Wiki</a>
+<a href="/p/allura-google-importer/issues/list" class="tab active">Issues</a>
+<a href="/p/allura-google-importer/source/checkout" class="tab ">Source</a>
+<div class="gtbc"></div>
+</div>
+<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0" class="st">
+<tr>
+<td class="subt">
+<div class="issueDetail">
+<div class="isf">
+<span class="inIssueEntry">
+<a class="buttonify" href="entry" onclick="return _newIssuePrompt();">New issue</a>
+</span> &nbsp;
+
+ <span class="inIssueList">
+<span>Search</span>
+</span><form action="list" method="GET" style="display:inline">
+<select id="can" name="can">
+<option disabled="disabled">Search within:</option>
+<option value="1">&nbsp;All issues</option>
+<option value="2" selected="selected">&nbsp;Open issues</option>
+<option value="6">&nbsp;New issues</option>
+<option value="7">&nbsp;Issues to verify</option>
+</select>
+<span>for</span>
+<span id="qq"><input type="text" size="38" id="searchq" name="q" value="" autocomplete="off" onkeydown="_blurOnEsc(event)" /></span>
+<span id="search_colspec"><input type="hidden" name="colspec" value="ID Type Status Priority Milestone Owner Summary" /></span>
+<input type="hidden" name="cells" value="tiles" />
+<input type="submit" value="Search" />
+</form>
+ &nbsp;
+ <span class="inIssueAdvSearch">
+<a href="advsearch">Advanced search</a>
+</span> &nbsp;
+ <span class="inIssueSearchTips">
+<a href="searchtips">Search tips</a>
+</span> &nbsp;
+ <span class="inIssueSubscriptions">
+<a href="/p/allura-google-importer/issues/subscriptions">Subscriptions</a>
+</span>
+</div>
+</div>
+</td>
+<td align="right" valign="top" class="bevel-right"></td>
+</tr>
+</table>
+<script type="text/javascript">
+ var cancelBubble = false;
+ function _go(url) { document.location = url; }
+</script>
+<div id="maincol">
+<div id="color_control" class="">
+<div id="issueheader">
+<table cellpadding="0" cellspacing="0" width="100%"><tbody>
+<tr>
+<td class="vt h3" nowrap="nowrap" style="padding:0 5px">
+
+
+ Issue <a href="detail?id=6">6</a>:
+ </td>
+<td width="90%" class="vt">
+<span class="h3">Test &quot;Issue&quot;</span>
+</td>
+<td>
+<div class="pagination">
+<a href="../../allura-google-importer/issues/detail?id=5" title="Prev">&lsaquo; Prev</a>
+ 6 of 6
+
+ </div>
+</td>
+</tr>
+<tr>
+<td></td>
+<td nowrap="nowrap">
+
+
+ 1 person starred this issue and may be notified of changes.
+
+
+
+ </td>
+<td align="center" nowrap="nowrap">
+<a href="http://code.google.com/p/allura-google-importer/issues/list?cursor=allura-google-importer%3A6">Back to list</a>
+</td>
+</tr>
+</tbody></table>
+</div>
+<table width="100%" cellpadding="0" cellspacing="0" border="0" class="issuepage" id="meta-container">
+<tbody class="collapse">
+<tr>
+<td id="issuemeta">
+<div id="meta-float">
+<table cellspacing="0" cellpadding="0">
+<tr><th align="left">Status:&nbsp;</th>
+<td width="100%">
+<span title="Work on this issue has begun">Started</span>
+</td>
+</tr>
+<tr><th align="left">Owner:&nbsp;</th><td>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a>
+</td>
+</tr>
+<tr><td colspan="2">
+<div style="padding-top:2px">
+<a href="list?q=label:Type-Defect" title="Report of a software defect" class="label"><b>Type-</b>Defect</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Priority-Medium" title="Normal priority" class="label"><b>Priority-</b>Medium</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Milestone-Release1.0" title="All essential functionality working" class="label"><b>Milestone-</b>Release1.0</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-All" title="Affects all operating systems" class="label"><b>OpSys-</b>All</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Component-Logic" title="Issue relates to application logic" class="label"><b>Component-</b>Logic</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Performance" title="Performance issue" class="label">Performance</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Security" title="Security risk to users" class="label">Security</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-Windows" title="Affects Windows users" class="label"><b>OpSys-</b>Windows</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-OSX" title="Affects Mac OS X users" class="label"><b>OpSys-</b>OSX</a>
+</div>
+</td></tr>
+</table>
+<div class="rel_issues">
+</div>
+<br /><br />
+<div style="white-space:nowrap"><a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6">Sign in</a> to add a comment</div>
+</div>&nbsp;
+ </td>
+<td class="vt issuedescription" width="100%" id="cursorarea">
+<div class="cursor_off vt issuedescription" id="hc0">
+<div class="author">
+<span class="role_label">Project Member</span>
+ Reported by
+
+
+ <a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a>,
+ <span class="date" title="Thu Aug  8 15:33:52 2013">Today (3 minutes ago)</span>
+</div>
+<pre>
+Test *Issue* for testing
+
+  1. Test List
+  2. Item
+
+**Testing**
+
+ * Test list 2
+ * Item
+
+# Test Section
+
+    p = source.test_issue.post()
+    p.count = p.count *5 #* 6
+    if p.count &gt; 5:
+        print "Not &lt; 5 &amp; != 5"
+
+References: <a href="/p/allura-google-importer/issues/detail?id=1">issue 1</a>, <a href="/p/allura-google-importer/source/detail?r=2">r2</a>
+
+That's all
+
+
+</pre>
+<div class="attachments">
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b>at1.txt</b>
+<br />
+ 13 bytes
+
+
+ &nbsp; <a href="../../allura-google-importer/issues/attachmentText?id=7&amp;aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255" target="_blank">View</a>
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255">Download</a>
+</td>
+</tr>
+</table>
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000001&amp;name=&amp;token=C9Hn4s1-g38hlSggRGo65VZM1ys%3A1376059941255">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b></b>
+<br />
+ 0 bytes
+
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000001&amp;name=&amp;token=C9Hn4s1-g38hlSggRGo65VZM1ys%3A1376059941255">Download</a>
+</td>
+</tr>
+</table>
+</div>
+</div>
+
+ <div class="vt issuecomment" width="100%" style="background:#e5ecf9; padding:2px .7em; margin:0; border:0">
+Showing comments 3 - 6
+of 6
+ &nbsp; <a href="detail?id=1769&amp;cnum=500&amp;cstart=2">Older <b>&rsaquo;</b></a>
+ </div>
+
+<div class="cursor_off vt issuecomment" id="hc1">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:35:15 2013">
+ Today (2 minutes ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c1" href="/p/allura-google-importer/issues/detail?id=6#c1">#1</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Test *comment* is a comment
+</pre>
+<div class="attachments">
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60001000&amp;name=&amp;token=JOSo4duwaN2FCKZrwYOQ-nx9r7U%3A1376001446667">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b>at2.txt</b>
+<br />
+ 13 bytes
+
+
+ &nbsp; <a href="../../allura-google-importer/issues/attachmentText?id=6&amp;aid=60001000&amp;name=at2.txt&amp;token=JOSo4duwaN2FCKZrwYOQ-nx9r7U%3A1376001446667" target="_blank">View</a>
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60001000&amp;name=at2.txt&amp;token=JOSo4duwaN2FCKZrwYOQ-nx9r7U%3A1376001446667">Download</a>
+</td>
+</tr>
+</table>
+</div>
+<div class="updates">
+<div class="round4"></div>
+<div class="round2"></div>
+<div class="round1"></div>
+<div class="box-inner">
+<b>Status:</b>
+ Started
+
+ <br />
+<b>Labels:</b>
+ -OpSys-Linux OpSys-Windows
+
+ <br />
+</div>
+<div class="round1"></div>
+<div class="round2"></div>
+<div class="round4"></div>
+</div>
+</div>
+<div class="cursor_off vt issuecomment" id="hc2">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:35:34 2013">
+ Today (1 minute ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c2" href="/p/allura-google-importer/issues/detail?id=6#c2">#2</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Another comment with references: <a href="/p/allura-google-importer/issues/detail?id=2">issue 2</a>, <a href="/p/allura-google-importer/source/detail?r=1">r1</a>
+</pre>
+</div>
+<div class="cursor_off vt issuecomment" id="hc3">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:36:39 2013">
+ Today (moments ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c3" href="/p/allura-google-importer/issues/detail?id=6#c3">#3</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Last comment
+</pre>
+<div class="attachments">
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60003000&amp;name=at4.txt&amp;token=6Ny2zYHmV6b82dqxyoiH6HUYoC4%3A1376001446667">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b>at4.txt</b>
+<br />
+ 13 bytes
+
+
+ &nbsp; <a href="../../allura-google-importer/issues/attachmentText?id=6&amp;aid=60003000&amp;name=at4.txt&amp;token=6Ny2zYHmV6b82dqxyoiH6HUYoC4%3A1376001446667" target="_blank">View</a>
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60003000&amp;name=at4.txt&amp;token=6Ny2zYHmV6b82dqxyoiH6HUYoC4%3A1376001446667">Download</a>
+</td>
+</tr>
+</table>
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60003001&amp;name=at1.txt&amp;token=NS8aMvWsKzTAPuY2kniJG5aLzPg%3A1376001446667">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b>at1.txt</b>
+<br />
+ 13 bytes
+
+
+ &nbsp; <a href="../../allura-google-importer/issues/attachmentText?id=6&amp;aid=60003001&amp;name=at1.txt&amp;token=NS8aMvWsKzTAPuY2kniJG5aLzPg%3A1376001446667" target="_blank">View</a>
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=60003001&amp;name=at1.txt&amp;token=NS8aMvWsKzTAPuY2kniJG5aLzPg%3A1376001446667">Download</a>
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="cursor_off vt issuecomment" id="hc4">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:36:57 2013">
+ Today (moments ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c4" href="/p/allura-google-importer/issues/detail?id=6#c4">#4</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Oh, I forgot one (with an inter-project reference to <a href="/p/other-project/issues/detail?id=1">issue other-project:1</a>)
+</pre>
+<div class="updates">
+<div class="round4"></div>
+<div class="round2"></div>
+<div class="round1"></div>
+<div class="box-inner">
+<b>Labels:</b>
+ OpSys-OSX
+
+ <br />
+</div>
+<div class="round1"></div>
+<div class="round2"></div>
+<div class="round4"></div>
+</div>
+</div>
+
+<div class="vt issuecomment" width="100%" style="background:#e5ecf9; padding:2px .7em; margin:0">
+Showing comments 3 - 6
+of 6
+ &nbsp; <a href="detail?id=1769&amp;cnum=500&amp;cstart=2">Older <b>&rsaquo;</b></a>
+</div>
+
+</td>
+</tr>
+<tr>
+<td></td>
+<td class="vt issuecomment">
+<span class="indicator">&#9658;</span> <a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6">Sign in</a> to add a comment
+ </td>
+</tr>
+</tbody>
+</table>
+<br />
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/dit_scripts.js"></script>
+</div>
+<form name="delcom" action="delComment.do?q=&amp;can=2&amp;groupby=&amp;sort=&amp;colspec=ID+Type+Status+Priority+Milestone+Owner+Summary" method="POST">
+<input type="hidden" name="sequence_num" value="" />
+<input type="hidden" name="mode" value="" />
+<input type="hidden" name="id" value="6" />
+<input type="hidden" name="token" value="" />
+</form>
+<div id="helparea"></div>
+<script type="text/javascript">
+ _onload();
+ function delComment(sequence_num, delete_mode) {
+ var f = document.forms["delcom"];
+ f.sequence_num.value = sequence_num;
+ f.mode.value = delete_mode;
+
+ f.submit();
+ return false;
+ }
+
+ _floatMetadata();
+</script>
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/kibbles.js"></script>
+<script type="text/javascript">
+ _setupKibblesOnDetailPage(
+ 'http://code.google.com/p/allura-google-importer/issues/list?cursor\x3dallura-google-importer%3A6',
+ '/p/allura-google-importer/issues/entry',
+ '../../allura-google-importer/issues/detail?id\x3d5',
+ '',
+ '', 'allura-google-importer', 6,
+ false, false, codesite_token);
+</script>
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/ph_core.js"></script>
+</div>
+<div id="footer" dir="ltr">
+<div class="text">
+<a href="/projecthosting/terms.html">Terms</a> -
+ <a href="http://www.google.com/privacy.html">Privacy</a> -
+ <a href="/p/support/">Project Hosting Help</a>
+</div>
+</div>
+<div class="hostedBy" style="margin-top: -20px;">
+<span style="vertical-align: top;">Powered by <a href="http://code.google.com/projecthosting/">Google Project Hosting</a></span>
+</div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/tests/data/google/test-issue-prev-page.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/data/google/test-issue-prev-page.html b/ForgeImporters/forgeimporters/tests/data/google/test-issue-prev-page.html
new file mode 100644
index 0000000..62a3b23
--- /dev/null
+++ b/ForgeImporters/forgeimporters/tests/data/google/test-issue-prev-page.html
@@ -0,0 +1,431 @@
+<!DOCTYPE html>
+<!--
+
+This is the second page of previous comments, that goes with test-issue-first-page.html
+
+-->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+<meta name="ROBOTS" content="NOARCHIVE" />
+<link rel="icon" type="image/vnd.microsoft.icon" href="http://www.gstatic.com/codesite/ph/images/phosting.ico" />
+<script type="text/javascript">
+
+
+
+
+ var codesite_token = null;
+
+
+ var CS_env = {"loggedInUserEmail":null,"relativeBaseUrl":"","projectHomeUrl":"/p/allura-google-importer","assetVersionPath":"http://www.gstatic.com/codesite/ph/3783617020303179221","assetHostPath":"http://www.gstatic.com/codesite/ph","domainName":null,"projectName":"allura-google-importer","token":null,"profileUrl":null};
+ var _gaq = _gaq || [];
+ _gaq.push(
+ ['siteTracker._setAccount', 'UA-18071-1'],
+ ['siteTracker._trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
+ })();
+
+ </script>
+<title>Issue 6 -
+ allura-google-importer -
+
+ Test Issue -
+ Import Google Code projects to an Allura forge - Google Project Hosting
+ </title>
+<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/core.css" />
+<link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/ph_detail.css" />
+<!--[if IE]>
+ <link type="text/css" rel="stylesheet" href="http://www.gstatic.com/codesite/ph/3783617020303179221/css/d_ie.css" >
+<![endif]-->
+<style type="text/css">
+ .menuIcon.off { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -42px }
+ .menuIcon.on { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -28px }
+ .menuIcon.down { background: no-repeat url(http://www.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 0; }
+
+
+ .attachments { width:33%; border-top:2px solid #999; padding-top: 3px; margin-left: .7em;}
+ .attachments table { margin-bottom: 0.75em; }
+ .attachments table tr td { padding: 0; margin: 0; font-size: 95%; }
+ .preview { border: 2px solid #c3d9ff; padding: 1px; }
+ .preview:hover { border: 2px solid blue; }
+ .label { white-space: nowrap; }
+ .derived { font-style:italic }
+ .cursor_on .author {
+ background: url(http://www.gstatic.com/codesite/ph/images/show-arrow.gif) no-repeat 2px;
+ }
+ .hiddenform {
+ display: none;
+ }
+
+
+ </style>
+</head>
+<body class="t3">
+<script type="text/javascript">
+ window.___gcfg = {lang: 'en'};
+ (function()
+ {var po = document.createElement("script");
+ po.type = "text/javascript"; po.async = true;po.src = "https://apis.google.com/js/plusone.js";
+ var s = document.getElementsByTagName("script")[0];
+ s.parentNode.insertBefore(po, s);
+ })();
+</script>
+<div class="headbg">
+<div id="gaia">
+<span>
+<a href="#" id="projects-dropdown" onclick="return false;"><u>My favorites</u> <small>&#9660;</small></a>
+ | <a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6" onclick="_CS_click('/gb/ph/signin');"><u>Sign in</u></a>
+</span>
+</div>
+<div class="gbh" style="left: 0pt;"></div>
+<div class="gbh" style="right: 0pt;"></div>
+<div style="height: 1px"></div>
+<!--[if lte IE 7]>
+<div style="text-align:center;">
+Your version of Internet Explorer is not supported. Try a browser that
+contributes to open source, such as <a href="http://www.firefox.com">Firefox</a>,
+<a href="http://www.google.com/chrome">Google Chrome</a>, or
+<a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame</a>.
+</div>
+<![endif]-->
+<table style="padding:0px; margin: 0px 0px 10px 0px; width:100%" cellpadding="0" cellspacing="0" itemscope="itemscope" itemtype="http://schema.org/CreativeWork">
+<tr style="height: 58px;">
+<td id="plogo">
+<link itemprop="url" href="/p/allura-google-importer" />
+<a href="/p/allura-google-importer/">
+<img src="/p/allura-google-importer/logo?cct=1374769571" alt="Logo" itemprop="image" />
+</a>
+</td>
+<td style="padding-left: 0.5em">
+<div id="pname">
+<a href="/p/allura-google-importer/"><span itemprop="name">allura-google-importer</span></a>
+</div>
+<div id="psum">
+<a id="project_summary_link" href="/p/allura-google-importer/"><span itemprop="description">Import Google Code projects to an Allura forge</span></a>
+</div>
+</td>
+<td style="white-space:nowrap;text-align:right; vertical-align:bottom;">
+<form action="/hosting/search">
+<input size="30" name="q" value="" type="text" />
+<input type="submit" name="projectsearch" value="Search projects" />
+</form>
+</td></tr>
+</table>
+</div>
+<div id="mt" class="gtb">
+<a href="/p/allura-google-importer/" class="tab ">Project&nbsp;Home</a>
+<a href="/p/allura-google-importer/wiki/TestPage?tm=6" class="tab ">Wiki</a>
+<a href="/p/allura-google-importer/issues/list" class="tab active">Issues</a>
+<a href="/p/allura-google-importer/source/checkout" class="tab ">Source</a>
+<div class="gtbc"></div>
+</div>
+<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0" class="st">
+<tr>
+<td class="subt">
+<div class="issueDetail">
+<div class="isf">
+<span class="inIssueEntry">
+<a class="buttonify" href="entry" onclick="return _newIssuePrompt();">New issue</a>
+</span> &nbsp;
+
+ <span class="inIssueList">
+<span>Search</span>
+</span><form action="list" method="GET" style="display:inline">
+<select id="can" name="can">
+<option disabled="disabled">Search within:</option>
+<option value="1">&nbsp;All issues</option>
+<option value="2" selected="selected">&nbsp;Open issues</option>
+<option value="6">&nbsp;New issues</option>
+<option value="7">&nbsp;Issues to verify</option>
+</select>
+<span>for</span>
+<span id="qq"><input type="text" size="38" id="searchq" name="q" value="" autocomplete="off" onkeydown="_blurOnEsc(event)" /></span>
+<span id="search_colspec"><input type="hidden" name="colspec" value="ID Type Status Priority Milestone Owner Summary" /></span>
+<input type="hidden" name="cells" value="tiles" />
+<input type="submit" value="Search" />
+</form>
+ &nbsp;
+ <span class="inIssueAdvSearch">
+<a href="advsearch">Advanced search</a>
+</span> &nbsp;
+ <span class="inIssueSearchTips">
+<a href="searchtips">Search tips</a>
+</span> &nbsp;
+ <span class="inIssueSubscriptions">
+<a href="/p/allura-google-importer/issues/subscriptions">Subscriptions</a>
+</span>
+</div>
+</div>
+</td>
+<td align="right" valign="top" class="bevel-right"></td>
+</tr>
+</table>
+<script type="text/javascript">
+ var cancelBubble = false;
+ function _go(url) { document.location = url; }
+</script>
+<div id="maincol">
+<div id="color_control" class="">
+<div id="issueheader">
+<table cellpadding="0" cellspacing="0" width="100%"><tbody>
+<tr>
+<td class="vt h3" nowrap="nowrap" style="padding:0 5px">
+
+
+ Issue <a href="detail?id=6">6</a>:
+ </td>
+<td width="90%" class="vt">
+<span class="h3">Test &quot;Issue&quot;</span>
+</td>
+<td>
+<div class="pagination">
+<a href="../../allura-google-importer/issues/detail?id=5" title="Prev">&lsaquo; Prev</a>
+ 6 of 6
+
+ </div>
+</td>
+</tr>
+<tr>
+<td></td>
+<td nowrap="nowrap">
+
+
+ 1 person starred this issue and may be notified of changes.
+
+
+
+ </td>
+<td align="center" nowrap="nowrap">
+<a href="http://code.google.com/p/allura-google-importer/issues/list?cursor=allura-google-importer%3A6">Back to list</a>
+</td>
+</tr>
+</tbody></table>
+</div>
+<table width="100%" cellpadding="0" cellspacing="0" border="0" class="issuepage" id="meta-container">
+<tbody class="collapse">
+<tr>
+<td id="issuemeta">
+<div id="meta-float">
+<table cellspacing="0" cellpadding="0">
+<tr><th align="left">Status:&nbsp;</th>
+<td width="100%">
+<span title="Work on this issue has begun">Started</span>
+</td>
+</tr>
+<tr><th align="left">Owner:&nbsp;</th><td>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a>
+</td>
+</tr>
+<tr><td colspan="2">
+<div style="padding-top:2px">
+<a href="list?q=label:Type-Defect" title="Report of a software defect" class="label"><b>Type-</b>Defect</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Priority-Medium" title="Normal priority" class="label"><b>Priority-</b>Medium</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Milestone-Release1.0" title="All essential functionality working" class="label"><b>Milestone-</b>Release1.0</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-All" title="Affects all operating systems" class="label"><b>OpSys-</b>All</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Component-Logic" title="Issue relates to application logic" class="label"><b>Component-</b>Logic</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Performance" title="Performance issue" class="label">Performance</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:Security" title="Security risk to users" class="label">Security</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-Windows" title="Affects Windows users" class="label"><b>OpSys-</b>Windows</a>
+</div>
+<div style="padding-top:2px">
+<a href="list?q=label:OpSys-OSX" title="Affects Mac OS X users" class="label"><b>OpSys-</b>OSX</a>
+</div>
+</td></tr>
+</table>
+<div class="rel_issues">
+</div>
+<br /><br />
+<div style="white-space:nowrap"><a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6">Sign in</a> to add a comment</div>
+</div>&nbsp;
+ </td>
+<td class="vt issuedescription" width="100%" id="cursorarea">
+<div class="cursor_off vt issuedescription" id="hc0">
+<div class="author">
+<span class="role_label">Project Member</span>
+ Reported by
+
+
+ <a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a>,
+ <span class="date" title="Thu Aug  8 15:33:52 2013">Today (3 minutes ago)</span>
+</div>
+<pre>
+Test *Issue* for testing
+
+  1. Test List
+  2. Item
+
+**Testing**
+
+ * Test list 2
+ * Item
+
+# Test Section
+
+    p = source.test_issue.post()
+    p.count = p.count *5 #* 6
+    if p.count &gt; 5:
+        print "Not &lt; 5 &amp; != 5"
+
+References: <a href="/p/allura-google-importer/issues/detail?id=1">issue 1</a>, <a href="/p/allura-google-importer/source/detail?r=2">r2</a>
+
+That's all
+
+
+</pre>
+<div class="attachments">
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b>at1.txt</b>
+<br />
+ 13 bytes
+
+
+ &nbsp; <a href="../../allura-google-importer/issues/attachmentText?id=7&amp;aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255" target="_blank">View</a>
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000000&amp;name=at1.txt&amp;token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255">Download</a>
+</td>
+</tr>
+</table>
+<table cellspacing="3" cellpadding="2" border="0">
+<tr><td width="20">
+<a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000001&amp;name=&amp;token=C9Hn4s1-g38hlSggRGo65VZM1ys%3A1376059941255">
+<img width="15" height="15" src="http://www.gstatic.com/codesite/ph/images/paperclip.gif" border="0" />
+</a>
+</td>
+<td style="min-width:16em" valign="top">
+<b></b>
+<br />
+ 0 bytes
+
+
+ &nbsp; <a href="//allura-google-importer.googlecode.com/issues/attachment?aid=70000001&amp;name=&amp;token=C9Hn4s1-g38hlSggRGo65VZM1ys%3A1376059941255">Download</a>
+</td>
+</tr>
+</table>
+</div>
+</div>
+
+ <div class="vt issuecomment" width="100%" style="background:#e5ecf9; padding:2px .7em; margin:0; border:0">
+ <a href="detail?id=1769&amp;cnum=500&amp;cstart=502">Newer <b>&rsaquo;</b></a>
+ &nbsp;
+Showing comments 1 - 2
+of 6
+ </div>
+
+<div class="cursor_off vt issuecomment" id="hc1">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:34:01 2013">
+ Today (3 minutes ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c1" href="/p/allura-google-importer/issues/detail?id=6#c1">#1</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Simple comment
+</pre>
+</div>
+<div class="cursor_off vt issuecomment" id="hc2">
+<div style="float:right; margin-right:.3em; text-align:right">
+<span class="date" title="Thu Aug  8 15:34:09 2013">
+ Today (3 minutes ago)
+ </span>
+</div>
+<span class="author">
+<span class="role_label">Project Member</span>
+<a name="c2" href="/p/allura-google-importer/issues/detail?id=6#c2">#2</a>
+<a class="userlink" href="/u/101557263855536553789/">john...@gmail.com</a></span>
+<pre>
+Boring comment
+</pre>
+</div>
+
+<div class="vt issuecomment" width="100%" style="background:#e5ecf9; padding:2px .7em; margin:0">
+ <a href="detail?id=1769&amp;cnum=500&amp;cstart=502">Newer <b>&rsaquo;</b></a>
+ &nbsp;
+Showing comments 1 - 2
+of 6
+</div>
+
+</td>
+</tr>
+<tr>
+<td></td>
+<td class="vt issuecomment">
+<span class="indicator">&#9658;</span> <a href="https://www.google.com/accounts/ServiceLogin?service=code&amp;ltmpl=phosting&amp;continue=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6&amp;followup=http%3A%2F%2Fcode.google.com%2Fp%2Fallura-google-importer%2Fissues%2Fdetail%3Fid%3D6">Sign in</a> to add a comment
+ </td>
+</tr>
+</tbody>
+</table>
+<br />
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/dit_scripts.js"></script>
+</div>
+<form name="delcom" action="delComment.do?q=&amp;can=2&amp;groupby=&amp;sort=&amp;colspec=ID+Type+Status+Priority+Milestone+Owner+Summary" method="POST">
+<input type="hidden" name="sequence_num" value="" />
+<input type="hidden" name="mode" value="" />
+<input type="hidden" name="id" value="6" />
+<input type="hidden" name="token" value="" />
+</form>
+<div id="helparea"></div>
+<script type="text/javascript">
+ _onload();
+ function delComment(sequence_num, delete_mode) {
+ var f = document.forms["delcom"];
+ f.sequence_num.value = sequence_num;
+ f.mode.value = delete_mode;
+
+ f.submit();
+ return false;
+ }
+
+ _floatMetadata();
+</script>
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/kibbles.js"></script>
+<script type="text/javascript">
+ _setupKibblesOnDetailPage(
+ 'http://code.google.com/p/allura-google-importer/issues/list?cursor\x3dallura-google-importer%3A6',
+ '/p/allura-google-importer/issues/entry',
+ '../../allura-google-importer/issues/detail?id\x3d5',
+ '',
+ '', 'allura-google-importer', 6,
+ false, false, codesite_token);
+</script>
+<script type="text/javascript" src="http://www.gstatic.com/codesite/ph/3783617020303179221/js/ph_core.js"></script>
+</div>
+<div id="footer" dir="ltr">
+<div class="text">
+<a href="/projecthosting/terms.html">Terms</a> -
+ <a href="http://www.google.com/privacy.html">Privacy</a> -
+ <a href="/p/support/">Project Hosting Help</a>
+</div>
+</div>
+<div class="hostedBy" style="margin-top: -20px;">
+<span style="vertical-align: top;">Powered by <a href="http://code.google.com/projecthosting/">Google Project Hosting</a></span>
+</div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/tests/data/google/test-issue.html
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/data/google/test-issue.html b/ForgeImporters/forgeimporters/tests/data/google/test-issue.html
index c6bce0a..59507a9 100644
--- a/ForgeImporters/forgeimporters/tests/data/google/test-issue.html
+++ b/ForgeImporters/forgeimporters/tests/data/google/test-issue.html
@@ -1,4 +1,9 @@
 <!DOCTYPE html>
+<!--
+
+Just a regular single-page issue
+
+-->
 <html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
@@ -323,6 +328,7 @@ That's all
 </table>
 </div>
 </div>
+
 <div class="cursor_off vt issuecomment" id="hc1">
 <div style="float:right; margin-right:.3em; text-align:right">
 <span class="date" title="Thu Aug  8 15:35:15 2013">
@@ -469,6 +475,7 @@ Oh, I forgot one (with an inter-project reference to <a href="/p/other-project/i
 <div class="round4"></div>
 </div>
 </div>
+
 </td>
 </tr>
 <tr>

http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/tests/google/functional/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/google/functional/test_tracker.py b/ForgeImporters/forgeimporters/tests/google/functional/test_tracker.py
index 96d789d..938d1c7 100644
--- a/ForgeImporters/forgeimporters/tests/google/functional/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/google/functional/test_tracker.py
@@ -31,6 +31,7 @@ from allura import model as M
 from forgetracker import model as TM
 from forgeimporters import base
 from forgeimporters import google
+import forgeimporters.google.tracker
 
 
 class TestGCTrackerImporter(TestCase):
@@ -42,6 +43,10 @@ class TestGCTrackerImporter(TestCase):
                 'allura-google-importer', 'project_info')
         extractor.page = BeautifulSoup(html)
         extractor.url = "http://test/issue/?id=1"
+        # iter_comments will make more get_page() calls but we don't want the real thing to run an mess up the .page
+        # and .url attributes, make it a no-op which works with these tests (since its just the same page being
+        # fetched really)
+        extractor.get_page = lambda *a, **kw: ''
         return extractor
 
     def _make_ticket(self, issue, issue_id=1):
@@ -197,7 +202,7 @@ class TestGCTrackerImporter(TestCase):
                    for a in actual)
         self.assertEqual(atts, set(expected))
 
-    def test_attachements(self):
+    def test_attachments(self):
         ticket = self._make_ticket(self.test_issue)
         self._assert_attachments(ticket.attachments,
                                  ('at1.txt', 'text/plain',

http://git-wip-us.apache.org/repos/asf/allura/blob/225dc73c/ForgeImporters/forgeimporters/tests/google/test_extractor.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/google/test_extractor.py b/ForgeImporters/forgeimporters/tests/google/test_extractor.py
index 60ca0e2..fe74f9c 100644
--- a/ForgeImporters/forgeimporters/tests/google/test_extractor.py
+++ b/ForgeImporters/forgeimporters/tests/google/test_extractor.py
@@ -33,7 +33,6 @@ class TestGoogleCodeProjectExtractor(TestCase):
 
     def setUp(self):
         self._p_urlopen = mock.patch.object(base.ProjectExtractor, 'urlopen')
-        # self._p_soup = mock.patch.object(google, 'BeautifulSoup')
         self._p_soup = mock.patch.object(base, 'BeautifulSoup')
         self.urlopen = self._p_urlopen.start()
         self.soup = self._p_soup.start()
@@ -145,8 +144,7 @@ class TestGoogleCodeProjectExtractor(TestCase):
 
     def _make_extractor(self, html):
         with mock.patch.object(base.ProjectExtractor, 'urlopen'):
-            extractor = google.GoogleCodeProjectExtractor(
-                'allura-google-importer')
+            extractor = google.GoogleCodeProjectExtractor('allura-google-importer')
         extractor.page = BeautifulSoup(html)
         extractor.get_page = lambda pagename: extractor.page
         extractor.url = "http://test/source/browse"
@@ -184,9 +182,9 @@ class TestGoogleCodeProjectExtractor(TestCase):
 
     @without_module('html2text')
     def test_get_issue_basic_fields(self):
-        test_issue = open(pkg_resources.resource_filename(
-            'forgeimporters', 'tests/data/google/test-issue.html')).read()
+        test_issue = open(pkg_resources.resource_filename('forgeimporters', 'tests/data/google/test-issue.html')).read()
         gpe = self._make_extractor(test_issue)
+
         self.assertEqual(gpe.get_issue_creator().name, 'john...@gmail.com')
         self.assertEqual(gpe.get_issue_creator().url,
                          'http://code.google.com/u/101557263855536553789/')
@@ -224,8 +222,7 @@ class TestGoogleCodeProjectExtractor(TestCase):
 
     @skipif(module_not_available('html2text'))
     def test_get_issue_basic_fields_html2text(self):
-        test_issue = open(pkg_resources.resource_filename(
-            'forgeimporters', 'tests/data/google/test-issue.html')).read()
+        test_issue = open(pkg_resources.resource_filename('forgeimporters', 'tests/data/google/test-issue.html')).read()
         gpe = self._make_extractor(test_issue)
         self.assertEqual(gpe.get_issue_creator().name, 'john...@gmail.com')
         self.assertEqual(gpe.get_issue_creator().url,
@@ -280,12 +277,6 @@ class TestGoogleCodeProjectExtractor(TestCase):
         gpe = self._make_extractor(html % u'My Summary')
         self.assertEqual(gpe.get_issue_summary(), u'My Summary')
 
-    def test_get_issue_mod_date(self):
-        test_issue = open(pkg_resources.resource_filename(
-            'forgeimporters', 'tests/data/google/test-issue.html')).read()
-        gpe = self._make_extractor(test_issue)
-        self.assertEqual(gpe.get_issue_mod_date(), 'Thu Aug  8 15:36:57 2013')
-
     def test_get_issue_labels(self):
         test_issue = open(pkg_resources.resource_filename(
             'forgeimporters', 'tests/data/google/test-issue.html')).read()
@@ -316,18 +307,116 @@ class TestGoogleCodeProjectExtractor(TestCase):
             attachments[0].url, 'http://allura-google-importer.googlecode.com/issues/attachment?aid=70000000&name=at1.txt&token=3REU1M3JUUMt0rJUg7ldcELt6LA%3A1376059941255')
         self.assertEqual(attachments[0].type, 'text/plain')
 
+    def test_get_issue_ids(self):
+        extractor = google.GoogleCodeProjectExtractor(None)
+        extractor.get_page = mock.Mock(side_effect=((1, 2, 3), (2, 3, 4), ()))
+        self.assertItemsEqual(extractor.get_issue_ids(start=10), (1, 2, 3, 4))
+        self.assertEqual(extractor.get_page.call_count, 3)
+        extractor.get_page.assert_has_calls([
+            mock.call('issues_csv', parser=google.csv_parser, start=10),
+            mock.call('issues_csv', parser=google.csv_parser, start=110),
+            mock.call('issues_csv', parser=google.csv_parser, start=210),
+        ])
+
+    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_page')
+    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_issue_ids')
+    def test_iter_issue_ids(self, get_issue_ids, get_page):
+        get_issue_ids.side_effect = [set([1, 2]), set([2, 3, 4])]
+        issue_ids = [i for i,
+                     e in list(google.GoogleCodeProjectExtractor.iter_issues('foo'))]
+        self.assertEqual(issue_ids, [1, 2, 3, 4])
+        get_issue_ids.assert_has_calls([
+            mock.call(start=0),
+            mock.call(start=-8),
+        ])
+
+    @mock.patch.object(google.GoogleCodeProjectExtractor, '__init__')
+    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_issue_ids')
+    def test_iter_issue_ids_raises(self, get_issue_ids, __init__):
+        get_issue_ids.side_effect = [set([1, 2, 3, 4, 5])]
+        __init__.side_effect = [
+            None,
+            None,
+            # should skip but keep going
+            HTTPError('fourohfour', 404, 'fourohfour', {}, mock.Mock()),
+            None,
+            # should be re-raised
+            HTTPError('fubar', 500, 'fubar', {}, mock.Mock()),
+            None,
+        ]
+        issue_ids = []
+        try:
+            for issue_id, extractor in google.GoogleCodeProjectExtractor.iter_issues('foo'):
+                issue_ids.append(issue_id)
+        except HTTPError as e:
+            self.assertEqual(e.code, 500)
+        else:
+            assert False, 'Missing expected raised exception'
+        self.assertEqual(issue_ids, [1, 3])
+
+    @mock.patch.object(google.requests, 'head')
+    def test_check_readable(self, head):
+        head.return_value.status_code = 200
+        assert google.GoogleCodeProjectExtractor('my-project').check_readable()
+        head.return_value.status_code = 404
+        assert not google.GoogleCodeProjectExtractor('my-project').check_readable()
+
+
+class TestWithSetupForComments(TestCase):
+    # The main test suite did too much patching for how we want these tests to work
+    # These tests use iter_comments and 2 HTML pages of comments.
+
+    def _create_extractor(self):
+        test_issue = open(pkg_resources.resource_filename('forgeimporters', 'tests/data/google/test-issue-first-page.html')).read()
+        test_issue_older = open(pkg_resources.resource_filename('forgeimporters', 'tests/data/google/test-issue-prev-page.html')).read()
+
+        class LocalTestExtractor(google.GoogleCodeProjectExtractor):
+            def urlopen(self, url, **kw):
+                return self.urlopen_results.pop(0)
+
+            def setup_urlopen_results(self, results):
+                self.urlopen_results = results
+
+        gpe = LocalTestExtractor('allura-google-importer')
+        gpe.setup_urlopen_results([test_issue, test_issue_older])
+
+        return gpe
+
+    def test_get_issue_mod_date(self):
+        gpe = self._create_extractor()
+        gpe.get_page('detail?id=6')
+        self.assertEqual(gpe.get_issue_mod_date(), 'Thu Aug  8 15:36:57 2013')
+
     @without_module('html2text')
     @mock.patch.object(base, 'StringIO')
     def test_iter_comments(self, StringIO):
-        test_issue = open(pkg_resources.resource_filename(
-            'forgeimporters', 'tests/data/google/test-issue.html')).read()
-        gpe = self._make_extractor(test_issue)
-        comments = list(gpe.iter_comments())
-        self.assertEqual(len(comments), 4)
+        gpe = self._create_extractor()
+        gpe.get_page('detail?id=6')
+
+        with mock.patch.object(base.ProjectExtractor, 'urlopen'):  # for attachments, which end up using a different Extractor urlopen
+            comments = list(gpe.iter_comments())
+
+        self.assertEqual(len(comments), 6)
         expected = [
             {
                 'author.name': 'john...@gmail.com',
                 'author.url': 'http://code.google.com/u/101557263855536553789/',
+                'created_date': 'Thu Aug  8 15:34:01 2013',
+                'body': 'Simple comment',
+                'updates': {},
+                'attachments': [],
+            },
+            {
+                'author.name': 'john...@gmail.com',
+                'author.url': 'http://code.google.com/u/101557263855536553789/',
+                'created_date': 'Thu Aug  8 15:34:09 2013',
+                'body': 'Boring comment',
+                'updates': {},
+                'attachments': [],
+            },
+            {
+                'author.name': 'john...@gmail.com',
+                'author.url': 'http://code.google.com/u/101557263855536553789/',
                 'created_date': 'Thu Aug  8 15:35:15 2013',
                 'body': 'Test \\*comment\\* is a comment',
                 'updates': {'Status:': 'Started', 'Labels:': '-OpSys-Linux OpSys-Windows'},
@@ -370,15 +459,33 @@ class TestGoogleCodeProjectExtractor(TestCase):
     @skipif(module_not_available('html2text'))
     @mock.patch.object(base, 'StringIO')
     def test_iter_comments_html2text(self, StringIO):
-        test_issue = open(pkg_resources.resource_filename(
-            'forgeimporters', 'tests/data/google/test-issue.html')).read()
-        gpe = self._make_extractor(test_issue)
-        comments = list(gpe.iter_comments())
-        self.assertEqual(len(comments), 4)
+        gpe = self._create_extractor()
+        gpe.get_page('detail?id=6')
+
+        with mock.patch.object(base.ProjectExtractor, 'urlopen'):  # for attachments, which end up using a different Extractor urlopen
+            comments = list(gpe.iter_comments())
+
+        self.assertEqual(len(comments), 6)
         expected = [
             {
                 'author.name': 'john...@gmail.com',
                 'author.url': 'http://code.google.com/u/101557263855536553789/',
+                'created_date': 'Thu Aug  8 15:34:01 2013',
+                'body': 'Simple comment',
+                'updates': {},
+                'attachments': [],
+            },
+            {
+                'author.name': 'john...@gmail.com',
+                'author.url': 'http://code.google.com/u/101557263855536553789/',
+                'created_date': 'Thu Aug  8 15:34:09 2013',
+                'body': 'Boring comment',
+                'updates': {},
+                'attachments': [],
+            },
+            {
+                'author.name': 'john...@gmail.com',
+                'author.url': 'http://code.google.com/u/101557263855536553789/',
                 'created_date': 'Thu Aug  8 15:35:15 2013',
                 'body': 'Test \\*comment\\* is a comment',
                 'updates': {'Status:': 'Started', 'Labels:': '-OpSys-Linux OpSys-Windows'},
@@ -418,60 +525,6 @@ class TestGoogleCodeProjectExtractor(TestCase):
             self.assertEqual(
                 [a.filename for a in actual.attachments], expected['attachments'])
 
-    def test_get_issue_ids(self):
-        extractor = google.GoogleCodeProjectExtractor(None)
-        extractor.get_page = mock.Mock(side_effect=((1, 2, 3), (2, 3, 4), ()))
-        self.assertItemsEqual(extractor.get_issue_ids(start=10), (1, 2, 3, 4))
-        self.assertEqual(extractor.get_page.call_count, 3)
-        extractor.get_page.assert_has_calls([
-            mock.call('issues_csv', parser=google.csv_parser, start=10),
-            mock.call('issues_csv', parser=google.csv_parser, start=110),
-            mock.call('issues_csv', parser=google.csv_parser, start=210),
-        ])
-
-    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_page')
-    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_issue_ids')
-    def test_iter_issue_ids(self, get_issue_ids, get_page):
-        get_issue_ids.side_effect = [set([1, 2]), set([2, 3, 4])]
-        issue_ids = [i for i,
-                     e in list(google.GoogleCodeProjectExtractor.iter_issues('foo'))]
-        self.assertEqual(issue_ids, [1, 2, 3, 4])
-        get_issue_ids.assert_has_calls([
-            mock.call(start=0),
-            mock.call(start=-8),
-        ])
-
-    @mock.patch.object(google.GoogleCodeProjectExtractor, '__init__')
-    @mock.patch.object(google.GoogleCodeProjectExtractor, 'get_issue_ids')
-    def test_iter_issue_ids_raises(self, get_issue_ids, __init__):
-        get_issue_ids.side_effect = [set([1, 2, 3, 4, 5])]
-        __init__.side_effect = [
-            None,
-            None,
-            # should skip but keep going
-            HTTPError('fourohfour', 404, 'fourohfour', {}, mock.Mock()),
-            None,
-            # should be re-raised
-            HTTPError('fubar', 500, 'fubar', {}, mock.Mock()),
-            None,
-        ]
-        issue_ids = []
-        try:
-            for issue_id, extractor in google.GoogleCodeProjectExtractor.iter_issues('foo'):
-                issue_ids.append(issue_id)
-        except HTTPError as e:
-            self.assertEqual(e.code, 500)
-        else:
-            assert False, 'Missing expected raised exception'
-        self.assertEqual(issue_ids, [1, 3])
-
-    @mock.patch.object(google.requests, 'head')
-    def test_check_readable(self, head):
-        head.return_value.status_code = 200
-        assert google.GoogleCodeProjectExtractor('my-project').check_readable()
-        head.return_value.status_code = 404
-        assert not google.GoogleCodeProjectExtractor('my-project').check_readable()
-
 
 class TestUserLink(TestCase):
 


[16/22] allura git commit: [#7855] update packages

Posted by je...@apache.org.
[#7855] update packages


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

Branch: refs/heads/ib/6017
Commit: a3f7d44ab3ca05686c10a655ea8d8f6d2f392421
Parents: d731920
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Apr 15 17:45:30 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Apr 15 17:45:30 2015 +0000

----------------------------------------------------------------------
 requirements.txt | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/a3f7d44a/requirements.txt
----------------------------------------------------------------------
diff --git a/requirements.txt b/requirements.txt
index 5664528..21b0ca5 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,7 +8,7 @@ colander==0.9.3
 Creoleparser==0.7.3
 decorator==3.3.2
 # dep of pypeline, sphinx
-docutils==0.9
+docutils==0.12
 EasyWidgets==0.2dev-20130716
 faulthandler==2.1
 feedparser==5.1.3
@@ -29,9 +29,9 @@ PasteDeploy==1.5.0
 PasteScript==1.7.4.2
 Pillow==2.0.0
 poster==0.8.1
-Pygments==1.6
+Pygments==2.0.2
 pymongo==2.4.2
-Pypeline==0.1dev
+Pypeline==0.2
 pysolr==2.1.0-beta
 python-dateutil==1.5
 python-magic==0.4.3
@@ -53,7 +53,7 @@ WebOb==1.0.8
 wsgiref==0.1.2
 
 # tg2 deps (not used directly)
-Babel==0.9.6
+Babel==1.3
 Mako==0.3.2
 MarkupSafe==0.15
 Pylons==1.0


[03/22] allura git commit: [#7850] clean up tests

Posted by je...@apache.org.
[#7850] clean up tests


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

Branch: refs/heads/ib/6017
Commit: ba7374be65f2a7bd4738e00c058aa76fed3c3649
Parents: f42961e
Author: Dave Brondsema <da...@brondsema.net>
Authored: Thu Apr 2 17:24:08 2015 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Thu Apr 2 17:24:37 2015 -0400

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tests/functional/test_rest.py | 1 +
 ForgeTracker/forgetracker/tests/functional/test_root.py | 3 ---
 2 files changed, 1 insertion(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ba7374be/ForgeTracker/forgetracker/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_rest.py b/ForgeTracker/forgetracker/tests/functional/test_rest.py
index 7c650be..aa728e1 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_rest.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_rest.py
@@ -96,6 +96,7 @@ class TestRestUpdateTicket(TestTrackerApiBase):
                         'reported_by', 'reported_by_id', '_id', 'votes_up', 'votes_down'):
             del args[bad_key]
         args['private'] = str(args['private'])
+        args['discussion_disabled'] = str(args['discussion_disabled'])
         ticket_view = self.api_post(
             '/rest/p/test/bugs/1/save', wrap_args='ticket_form', params=h.encode_keys(args))
         assert ticket_view.status_int == 200, ticket_view.showbrowser()

http://git-wip-us.apache.org/repos/asf/allura/blob/ba7374be/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 4a2e598..0e92b1f 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -652,9 +652,6 @@ class TestFunctionalController(TrackerTestController):
         r = self.app.get('/p/test/bugs/1', extra_environ=env)
         assert_not_in('edit_post_form reply', r)
 
-        env = dict(username='test-admin')
-        r = self.app.get('/p/test/bugs/1', extra_environ=env)
-
         # Test re-enabling discussions
         ticket_params['ticket_form.discussion_disabled'] = 'off'
         response = self.app.post('/bugs/1/update_ticket_from_widget', ticket_params).follow()


[02/22] allura git commit: [#7850] more readable ACL setup

Posted by je...@apache.org.
[#7850] more readable ACL setup


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

Branch: refs/heads/ib/6017
Commit: d503b14a1527386dba55cc95c2b8b20d94beb780
Parents: ba7374b
Author: Dave Brondsema <da...@brondsema.net>
Authored: Thu Apr 2 17:24:20 2015 -0400
Committer: Dave Brondsema <da...@brondsema.net>
Committed: Thu Apr 2 17:24:37 2015 -0400

----------------------------------------------------------------------
 ForgeTracker/forgetracker/model/ticket.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/d503b14a/ForgeTracker/forgetracker/model/ticket.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 06626a1..e26f57a 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -820,8 +820,8 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
         if bool_flag:
             role_developer = ProjectRole.by_name('Developer')
             role_creator = ProjectRole.by_user(self.reported_by, upsert=True)
-            _allow_all = lambda role, perms: [
-                ACE.allow(role._id, perm) for perm in perms]
+            def _allow_all(role, perms):
+                return [ACE.allow(role._id, perm) for perm in perms]
             # maintain existing access for developers and the ticket creator,
             # but revoke all access for everyone else
             acl = _allow_all(role_developer, security.all_allowed(self, role_developer))
@@ -839,8 +839,8 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
         :param is_disabled: If True, an explicit deny will be created on the discussion thread ACL.
         """
         if is_disabled:
-            _deny_post = lambda role, perms: [ACE.deny(role, perm) for perm in perms]
-            self.discussion_thread.acl = _deny_post(EVERYONE, ('post', 'unmoderated_post'))
+            self.discussion_thread.acl = [ACE.deny(EVERYONE, 'post'),
+                                          ACE.deny(EVERYONE, 'unmoderated_post')]
         else:
             self.discussion_thread.acl = []
 


[04/22] allura git commit: [#7865] add docstrings and pass user param through

Posted by je...@apache.org.
[#7865] add docstrings and pass user param through


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

Branch: refs/heads/ib/6017
Commit: 99ed9568fd8c94bddaba0c1e6b589fa9b503b7a8
Parents: 7ee7734
Author: Dave Brondsema <da...@brondsema.net>
Authored: Thu Apr 2 17:59:38 2015 -0400
Committer: Heith Seewald <hs...@slashdotmedia.com>
Committed: Fri Apr 3 15:43:49 2015 -0400

----------------------------------------------------------------------
 Allura/allura/model/repository.py | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/99ed9568/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 96c9358..8083e17 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -823,19 +823,25 @@ class MergeRequest(VersionedArtifact, ActivityObject):
         return result
 
     def merge_allowed(self, user):
+        """
+        Returns true if a merge is allowed by system and tool configuration.
+        """
         if not c.app.forkable:
             return False
         if self.status != 'open':
             return False
         if asbool(tg.config.get('scm.merge.{}.disabled'.format(self.app.config.tool_name))):
             return False
-        if not h.has_access(c.app, 'write'):
+        if not h.has_access(c.app, 'write', user):
             return False
         if self.app.config.options.get('merge_disabled'):
             return False
         return True
 
     def can_merge(self):
+        """
+        Returns true if you can merge cleanly (no conflicts)
+        """
         if not self.app.forkable:
             return False
         try:


[20/22] allura git commit: [#6017] ticket:756 Show attachments list as a diff, so it's easy to see what was added or removed

Posted by je...@apache.org.
[#6017] ticket:756 Show attachments list as a diff, so it's easy to see what was added or removed


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

Branch: refs/heads/ib/6017
Commit: 9546f5ae425c02cd21266a047a2b34ebb50bbf5e
Parents: da94700
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Apr 16 11:04:23 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 11:04:23 2015 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/data/ticket_changed_tmpl |  4 ++--
 ForgeTracker/forgetracker/tracker_main.py          | 11 ++++++-----
 2 files changed, 8 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/9546f5ae/ForgeTracker/forgetracker/data/ticket_changed_tmpl
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/data/ticket_changed_tmpl b/ForgeTracker/forgetracker/data/ticket_changed_tmpl
index 899b353..c04d92d 100644
--- a/ForgeTracker/forgetracker/data/ticket_changed_tmpl
+++ b/ForgeTracker/forgetracker/data/ticket_changed_tmpl
@@ -20,8 +20,8 @@
 {% python from allura.model import User %}\
 {% for key, values in changelist %}\
 {% choose %}\
-{% when key == 'description' %}\
-- Description has changed:
+{% when key in ['description', 'attachments'] %}\
+- ${key.capitalize()} has changed:
 
 Diff:
 

http://git-wip-us.apache.org/repos/asf/allura/blob/9546f5ae/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 7558eab..f8a5680 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1391,12 +1391,13 @@ class TicketController(BaseController, FeedController):
     @require_post()
     def _update_ticket(self, post_data):
         def attachments_info(attachments):
-            text = ''
+            text = []
             for attach in attachments:
-                text = "%s %s (%s; %s) " % (
-                    text, attach.filename,
-                    h.do_filesizeformat(attach.length), attach.content_type)
-            return text
+                text.append("{} ({}; {})".format(
+                    attach.filename,
+                    h.do_filesizeformat(attach.length),
+                    attach.content_type))
+            return "\n".join(text)
 
         require_access(self.ticket, 'update')
         changes = changelog()


[18/22] allura git commit: [#6017] ticket:751 Fixed test

Posted by je...@apache.org.
[#6017] ticket:751 Fixed test


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

Branch: refs/heads/ib/6017
Commit: da94700afcfd4b69cf38adf8835bc16ac599a12b
Parents: 677fd67
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Wed Apr 8 14:51:39 2015 +0300
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 10:43:47 2015 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tests/functional/test_root.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/da94700a/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 0e92b1f..666fbc1 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -807,7 +807,7 @@ class TestFunctionalController(TrackerTestController):
             'delete': 'True'
         })
         deleted_form = self.app.get('/bugs/1/')
-        assert file_name not in deleted_form
+        assert file_link not in deleted_form
 
     def test_delete_attachment_from_comments(self):
         ticket_view = self.new_ticket(summary='test ticket').follow()


[22/22] allura git commit: [#6017] ticket:756 Test attachments changelog better

Posted by je...@apache.org.
[#6017] ticket:756 Test attachments changelog better


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

Branch: refs/heads/ib/6017
Commit: ad7c5f9cc4ea231dc896a7fbb14be6956125cd4a
Parents: b5a084f
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Apr 16 15:10:13 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Thu Apr 16 15:10:13 2015 +0000

----------------------------------------------------------------------
 ForgeTracker/forgetracker/tests/functional/test_root.py | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/ad7c5f9c/ForgeTracker/forgetracker/tests/functional/test_root.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_root.py b/ForgeTracker/forgetracker/tests/functional/test_root.py
index 666fbc1..21a3564 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_root.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_root.py
@@ -790,6 +790,10 @@ class TestFunctionalController(TrackerTestController):
         }, upload_files=[upload]).follow()
         assert_true(file_name in ticket_editor)
         assert '<span>py</span>' not in ticket_editor
+        ticket_page = self.app.get('/bugs/1/')
+        diff = ticket_page.html.findAll('div', attrs={'class': 'codehilite'})
+        added = diff[-1].findAll('span', attrs={'class': 'gi'})[-1]
+        assert_in('+test_root.py', added.getText())
 
     def test_delete_attachment(self):
         file_name = 'test_root.py'
@@ -806,8 +810,11 @@ class TestFunctionalController(TrackerTestController):
         self.app.post(str(file_link['href']), {
             'delete': 'True'
         })
-        deleted_form = self.app.get('/bugs/1/')
-        assert file_link not in deleted_form
+        ticket_page = self.app.get('/bugs/1/')
+        assert file_link not in ticket_page
+        diff = ticket_page.html.findAll('div', attrs={'class': 'codehilite'})
+        removed = diff[-1].findAll('span', attrs={'class': 'gd'})[-1]
+        assert_in('-test_root.py', removed.getText())
 
     def test_delete_attachment_from_comments(self):
         ticket_view = self.new_ticket(summary='test ticket').follow()


[15/22] allura git commit: [#7855] Updated a test to work with Pygments version 1.6 and higher

Posted by je...@apache.org.
[#7855] Updated a test to work with Pygments version 1.6 and higher


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

Branch: refs/heads/ib/6017
Commit: d731920ab24ea35c21cf0f57ef4ed040612720e9
Parents: 89981f0
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Tue Apr 14 19:16:25 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Apr 15 16:36:13 2015 +0000

----------------------------------------------------------------------
 Allura/allura/tests/test_utils.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/d731920a/Allura/allura/tests/test_utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index 06579c5..7903ba3 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -25,7 +25,7 @@ from os import path
 
 from webob import Request
 from mock import Mock, patch
-from nose.tools import assert_equal, assert_raises
+from nose.tools import assert_equal, assert_raises, assert_in
 from pygments import highlight
 from pygments.lexers import get_lexer_for_filename
 from tg import config
@@ -201,7 +201,7 @@ class TestLineAnchorCodeHtmlFormatter(unittest.TestCase):
         hl_code = highlight(code, lexer, formatter)
         assert '<div class="codehilite">' in hl_code
         assert '<div id="l1" class="code_block">' in hl_code
-        assert '<span class="lineno">1</span>' in hl_code
+        assert_in('<span class="lineno">1 </span>', hl_code)
 
 
 class TestIsTextFile(unittest.TestCase):


[08/22] allura git commit: [#7869] only apply patches once. Note how below the newrelic patch already avoids the issue

Posted by je...@apache.org.
[#7869] only apply patches once.  Note how below the newrelic patch already avoids the issue


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

Branch: refs/heads/ib/6017
Commit: 33dcb089e5ff37b468f7003eb1a20cb9dab98b4b
Parents: 99ed956
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Mon Apr 6 21:31:27 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Apr 6 21:31:27 2015 +0000

----------------------------------------------------------------------
 Allura/allura/lib/patches.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/allura/blob/33dcb089/Allura/allura/lib/patches.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/patches.py b/Allura/allura/lib/patches.py
index 19f91b3..2c363ea 100644
--- a/Allura/allura/lib/patches.py
+++ b/Allura/allura/lib/patches.py
@@ -26,8 +26,13 @@ import simplejson
 
 from allura.lib import helpers as h
 
-
+_patched = False
 def apply():
+    global _patched
+    if _patched:
+        return
+    _patched = True
+
     old_lookup_template_engine = tg.decorators.Decoration.lookup_template_engine
 
     @h.monkeypatch(tg.decorators.Decoration)