Repository: django-wiki/django-wiki

Issue:
Slug validation should deny numbers-only
@teury wrote:

> if you put a single number in the slug creates an error

...meaning that the form accepts '1' as a slug, but the url pattern doesn't match. The point is that a number is interpreted as an Article ID and not a slug path, so any pure number shouldn't be accepted as a slug.



Discussion:
Fix error numerical URLs.

Django-wiki/Wiki/URLs.py

Def get_article_urls(self):
``urlpatterns =[
.       url('^P<article_id>\d+)/$',
.            self.article_view_class.as_view(),
.           name`='get'),`

 ***\* Change 

``    Def get_article_urls(self):
   urlpatterns =[

> `url('^P<article_id>\d+)/get$',`

\d+)/"He can be whatsoever Digits
"


Resolving patch:
diff --git a/wiki/forms.py b/wiki/forms.py
--- a/wiki/forms.py
+++ b/wiki/forms.py
@@ -9,7 +9,9 @@
 from django import forms
 from django.apps import apps
 from django.contrib.auth.forms import UserCreationForm
+from django.core import validators
 from django.core.urlresolvers import Resolver404, resolve
+from django.core.validators import RegexValidator
 from django.forms.widgets import HiddenInput
 from django.utils import timezone
 from django.utils.html import conditional_escape, escape
@@ -32,6 +34,32 @@ def force_unicode(x):
         return(x)
 
 
+validate_slug_numbers = RegexValidator(
+    r'^\d+$',
+    _("A 'slug' cannot consist solely of numbers."),
+    'invalid',
+    inverse_match=True
+)
+
+
+class WikiSlugField(forms.SlugField):
+    """
+    In future versions of Django, we might be able to define this field as
+    the default field directly on the model. For now, it's used in CreateForm.
+    """
+
+    default_validators = [validators.validate_slug, validate_slug_numbers]
+
+    def __init__(self, *args, **kwargs):
+        self.allow_unicode = kwargs.pop('allow_unicode', False)
+        if self.allow_unicode:
+            self.default_validators = [
+                validators.validate_unicode_slug,
+                validate_slug_numbers
+            ]
+        super(forms.SlugField, self).__init__(*args, **kwargs)
+
+
 User = get_user_model()
 Group = apps.get_model(settings.GROUP_MODEL)
 
@@ -313,7 +341,7 @@ def __init__(self, request, urlpath_parent, *args, **kwargs):
         self.urlpath_parent = urlpath_parent
 
     title = forms.CharField(label=_('Title'),)
-    slug = forms.SlugField(
+    slug = WikiSlugField(
         label=_('Slug'),
         help_text=_(
             "This will be the address where your article can be found. Use only alphanumeric characters and - or _. Note that you cannot change the slug after creating the article."),


Test patch:
diff --git a/wiki/tests/test_views.py b/wiki/tests/test_views.py
--- a/wiki/tests/test_views.py
+++ b/wiki/tests/test_views.py
@@ -3,7 +3,9 @@
 import pprint
 
 from django.contrib.auth import authenticate
+from django.utils.html import escape
 from wiki import models
+from wiki.forms import validate_slug_numbers
 from wiki.models import reverse
 
 from .base import ArticleWebTestBase, WebTestBase
@@ -154,6 +156,21 @@ def test_create_nested_article_in_article(self):
             'Content'
         )  # on level 2')
 
+    def test_illegal_slug(self):
+
+        c = self.c
+
+        # A slug cannot be '123' because it gets confused with an article ID.
+        response = c.post(
+            reverse('wiki:create', kwargs={'path': ''}),
+            {'title': 'Illegal slug', 'slug': '123', 'content': 'blah'}
+        )
+        self.assertContains(
+            response,
+            escape(validate_slug_numbers.message)
+        )
+
+
 
 class DeleteViewTest(ArticleWebTestBase):