You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/05/17 16:01:06 UTC

[allura] branch db/8265 created (now bf8b1ba)

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a change to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git.


      at bf8b1ba  [#8265] when undoing a spam, report it as ham.  Show a message if something failed

This branch includes the following new commits:

     new aac4cc6  [#8265] avoid potential network errors during AkismetSpamFilter setup; errors within check() are already handled
     new 4b53e49  [#8265] avoid persisting tickets with None values, if error happened after new()
     new 7f8abeb  [#8265] remove mollom plugin (service no longer exists)
     new bf55fc2  [#8265] upgrade akismet library.  Send timestamps, original IP, make permalink a full url
     new bf8b1ba  [#8265] when undoing a spam, report it as ham.  Show a message if something failed

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[allura] 04/05: [#8265] upgrade akismet library. Send timestamps, original IP, make permalink a full url

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git

commit bf55fc2b64c6328ade7af33301a38304018615f0
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri May 17 11:45:13 2019 -0400

    [#8265] upgrade akismet library.  Send timestamps, original IP, make permalink a full url
---
 Allura/allura/lib/spam/akismetfilter.py       | 68 +++++++++++++++++----------
 Allura/allura/tests/unit/spam/test_akismet.py | 66 +++++++++++++++++---------
 requirements-optional.txt                     |  2 +-
 3 files changed, 89 insertions(+), 47 deletions(-)

diff --git a/Allura/allura/lib/spam/akismetfilter.py b/Allura/allura/lib/spam/akismetfilter.py
index 0c8e0f3..e30f05e 100644
--- a/Allura/allura/lib/spam/akismetfilter.py
+++ b/Allura/allura/lib/spam/akismetfilter.py
@@ -34,6 +34,14 @@ except ImportError:
 log = logging.getLogger(__name__)
 
 
+if AKISMET_AVAILABLE:
+    class AkismetWithoutStartupVerify(akismet.Akismet):
+        def __init__(self, key=None, blog_url=None):
+            # avoid possible errors at instantiation time, will encounter them later
+            self.api_key = key
+            self.blog_url = blog_url
+
+
 class AkismetSpamFilter(SpamFilter):
 
     """Spam checking implementation via Akismet service.
@@ -48,19 +56,27 @@ class AkismetSpamFilter(SpamFilter):
     def __init__(self, config):
         if not AKISMET_AVAILABLE:
             raise ImportError('akismet not available')
-        self.service = akismet.Akismet(
-            config.get('spam.key'), config.get('base_url'))
+        self.service = AkismetWithoutStartupVerify(config['spam.key'], config['base_url'])
 
     def get_data(self, text, artifact=None, user=None, content_type='comment', request=None, **kw):
+        # Docs: https://akismet.com/development/api/
         kw['comment_content'] = text
         kw['comment_type'] = content_type
         if artifact:
             try:
                 # if its a comment, get wiki, ticket, etc URL
                 url = artifact.main_url()
-            except:
+            except Exception:
                 url = artifact.url()
-            kw['permalink'] = url
+            kw['permalink'] = h.absurl(url)
+            if hasattr(artifact, 'timestamp'):
+                # Message & Post objects
+                date_created = artifact.timestamp
+            else:
+                # fallback for other artifacts, not exactly "created" date though
+                date_created = artifact.mod_date
+            kw['comment_date_gmt'] = date_created.isoformat()
+            kw['comment_post_modified_gmt'] = artifact.primary().mod_date.isoformat()
         user = user or c.user
         if user:
             kw['comment_author'] = user.display_name or user.username
@@ -69,35 +85,39 @@ class AkismetSpamFilter(SpamFilter):
             kw['user_ip'] = utils.ip_address(request)
             kw['user_agent'] = request.headers.get('USER_AGENT')
             kw['referrer'] = request.headers.get('REFERER')
+        else:
+            # these are required fields, but for ham/spam reports we don't have the original values to send :/
+            kw['user_ip'] = None
+            kw['user_agent'] = None
+            if artifact and hasattr(artifact, 'get_version'):  # VersionedArtifacts (includes comment posts)
+                try:
+                    kw['user_ip'] = artifact.get_version(1).author.logged_ip
+                except IndexError:
+                    log.debug("couldn't get Snapshot for this artifact %s", artifact)
+
         # kw will be urlencoded, need to utf8-encode
         for k, v in kw.items():
             kw[k] = h.really_unicode(v).encode('utf8')
         return kw
 
     def check(self, text, artifact=None, user=None, content_type='comment', **kw):
-        res = self.service.comment_check(text,
-                                         data=self.get_data(text=text,
-                                                            artifact=artifact,
-                                                            user=user,
-                                                            content_type=content_type,
-                                                            request=request,
-                                                            ),
-                                         build_data=False)
+        res = self.service.comment_check(**self.get_data(text=text,
+                                                         artifact=artifact,
+                                                         user=user,
+                                                         content_type=content_type,
+                                                         request=request,
+                                                         ))
         self.record_result(res, artifact, user)
         return res
 
     def submit_spam(self, text, artifact=None, user=None, content_type='comment'):
-        self.service.submit_spam(text,
-                                 data=self.get_data(text=text,
-                                                    artifact=artifact,
-                                                    user=user,
-                                                    content_type=content_type),
-                                 build_data=False)
+        self.service.submit_spam(**self.get_data(text=text,
+                                                 artifact=artifact,
+                                                 user=user,
+                                                 content_type=content_type))
 
     def submit_ham(self, text, artifact=None, user=None, content_type='comment'):
-        self.service.submit_ham(text,
-                                data=self.get_data(text=text,
-                                                   artifact=artifact,
-                                                   user=user,
-                                                   content_type=content_type),
-                                build_data=False)
+        self.service.submit_ham(**self.get_data(text=text,
+                                                artifact=artifact,
+                                                user=user,
+                                                content_type=content_type))
diff --git a/Allura/allura/tests/unit/spam/test_akismet.py b/Allura/allura/tests/unit/spam/test_akismet.py
index 17e6b51..f089303 100644
--- a/Allura/allura/tests/unit/spam/test_akismet.py
+++ b/Allura/allura/tests/unit/spam/test_akismet.py
@@ -21,6 +21,7 @@
 import mock
 import unittest
 import urllib
+from datetime import datetime
 
 from bson import ObjectId
 
@@ -32,17 +33,28 @@ class TestAkismet(unittest.TestCase):
 
     @mock.patch('allura.lib.spam.akismetfilter.akismet')
     def setUp(self, akismet_lib):
-        self.akismet = AkismetSpamFilter({})
+        self.akismet = AkismetSpamFilter({'spam.key': 'example', 'base_url': 'http://localhost/'})
 
         def side_effect(*args, **kw):
             # side effect to test that data being sent to
             # akismet can be successfully urlencoded
             urllib.urlencode(kw.get('data', {}))
         self.akismet.service.comment_check = mock.Mock(side_effect=side_effect)
+        self.akismet.service.submit_spam = mock.Mock(side_effect=side_effect)
+        self.akismet.service.submit_ham = mock.Mock(side_effect=side_effect)
         self.fake_artifact = mock.Mock(**{
-            'main_url.return_value': 'artifact url',
+            'main_url.return_value': '/artifact-url',
             'project_id': ObjectId(),
             'ref': None,
+            'timestamp': datetime(2019, 5, 17),
+            'primary.return_value': mock.Mock(
+                mod_date=datetime(2019, 5, 17, 0, 5, 1)
+            ),
+            'get_version.return_value': mock.Mock(
+                author=mock.Mock(
+                    logged_ip='33.4.5.66',
+                )
+            ),
         })
         self.fake_user = mock.Mock(display_name=u'Søme User',
                                    email_addresses=['user@domain'],
@@ -66,9 +78,7 @@ class TestAkismet(unittest.TestCase):
         c.user = None
         self.akismet.service.comment_check.side_effect({'side_effect': ''})
         self.akismet.check(self.content)
-        self.akismet.service.comment_check.assert_called_once_with(
-            self.content,
-            data=self.expected_data, build_data=False)
+        self.akismet.service.comment_check.assert_called_once_with(**self.expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     @mock.patch('allura.lib.spam.akismetfilter.request')
@@ -78,9 +88,7 @@ class TestAkismet(unittest.TestCase):
         c.user = None
         self.akismet.check(self.content, content_type='some content type')
         self.expected_data['comment_type'] = 'some content type'
-        self.akismet.service.comment_check.assert_called_once_with(
-            self.content,
-            data=self.expected_data, build_data=False)
+        self.akismet.service.comment_check.assert_called_once_with(**self.expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     @mock.patch('allura.lib.spam.akismetfilter.request')
@@ -90,10 +98,10 @@ class TestAkismet(unittest.TestCase):
         c.user = None
         self.akismet.check(self.content, artifact=self.fake_artifact)
         expected_data = self.expected_data
-        expected_data['permalink'] = 'artifact url'
-        self.akismet.service.comment_check.assert_called_once_with(
-            self.content,
-            data=expected_data, build_data=False)
+        expected_data['permalink'] = 'http://localhost/artifact-url'
+        expected_data['comment_date_gmt'] = '2019-05-17T00:00:00'
+        expected_data['comment_post_modified_gmt'] = '2019-05-17T00:05:01'
+        self.akismet.service.comment_check.assert_called_once_with(**expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     @mock.patch('allura.lib.spam.akismetfilter.request')
@@ -105,9 +113,7 @@ class TestAkismet(unittest.TestCase):
         expected_data = self.expected_data
         expected_data.update(comment_author=u'Søme User'.encode('utf8'),
                              comment_author_email='user@domain')
-        self.akismet.service.comment_check.assert_called_once_with(
-            self.content,
-            data=expected_data, build_data=False)
+        self.akismet.service.comment_check.assert_called_once_with(**expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     @mock.patch('allura.lib.spam.akismetfilter.request')
@@ -119,9 +125,7 @@ class TestAkismet(unittest.TestCase):
         expected_data = self.expected_data
         expected_data.update(comment_author=u'Søme User'.encode('utf8'),
                              comment_author_email='user@domain')
-        self.akismet.service.comment_check.assert_called_once_with(
-            self.content,
-            data=expected_data, build_data=False)
+        self.akismet.service.comment_check.assert_called_once_with(**expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     def test_submit_spam(self, c):
@@ -132,9 +136,10 @@ class TestAkismet(unittest.TestCase):
         # no IP addr, UA, etc, since this isn't the original request
         expected_data = dict(comment_content=u'spåm text'.encode('utf8'),
                              comment_type='comment',
+                             user_ip='',
+                             user_agent='',
                              )
-        self.akismet.service.submit_spam.assert_called_once_with(
-            self.content, data=expected_data, build_data=False)
+        self.akismet.service.submit_spam.assert_called_once_with(**expected_data)
 
     @mock.patch('allura.lib.spam.akismetfilter.c')
     def test_submit_ham(self, c):
@@ -145,6 +150,23 @@ class TestAkismet(unittest.TestCase):
         # no IP addr, UA, etc, since this isn't the original request
         expected_data = dict(comment_content=u'spåm text'.encode('utf8'),
                              comment_type='comment',
+                             user_ip='',
+                             user_agent='',
                              )
-        self.akismet.service.submit_ham.assert_called_once_with(
-            self.content, data=expected_data, build_data=False)
+        self.akismet.service.submit_ham.assert_called_once_with(**expected_data)
+
+    @mock.patch('allura.lib.spam.akismetfilter.c')
+    def test_submit_ham_with_artifact(self, c):
+        c.user = None
+
+        self.akismet.submit_ham(self.content, artifact=self.fake_artifact)
+
+        expected_data = dict(comment_content=u'spåm text'.encode('utf8'),
+                             comment_type='comment',
+                             user_ip='33.4.5.66',
+                             user_agent='',
+                             permalink='http://localhost/artifact-url',
+                             comment_date_gmt='2019-05-17T00:00:00',
+                             comment_post_modified_gmt='2019-05-17T00:05:01',
+                             )
+        self.akismet.service.submit_ham.assert_called_once_with(**expected_data)
diff --git a/requirements-optional.txt b/requirements-optional.txt
index aa64970..2da797a 100644
--- a/requirements-optional.txt
+++ b/requirements-optional.txt
@@ -13,7 +13,7 @@
 MySQL-python  # GPL
 
 # for spam checking
-akismet==0.2.0
+akismet==1.0.1
 
 # faster charset detection
 cchardet==2.1.1  # GPL
\ No newline at end of file


[allura] 05/05: [#8265] when undoing a spam, report it as ham. Show a message if something failed

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git

commit bf8b1ba83e86d0e0c54a33190812d7c66e2b85ab
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Fri May 17 11:53:19 2019 -0400

    [#8265] when undoing a spam, report it as ham.  Show a message if something failed
---
 Allura/allura/controllers/discuss.py           |  2 ++
 Allura/allura/lib/widgets/resources/js/post.js | 10 ++++++++--
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/controllers/discuss.py b/Allura/allura/controllers/discuss.py
index 52a1b3c..7a38484 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -429,6 +429,8 @@ class PostController(BaseController):
             self.post.spam()
         elif kw.pop('undo', None):
             prev_status = kw.pop('prev_status', None)
+            if self.post.status == 'spam' and prev_status == 'ok':
+                g.spam_checker.submit_ham(self.post.text, artifact=self.post, user=self.post.author())
             self.post.undo(prev_status)
         elif kw.pop('approve', None):
             if self.post.status != 'ok':
diff --git a/Allura/allura/lib/widgets/resources/js/post.js b/Allura/allura/lib/widgets/resources/js/post.js
index a4811dd..1da44aa 100644
--- a/Allura/allura/lib/widgets/resources/js/post.js
+++ b/Allura/allura/lib/widgets/resources/js/post.js
@@ -51,7 +51,10 @@
                     else if (mod == 'Undo'){
                         spam_block_display($(post), 'hide_spam');
                     }
-                }
+                },
+                error: function() {
+                    flash('Oops, something went wrong.', 'error')
+                },
             });
         });
 
@@ -80,7 +83,10 @@
                     } else {
                         flash('Error.  Make sure you are logged in still.', 'error');
                     }
-                }
+                },
+                error: function() {
+                    flash('Oops, something went wrong.', 'error')
+                },
             });
         });
 


[allura] 03/05: [#8265] remove mollom plugin (service no longer exists)

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 7f8abeb86d8467b9298c62a2aae8df8f99198703
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Thu May 16 17:20:41 2019 -0400

    [#8265] remove mollom plugin (service no longer exists)
---
 Allura/allura/lib/spam/mollomfilter.py       |  92 ------------------------
 Allura/allura/tests/unit/spam/test_mollom.py | 100 ---------------------------
 Allura/development.ini                       |   5 +-
 Allura/setup.py                              |   1 -
 requirements-optional.txt                    |   3 +-
 5 files changed, 2 insertions(+), 199 deletions(-)

diff --git a/Allura/allura/lib/spam/mollomfilter.py b/Allura/allura/lib/spam/mollomfilter.py
deleted file mode 100644
index ddd2e86..0000000
--- a/Allura/allura/lib/spam/mollomfilter.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import logging
-
-from tg import request
-from tg import tmpl_context as c
-
-from allura.lib import helpers as h
-from allura.lib import utils
-from allura.lib.spam import SpamFilter
-
-try:
-    import Mollom
-    MOLLOM_AVAILABLE = True
-except ImportError:
-    MOLLOM_AVAILABLE = False
-
-
-log = logging.getLogger(__name__)
-
-
-class MollomSpamFilter(SpamFilter):
-
-    """Spam checking implementation via Mollom service.
-
-    To enable Mollom spam filtering in your Allura instance, first pip install PyMollom (see requirements-optional.txt)
-    and then include the following parameters in your .ini file::
-
-        spam.method = mollom
-        spam.public_key = <your Mollom public key here>
-        spam.private_key = <your Mollom private key here>
-    """
-
-    def __init__(self, config):
-        if not MOLLOM_AVAILABLE:
-            raise ImportError('Mollom not available')
-        self.service = Mollom.MollomAPI(
-            publicKey=config.get('spam.public_key'),
-            privateKey=config.get('spam.private_key'))
-        if not self.service.verifyKey():
-            raise Mollom.MollomFault('Your MOLLOM credentials are invalid.')
-
-    def check(self, text, artifact=None, user=None, content_type='comment', **kw):
-        """Basic content spam check via Mollom. For more options
-        see http://mollom.com/api#api-content
-        """
-        log_msg = text
-        kw['postBody'] = text
-        if artifact:
-            try:
-                # if its a comment, get wiki, ticket, etc URL
-                url = artifact.main_url()
-            except:
-                url = artifact.url()
-            # Should be able to send url, but can't right now due to a bug in
-            # the PyMollom lib
-            # kw['url'] = url
-        user = user or c.user
-        if user:
-            kw['authorName'] = user.display_name or user.username
-            kw['authorMail'] = user.email_addresses[
-                0] if user.email_addresses else ''
-        kw['authorIP'] = utils.ip_address(request)
-        # kw will be urlencoded, need to utf8-encode
-        for k, v in kw.items():
-            kw[k] = h.really_unicode(v).encode('utf8')
-        cc = self.service.checkContent(**kw)
-        res = cc['spam'] == 2
-        artifact.spam_check_id = cc.get('session_id', '')
-        self.record_result(res, artifact, user)
-        return res
-
-    def submit_spam(self, text, artifact=None, user=None, content_type='comment', **kw):
-        self.service.sendFeedback(artifact.spam_check_id, 'spam')
-
-    def submit_ham(self, *args, **kw):
-        log.info("Mollom doesn't support reporting a ham")
diff --git a/Allura/allura/tests/unit/spam/test_mollom.py b/Allura/allura/tests/unit/spam/test_mollom.py
deleted file mode 100644
index 8a871f8..0000000
--- a/Allura/allura/tests/unit/spam/test_mollom.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# -*- coding: utf-8 -*-
-
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-
-import mock
-import unittest
-import urllib
-
-from bson import ObjectId
-
-from allura.lib.spam.mollomfilter import MOLLOM_AVAILABLE, MollomSpamFilter
-
-
-@unittest.skipIf(not MOLLOM_AVAILABLE, "Mollom not available")
-class TestMollom(unittest.TestCase):
-
-    @mock.patch('allura.lib.spam.mollomfilter.Mollom')
-    def setUp(self, mollom_lib):
-        self.mollom = MollomSpamFilter({})
-
-        def side_effect(*args, **kw):
-            # side effect to test that data being sent to
-            # mollom can be successfully urlencoded
-            urllib.urlencode(kw.get('data', {}))
-            return dict(spam=2)
-        self.mollom.service.checkContent = mock.Mock(side_effect=side_effect,
-                                                     return_value=dict(spam=2))
-        self.fake_artifact = mock.Mock(**{'url.return_value': 'artifact url'})
-        self.fake_user = mock.Mock(display_name=u'Søme User',
-                                   email_addresses=['user@domain'],
-                                   _id=ObjectId())
-        self.fake_headers = dict(
-            USER_AGENT='some browser',
-            REFERER='some url')
-        self.content = u'spåm text'
-        self.expected_data = dict(
-            postBody=self.content.encode('utf8'),
-            authorIP='some ip')
-        self.artifact = mock.Mock()
-        self.artifact.spam_check_id = 'test_id'
-        self.artifact.project_id = ObjectId()
-        self.artifact.ref = None
-
-    @mock.patch('allura.lib.spam.mollomfilter.c')
-    @mock.patch('allura.lib.spam.mollomfilter.request')
-    def test_check(self, request, c):
-        request.headers = self.fake_headers
-        request.remote_addr = 'some ip'
-        c.user = None
-        self.mollom.check(self.content, artifact=self.artifact)
-        self.mollom.service.checkContent.assert_called_once_with(
-            **self.expected_data)
-
-    @mock.patch('allura.lib.spam.mollomfilter.c')
-    @mock.patch('allura.lib.spam.mollomfilter.request')
-    def test_check_with_user(self, request, c):
-        request.headers = self.fake_headers
-        request.remote_addr = 'some ip'
-        c.user = None
-        self.mollom.check(self.content, user=self.fake_user,
-                          artifact=self.artifact)
-        expected_data = self.expected_data
-        expected_data.update(authorName=u'Søme User'.encode('utf8'),
-                             authorMail='user@domain')
-        self.mollom.service.checkContent.assert_called_once_with(
-            **self.expected_data)
-
-    @mock.patch('allura.lib.spam.mollomfilter.c')
-    @mock.patch('allura.lib.spam.mollomfilter.request')
-    def test_check_with_implicit_user(self, request, c):
-        request.headers = self.fake_headers
-        request.remote_addr = 'some ip'
-        c.user = self.fake_user
-        self.mollom.check(self.content, artifact=self.artifact)
-        expected_data = self.expected_data
-        expected_data.update(authorName=u'Søme User'.encode('utf8'),
-                             authorMail='user@domain')
-        self.mollom.service.checkContent.assert_called_once_with(
-            **self.expected_data)
-
-    def test_submit_spam(self):
-        self.mollom.submit_spam('test', artifact=self.artifact)
-        assert self.mollom.service.sendFeedback.call_args[0] == (
-            'test_id', 'spam'), self.mollom.service.sendFeedback.call_args[0]
diff --git a/Allura/development.ini b/Allura/development.ini
index ed2cf50..a02ca17 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -254,13 +254,10 @@ personal_dashboard_sections.order = activity, tickets, merge_requests, projects
 site_admin_project = allura
 site_admin_project_nbhd = Projects
 
-; Spam filtering service: this can be one or more of: mollom akismet stopforumspam
+; Spam filtering service: this can be one or more of: akismet stopforumspam
 ;spam.method = akismet
 ; for akismet:
 ;spam.key =
-; for mollom:
-;spam.public_key =
-;spam.private_key =
 ; for stopforumspam, should be a listed_ip_*_all.txt file
 ;spam.stopforumspam.ip_addr_file =
 ;spam.stopforumspam.threshold = 20
diff --git a/Allura/setup.py b/Allura/setup.py
index 4de3c6b..81133c9 100644
--- a/Allura/setup.py
+++ b/Allura/setup.py
@@ -109,7 +109,6 @@ setup(
 
     [allura.spam]
     akismet = allura.lib.spam.akismetfilter:AkismetSpamFilter
-    mollom = allura.lib.spam.mollomfilter:MollomSpamFilter
     stopforumspam = allura.lib.spam.stopforumspamfilter:StopForumSpamSpamFilter
 
     [allura.phone]
diff --git a/requirements-optional.txt b/requirements-optional.txt
index a94210b..aa64970 100644
--- a/requirements-optional.txt
+++ b/requirements-optional.txt
@@ -12,9 +12,8 @@
 -e git://github.com/zikzakmedia/python-mediawiki.git#egg=python-mediawiki   # GPL
 MySQL-python  # GPL
 
-# One or the other is required to enable spam checking
+# for spam checking
 akismet==0.2.0
-PyMollom==0.1  # GPL
 
 # faster charset detection
 cchardet==2.1.1  # GPL
\ No newline at end of file


[allura] 02/05: [#8265] avoid persisting tickets with None values, if error happened after new()

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 4b53e49202916395d4585bce88faff251c0b468d
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Thu May 16 17:21:22 2019 -0400

    [#8265] avoid persisting tickets with None values, if error happened after new()
---
 ForgeTracker/forgetracker/model/ticket.py | 27 ++++++++++++++++++++-------
 ForgeTracker/forgetracker/tracker_main.py |  5 +++--
 2 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 9d5611c..fc30b87 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -664,14 +664,16 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
         return d
 
     @classmethod
-    def new(cls):
-        '''Create a new ticket, safely (ensuring a unique ticket_num'''
+    def new(cls, form_fields=None):
+        '''Create a new ticket, safely (ensuring a unique ticket_num)'''
         while True:
             ticket_num = c.app.globals.next_ticket_num()
             ticket = cls(
                 app_config_id=c.app.config._id,
                 custom_fields=dict(),
                 ticket_num=ticket_num)
+            if form_fields:
+                ticket.update_fields_basics(form_fields)
             try:
                 session(ticket).flush(ticket)
                 h.log_action(log, 'opened').info('')
@@ -953,10 +955,11 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
             return 'nobody'
         return who.get_pref('display_name')
 
-    def update(self, ticket_form):
+    def update_fields_basics(self, ticket_form):
+        # "simple", non-persisting updates.  Must be safe to call within the Ticket.new() while its creating it
+
         # update is not allowed to change the ticket_num
         ticket_form.pop('ticket_num', None)
-        subscribe = ticket_form.pop('subscribe', False)
         self.labels = ticket_form.pop('labels', [])
         custom_users = set()
         other_custom_fields = set()
@@ -973,15 +976,15 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
             if 'custom_fields' not in ticket_form:
                 ticket_form['custom_fields'] = dict()
             ticket_form['custom_fields']['_milestone'] = milestone
-        attachment = None
-        if 'attachment' in ticket_form:
-            attachment = ticket_form.pop('attachment')
         for k, v in ticket_form.iteritems():
             if k == 'assigned_to':
                 if v:
                     user = c.project.user_in_project(v)
                     if user:
                         self.assigned_to_id = user._id
+            elif k in ('subscribe', 'attachment'):
+                # handled separately in update_fields_finish()
+                pass
             else:
                 setattr(self, k, v)
         if 'custom_fields' in ticket_form:
@@ -994,13 +997,23 @@ class Ticket(VersionedArtifact, ActivityObject, VotableArtifact):
                 elif k in other_custom_fields:
                     # strings are good enough for any other custom fields
                     self.custom_fields[k] = v
+
+    def update_fields_finish(self, ticket_form):
+        attachment = None
+        if 'attachment' in ticket_form:
+            attachment = ticket_form.pop('attachment')
         if attachment is not None:
             self.add_multiple_attachments(attachment)
             # flush the session to make attachments available in the
             # notification email
             ThreadLocalORMSession.flush_all()
+        subscribe = ticket_form.pop('subscribe', False)
         self.commit(subscribe=subscribe)
 
+    def update(self, ticket_form):
+        self.update_fields_basics(ticket_form)
+        self.update_fields_finish(ticket_form)
+
     def _move_attach(self, attachments, attach_metadata, app_config):
         for attach in attachments:
             attach.app_config_id = app_config._id
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 1be5070..d2c567e 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -937,13 +937,14 @@ class RootController(BaseController, FeedController):
             if not ticket:
                 raise Exception('Ticket number not found.')
             require_access(ticket, 'update')
+            ticket.update_fields_basics(ticket_form)
         else:
             require_access(c.app, 'create')
             self.rate_limit(TM.Ticket, 'Ticket creation', redir='.')
-            ticket = TM.Ticket.new()
+            ticket = TM.Ticket.new(form_fields=ticket_form)
+        ticket.update_fields_finish(ticket_form)
         g.spam_checker.check(ticket_form['summary'] + u'\n' + ticket_form.get('description', ''), artifact=ticket,
                              user=c.user, content_type='ticket')
-        ticket.update(ticket_form)
         c.app.globals.invalidate_bin_counts()
         g.director.create_activity(c.user, 'created', ticket,
                                    related_nodes=[c.project], tags=['ticket'])


[allura] 01/05: [#8265] avoid potential network errors during AkismetSpamFilter setup; errors within check() are already handled

Posted by br...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8265
in repository https://gitbox.apache.org/repos/asf/allura.git

commit aac4cc62e89864e8e2284e54051b6556a4292ddc
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Thu May 16 16:55:45 2019 -0400

    [#8265] avoid potential network errors during AkismetSpamFilter setup; errors within check() are already handled
---
 Allura/allura/lib/spam/__init__.py                | 1 +
 Allura/allura/lib/spam/akismetfilter.py           | 1 -
 Allura/allura/tests/unit/spam/test_spam_filter.py | 2 +-
 3 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/lib/spam/__init__.py b/Allura/allura/lib/spam/__init__.py
index aaa4412..b51f20d 100644
--- a/Allura/allura/lib/spam/__init__.py
+++ b/Allura/allura/lib/spam/__init__.py
@@ -88,6 +88,7 @@ class ChainedSpamFilter(SpamFilter):
 
     def check(self, *a, **kw):
         for spam_filter in self.filters:
+            # note: SpamFilter.get() has wrapped all .check() functions with exceptionless
             if spam_filter.check(*a, **kw):
                 return True
         return False
diff --git a/Allura/allura/lib/spam/akismetfilter.py b/Allura/allura/lib/spam/akismetfilter.py
index 2ea4c13..0c8e0f3 100644
--- a/Allura/allura/lib/spam/akismetfilter.py
+++ b/Allura/allura/lib/spam/akismetfilter.py
@@ -50,7 +50,6 @@ class AkismetSpamFilter(SpamFilter):
             raise ImportError('akismet not available')
         self.service = akismet.Akismet(
             config.get('spam.key'), config.get('base_url'))
-        self.service.verify_key()
 
     def get_data(self, text, artifact=None, user=None, content_type='comment', request=None, **kw):
         kw['comment_content'] = text
diff --git a/Allura/allura/tests/unit/spam/test_spam_filter.py b/Allura/allura/tests/unit/spam/test_spam_filter.py
index d05fa97..607273d 100644
--- a/Allura/allura/tests/unit/spam/test_spam_filter.py
+++ b/Allura/allura/tests/unit/spam/test_spam_filter.py
@@ -106,7 +106,7 @@ class TestChainedSpamFilter(object):
         assert_equal(checker.filters[0].config, {'spam.method': 'mock1', 'spam.settingA': 'bcd'})
         assert_equal(checker.filters[1].config, {'spam.method': 'mock2', 'spam.settingA': 'bcd'})
 
-        assert checker.check()  # True because first filter errors out, and 2nd returns True
+        assert checker.check()  # first filter errors out (but ignored by `exceptionless`), and 2nd returns True
 
         checker.submit_spam('some text')
         checker.submit_ham('some text')