Repository: django-wiki/django-wiki

Issue:
Exception Value: __init__() got an unexpected keyword argument 'disabled' error in version 0.6
After upgrading to v0.6, I get the following error when trying to access the settings of a wiki page (see below).

Is there a quick fix for it?

Thanks a lot in advance.

```
Internal Server Error: /wiki/intranet-introduction/_settings/

TypeError at /wiki/intranet-introduction/_settings/
__init__() got an unexpected keyword argument 'disabled'

Request Method: GET
Request URL: .../wiki/intranet-introduction/_settings/
Django Version: 2.2.13
Python Executable: /home/nzilio/uwsgi/bin/uwsgi Python Version: 3.6.8 Python Path: ['.', '', '/home/lib/python36.zip', '/home/lib/python3.6', '/home/lib/python3.6/lib-dynload', '/home/nzilio/software/Python-3.6.8/lib/python3.6', '/home/lib/python3.6/site-packages', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf', '/home/lib/python3.6/site-packages/odf']
Server time: Mon, 15 Jun 2020 15:18:07 +0200 Installed Applications:
['adminactions',
 'django.forms',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'djangoql',
 'simple_history',
 'import_export',
 'collection_management',
 'colorful',
 'user_management',
 'order_management',
 'guardian',
 'django.contrib.sites.apps.SitesConfig',
 'django.contrib.humanize.apps.HumanizeConfig',
 'django_nyt.apps.DjangoNytConfig',
 'mptt',
 'sekizai',
 'sorl.thumbnail',
 'wiki.apps.WikiConfig',
 'wiki.plugins.attachments.apps.AttachmentsConfig',
 'wiki.plugins.notifications.apps.NotificationsConfig',
 'wiki.plugins.images.apps.ImagesConfig',
 'wiki.plugins.macros.apps.MacrosConfig',
 'background_task',
 'formz',
 'record_approval',
 'my_admin']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'simple_history.middleware.HistoryRequestMiddleware']


Traceback:

File "/home/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/home/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/home/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "./django_project/urls.py" in decorator
  75.                 return f(request, **kwargs)

File "/home/lib/python3.6/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/home/lib/python3.6/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/lib/python3.6/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  21.                 return view_func(request, *args, **kwargs)

File "/home/lib/python3.6/site-packages/django/utils/decorators.py" in _wrapper
  45.         return bound_method(*args, **kwargs)

File "/home/lib/python3.6/site-packages/wiki/decorators.py" in wrapper
  164.         return func(request, article, *args, **kwargs)

File "/home/lib/python3.6/site-packages/wiki/views/article.py" in dispatch
  733.         return super().dispatch(request, article, *args, **kwargs)

File "/home/lib/python3.6/site-packages/wiki/views/mixins.py" in dispatch
  34.         return super().dispatch(request, *args, **kwargs)

File "/home/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/home/lib/python3.6/site-packages/wiki/views/article.py" in get
  779.             self.forms.append(Form(new_article, self.request))

File "/home/lib/python3.6/site-packages/wiki/forms.py" in __init__
  506.                 if settings.USE_BOOTSTRAP_SELECT_WIDGET

Exception Type: TypeError at /wiki/intranet-introduction/_settings/
Exception Value: __init__() got an unexpected keyword argument 'disabled'
```


Discussion:
Hi @nicolazilio -- there is something odd about the traceback. Can you reproduce this consistently after upgrading?

I cannot preproduce the issue on the demo website.
Yes, I have multiple copies of my Django app and they all give this error. Interestingly, it does not affect the 'root' article, for that I can access the Settings no problem, the issue seems to affect only sub-articles.
I'm getting the same less-than-helpful traceback with v0.6. My superuser account is unaffected but group-permitted users are experiencing the issue.

Interestingly, I can't reproduce the issue in my development version of my application. It only occurs in production. I'll continue investigating.
Hi @bverem! Did you see this occur again and trace a pattern?
Same error with Django 3.1.7 and Wiki 0.7.3. I got it with a simple unprivileged test user.

```
Internal Server Error: /created-user/_settings/
Traceback (most recent call last):
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/wiki/decorators.py", line 168, in wrapper
    return func(request, article, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/wiki/views/article.py", line 736, in dispatch
    return super().dispatch(request, article, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/wiki/views/mixins.py", line 34, in dispatch
    return super().dispatch(request, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/wiki/views/article.py", line 782, in get
    self.forms.append(Form(new_article, self.request))
  File "/data/projects/python/wiki/env/lib64/python3.6/site-packages/wiki/forms.py", line 512, in __init__
    if settings.USE_BOOTSTRAP_SELECT_WIDGET
TypeError: __init__() got an unexpected keyword argument 'disabled'
```

