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

kentontaylor pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 9f34b77e149de604ec5dc9be51ed4058b397c4f0
Author: Guillermo Cruz <[email protected]>
AuthorDate: Mon Sep 8 17:14:01 2025 +0000

    [#8584] added missing validate function to formencode validators
---
 Allura/allura/config/app_cfg.py                      | 20 ++++++++++++++++----
 Allura/allura/controllers/discuss.py                 |  2 +-
 Allura/allura/controllers/project.py                 |  2 +-
 Allura/allura/lib/validators.py                      |  2 +-
 Allura/allura/lib/widgets/forms.py                   | 16 +++++++++++++++-
 Allura/allura/lib/widgets/subscriptions.py           |  5 +++++
 .../test_discussion_moderation_controller.py         |  4 ++--
 Allura/allura/tests/unit/patches.py                  |  3 +++
 AlluraTest/alluratest/validation.py                  |  1 +
 ForgeActivity/forgeactivity/widgets/follow.py        |  5 +++++
 ForgeBlog/forgeblog/widgets.py                       |  5 +++++
 ForgeDiscussion/forgediscussion/controllers/forum.py |  2 +-
 ForgeDiscussion/forgediscussion/widgets/admin.py     |  2 +-
 ForgeTracker/forgetracker/widgets/bin_form.py        |  5 +++++
 ForgeTracker/forgetracker/widgets/ticket_form.py     |  5 +++++
 15 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/Allura/allura/config/app_cfg.py b/Allura/allura/config/app_cfg.py
index 704fdd72f..b545f043c 100644
--- a/Allura/allura/config/app_cfg.py
+++ b/Allura/allura/config/app_cfg.py
@@ -90,12 +90,14 @@ def __init__(self, root_controller=None):
 
             # prevent dispatcher from striping extensions like '.io' from URLs
             'disable_request_extensions': True,
-            'validation.exceptions': [formencode.Invalid, 
formencode.api.Invalid],
+            'validation.exceptions': [formencode.Invalid, 
formencode.api.Invalid,],
              'validation.validators': {
                 formencode.Schema: lambda schema, params: 
schema.to_python(params, state=tg.request),
-                #formencode.FancyValidator: lambda validator, params: 
validator.to_python(params, state=tg.request),
+                formencode.FancyValidator: lambda validator, params: 
validator.to_python(params, state=tg.request),
+            },
+            'validation.explode': {
+                formencode.Invalid: self.explode_formencode_invalid,
             },
-            'validation.explode': {formencode.Invalid: 
self.explode_formencode_invalid},
 
             # if left to True (default) would use 
crank.util.default_path_translator to convert all URL punctuation to "_"
             # which is convenient for /foo-bar to execute a "def foo_bar" 
method, but is a pretty drastic change for us
@@ -106,7 +108,17 @@ def __init__(self, root_controller=None):
         self.replace(TemplateRenderingConfigurationComponent.id, 
AlluraTemplateConfig)
 
     def explode_formencode_invalid(self, err):
-        return {"errors": err.error_dict, "values": err.value}
+        errors = {}
+        if getattr(err, 'error_dict', None):
+            for k, v in getattr(err, 'error_dict', {}).items():
+                if isinstance(v, formencode.Invalid):
+                    errors[k] = v.msg
+                else:
+                    errors[k] = v
+        else:
+            errors = err.msg
+        values = {str(k): v for k, v in getattr(err, 'value', {}).items()} if 
hasattr(err, 'value') and isinstance(err.value, dict) else {}
+        return {"errors": errors, "values": values}
 
 class AlluraTemplateConfig(TemplateRenderingConfigurationComponent):
 
diff --git a/Allura/allura/controllers/discuss.py 
b/Allura/allura/controllers/discuss.py
index e5ed81f87..c890696e7 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -52,7 +52,7 @@
 
 
 class pass_validator:
-    def validate(self, v, s):
+    def validate(self, v, s=None):
         return v
 
 
diff --git a/Allura/allura/controllers/project.py 
b/Allura/allura/controllers/project.py
index 86ba0a7b4..cf521e59c 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -151,7 +151,7 @@ def index(self, sort='alpha', limit=25, page=0, **kw):
     def add_project(self, **form_data):
         with h.login_overlay():
             require_access(self.neighborhood, 'register')
-        verify = request.validation.errors == {'_the_form': 
'phone-verification'}
+        verify = 'phone-verification' in request.validation.errors
         c.show_phone_verification_overlay = verify
         c.add_project = W.add_project
         form_data.setdefault('tools', W.add_project.default_tools)
diff --git a/Allura/allura/lib/validators.py b/Allura/allura/lib/validators.py
index 1a949d7e0..08586706f 100644
--- a/Allura/allura/lib/validators.py
+++ b/Allura/allura/lib/validators.py
@@ -153,7 +153,7 @@ def to_python(self, value, state):
     def from_python(self, value, state):
         return value
 
-    def validate(self, value, state):
+    def validate(self, value, state=None):
         return value
 
 
diff --git a/Allura/allura/lib/widgets/forms.py 
b/Allura/allura/lib/widgets/forms.py
index 51e42a55f..032a7c69d 100644
--- a/Allura/allura/lib/widgets/forms.py
+++ b/Allura/allura/lib/widgets/forms.py
@@ -117,6 +117,10 @@ def display_field(self, field, ignore_errors=False):
             display += Markup("<div 
class='error'>{}</div>").format(ctx['errors'])
         return display
 
+    def validate(self, value, state=None):
+        state = getattr(tg, 'request', None)
+        return super().validate(value, state)
+
 
 class ForgeFormResponsive(ForgeForm):
     def __init__(self):
@@ -808,6 +812,10 @@ def resources(self):
 class AdminForm(ForgeForm):
     template = 'jinja:allura:templates/widgets/admin_form.html'
 
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(state, 'request') else state
+        return super().validate(value, state)
+
 
 class AdminFormResponsive(ForgeForm):
     def __init__(self):
@@ -982,10 +990,11 @@ def fields(self):
 
     @ew_core.core.validator
     def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
         value = super().validate(value, state)
         provider = plugin.ProjectRegistrationProvider.get()
         if not provider.phone_verified(c.user, c.project.neighborhood):
-            raise formencode.Invalid('phone-verification', value, None)
+            raise formencode.Invalid('phone-verification', value, None, 
error_dict={'phone-verification': 'verification required'})
         return value
 
     def resources(self):
@@ -1118,6 +1127,11 @@ def context_for(self, field):
             ctx['value'] = tg.request.cookies.get('_csrf_token') or 
tg.request.environ['_csrf_token']
         return ctx
 
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
+
+
 
 class AwardGrantForm(ForgeForm):
 
diff --git a/Allura/allura/lib/widgets/subscriptions.py 
b/Allura/allura/lib/widgets/subscriptions.py
index 64bc33c9f..64ee11e05 100644
--- a/Allura/allura/lib/widgets/subscriptions.py
+++ b/Allura/allura/lib/widgets/subscriptions.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import tg
 import ew as ew_core
 import ew.jinja2_ew as ew
 from tg import tmpl_context as c
@@ -90,3 +91,7 @@ def resources(self):
         yield from super().resources()
         if not c.user.is_anonymous():
             yield ew.JSLink('js/subscriptions.js', location='body_js_tail')  # 
location, to force after react js files
+
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
\ No newline at end of file
diff --git 
a/Allura/allura/tests/unit/controllers/test_discussion_moderation_controller.py 
b/Allura/allura/tests/unit/controllers/test_discussion_moderation_controller.py
index 2b9a01fff..8faef962d 100644
--- 
a/Allura/allura/tests/unit/controllers/test_discussion_moderation_controller.py
+++ 
b/Allura/allura/tests/unit/controllers/test_discussion_moderation_controller.py
@@ -73,7 +73,7 @@ def get_post(self):
 
 
 class TestIndexWithNoPosts(WithDatabase):
-    patches = [patches.fake_app_patch]
+    patches = [patches.fake_app_patch, patches.fake_form_request_patch]
 
     def test_that_it_returns_no_posts(self):
         discussion = create_discussion()
@@ -82,7 +82,7 @@ def test_that_it_returns_no_posts(self):
 
 
 class TestIndexWithAPostInTheDiscussion(WithDatabase):
-    patches = [patches.fake_app_patch]
+    patches = [patches.fake_app_patch, patches.fake_form_request_patch]
 
     def setup_method(self, method):
         super().setup_method(method)
diff --git a/Allura/allura/tests/unit/patches.py 
b/Allura/allura/tests/unit/patches.py
index 630beeccf..ddb92ff00 100644
--- a/Allura/allura/tests/unit/patches.py
+++ b/Allura/allura/tests/unit/patches.py
@@ -17,6 +17,7 @@
 
 from mock import Mock, patch, MagicMock
 from tg import tmpl_context as c
+import tg
 
 from allura.tests.unit.factories import (
     create_project,
@@ -62,3 +63,5 @@ def fake_request_patch(test_case):
                  MagicMock(
                      referer='.'
                  ))
+def fake_form_request_patch(test_case):
+    return patch('tg.request', MagicMock(referer='.'))
diff --git a/AlluraTest/alluratest/validation.py 
b/AlluraTest/alluratest/validation.py
index 2a13b9eb4..78d9fced4 100644
--- a/AlluraTest/alluratest/validation.py
+++ b/AlluraTest/alluratest/validation.py
@@ -27,6 +27,7 @@
 import re
 import importlib.resources
 
+import tg
 import webtest
 from webtest import TestApp, TestResponse
 from ming.utils import LazyProperty
diff --git a/ForgeActivity/forgeactivity/widgets/follow.py 
b/ForgeActivity/forgeactivity/widgets/follow.py
index 6a81ede1f..d2aa0f665 100644
--- a/ForgeActivity/forgeactivity/widgets/follow.py
+++ b/ForgeActivity/forgeactivity/widgets/follow.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import tg
 from tg import tmpl_context as c
 from formencode import validators as fev
 import ew as ew_core
@@ -55,3 +56,7 @@ def success_message(self, following):
             action=context['action_label'],
             thing=context['thing'],
         )
+
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
diff --git a/ForgeBlog/forgeblog/widgets.py b/ForgeBlog/forgeblog/widgets.py
index c0445369c..4cde920f9 100644
--- a/ForgeBlog/forgeblog/widgets.py
+++ b/ForgeBlog/forgeblog/widgets.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import tg
 import ew as ew_core
 import ew.jinja2_ew as ew
 
@@ -68,6 +69,10 @@ def resources(self):
             });
         ''')
 
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
+
 
 class NewPostForm(BlogPostForm):
 
diff --git a/ForgeDiscussion/forgediscussion/controllers/forum.py 
b/ForgeDiscussion/forgediscussion/controllers/forum.py
index b7f3e0761..a6d472aae 100644
--- a/ForgeDiscussion/forgediscussion/controllers/forum.py
+++ b/ForgeDiscussion/forgediscussion/controllers/forum.py
@@ -44,7 +44,7 @@
 
 
 class pass_validator:
-    def validate(self, v, s):
+    def validate(self, v, s=None):
         return v
 
 
diff --git a/ForgeDiscussion/forgediscussion/widgets/admin.py 
b/ForgeDiscussion/forgediscussion/widgets/admin.py
index eba27dcd7..990697d40 100644
--- a/ForgeDiscussion/forgediscussion/widgets/admin.py
+++ b/ForgeDiscussion/forgediscussion/widgets/admin.py
@@ -14,7 +14,7 @@
 #       KIND, either express or implied.  See the License for the
 #       specific language governing permissions and limitations
 #       under the License.
-
+import tg
 from formencode import validators as fev
 from formencode import All
 import formencode
diff --git a/ForgeTracker/forgetracker/widgets/bin_form.py 
b/ForgeTracker/forgetracker/widgets/bin_form.py
index 7040ff392..fa08f6402 100644
--- a/ForgeTracker/forgetracker/widgets/bin_form.py
+++ b/ForgeTracker/forgetracker/widgets/bin_form.py
@@ -15,6 +15,7 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
+import tg
 import ew
 from ew import jinja2_ew
 from allura.lib import validators as v
@@ -39,3 +40,7 @@ class fields(ew.NameList):
         terms = jinja2_ew.TextField(
             label='Search Terms',
             validator=v.UnicodeString(not_empty=True))
+
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
diff --git a/ForgeTracker/forgetracker/widgets/ticket_form.py 
b/ForgeTracker/forgetracker/widgets/ticket_form.py
index fc19fb94e..b53bfdafc 100644
--- a/ForgeTracker/forgetracker/widgets/ticket_form.py
+++ b/ForgeTracker/forgetracker/widgets/ticket_form.py
@@ -16,6 +16,7 @@
 #       under the License.
 
 
+import tg
 from tg import tmpl_context as c
 from formencode import validators as fev
 from markupsafe import Markup
@@ -140,6 +141,10 @@ def fields(self):
                     break
         return ew_core.NameList(fields)
 
+    def validate(self, value, state=None):
+        state = tg.request if hasattr(tg, 'request') else state
+        return super().validate(value, state)
+
 
 class TicketForm(GenericTicketForm):
     template = 'jinja:forgetracker:templates/tracker_widgets/ticket_form.html'

Reply via email to