You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by he...@apache.org on 2015/04/01 19:39:27 UTC
allura git commit: [#7850] Ability to close discussion on a ticket.
Repository: allura
Updated Branches:
refs/heads/hs/7850 [created] b22eedeb1
[#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/b22eedeb
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/b22eedeb
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/b22eedeb
Branch: refs/heads/hs/7850
Commit: b22eedeb1385a79c4ce4e095ebed27b1e8067a1a
Parents: 758b51b
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Wed Apr 1 13:37:36 2015 -0400
Committer: Heith Seewald <hs...@slashdotmedia.com>
Committed: Wed Apr 1 13:38:50 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/b22eedeb/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/b22eedeb/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/b22eedeb/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/b22eedeb/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"> </div>
<div class="grid-6">
http://git-wip-us.apache.org/repos/asf/allura/blob/b22eedeb/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 --> 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 --> 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 --> 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/b22eedeb/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/b22eedeb/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',