Resolving patch:
diff --git a/src/wiki/forms.py b/src/wiki/forms.py
--- a/src/wiki/forms.py
+++ b/src/wiki/forms.py
@@ -508,7 +508,7 @@ def __init__(self, article, request, *args, **kwargs):
                 else Group.objects.none(),
                 empty_label=_("(none)"),
                 required=False,
-                widget=SelectWidgetBootstrap(disabled=True)
+                widget=SelectWidgetBootstrap(attrs={"disabled": True})
                 if settings.USE_BOOTSTRAP_SELECT_WIDGET
                 else forms.Select(attrs={"disabled": True}),
             )


Test patch:
diff --git a/tests/base.py b/tests/base.py
--- a/tests/base.py
+++ b/tests/base.py
@@ -13,6 +13,9 @@
 SUPERUSER1_USERNAME = "admin"
 SUPERUSER1_PASSWORD = "secret"
 
+NORMALUSER1_USERNAME = "normaluser"
+NORMALUSER1_PASSWORD = "secret"
+
 
 class RequireSuperuserMixin:
     def setUp(self):
@@ -32,7 +35,16 @@ class RequireBasicData(RequireSuperuserMixin):
     Mixin that creates common data required for all tests.
     """
 
-    pass
+    def setUp(self):
+        super().setUp()
+
+        from django.contrib.auth import get_user_model
+
+        User = get_user_model()
+
+        self.normaluser1 = User.objects.create_user(
+            NORMALUSER1_USERNAME, "nobody@example.com", NORMALUSER1_PASSWORD
+        )
 
 
 class TestBase(RequireBasicData, TestCase):
diff --git a/tests/core/test_views.py b/tests/core/test_views.py
--- a/tests/core/test_views.py
+++ b/tests/core/test_views.py
@@ -16,6 +16,8 @@
 
 from ..base import ArticleWebTestUtils
 from ..base import DjangoClientTestBase
+from ..base import NORMALUSER1_PASSWORD
+from ..base import NORMALUSER1_USERNAME
 from ..base import RequireRootArticleMixin
 from ..base import SeleniumBase
 from ..base import SUPERUSER1_USERNAME
@@ -129,6 +131,24 @@ def test_article_list_update(self):
         )
         self.assertNotContains(self.get_by_path(""), "Sub Article 1")
 
+    def test_anonymous_root(self):
+        self.client.logout()
+        response = self.client.get(
+            reverse("wiki:get", kwargs={"article_id": self.root_article.pk})
+        )
+        self.assertEqual(response.status_code, 200)
+        response = self.client.get(reverse("wiki:get", kwargs={"path": ""}))
+        self.assertEqual(response.status_code, 200)
+
+    def test_normaluser_root(self):
+        self.client.login(username=NORMALUSER1_USERNAME, password=NORMALUSER1_PASSWORD)
+        response = self.client.get(
+            reverse("wiki:get", kwargs={"article_id": self.root_article.pk})
+        )
+        self.assertEqual(response.status_code, 200)
+        response = self.client.get(reverse("wiki:get", kwargs={"path": ""}))
+        self.assertEqual(response.status_code, 200)
+
 
 class CreateViewTest(
     RequireRootArticleMixin, ArticleWebTestUtils, DjangoClientTestBase
@@ -771,6 +791,19 @@ def test_auth_user(self):
         )
         self.assertEqual(response.status_code, 200)
 
+    def test_normal_user(self):
+        """
+        Tests that the settings view page renders for a normal user
+        Regression test: https://github.com/django-wiki/django-wiki/issues/1058
+        """
+        response = self.client.post(
+            resolve_url("wiki:create", path=""),
+            {"title": "Level 1", "slug": "Level1", "content": "Content level 1"},
+        )
+        self.client.login(username=NORMALUSER1_USERNAME, password=NORMALUSER1_PASSWORD)
+        response = self.client.get(reverse("wiki:settings", kwargs={"path": "level1/"}))
+        self.assertEqual(response.status_code, 200)
+
     def test_content(self):
         response = self.client.get(
             reverse("wiki:settings", kwargs={"article_id": self.root_article.pk})
diff --git a/tests/testdata/models.py b/tests/testdata/models.py
--- a/tests/testdata/models.py
+++ b/tests/testdata/models.py
@@ -6,6 +6,18 @@
 class CustomUser(AbstractUser):
     some_field = models.IntegerField(default=0)
 
+    custom_groups = models.ManyToManyField(
+        "CustomGroup",
+        verbose_name="groups",
+        blank=True,
+        help_text=(
+            "The groups this user belongs to. A user will get all permissions "
+            "granted to each of their groups."
+        ),
+        related_name="user_set",
+        related_query_name="user",
+    )
+
 
 class CustomGroup(models.Model):
     pass