Просмотр исходного кода

Fix URL character validation in dashboard PageUpdateForm.

Also fix the max_length on the url field. The underlying model field has
a max_length of 100, so it is invalid to have a larger value here.

DRY out dashboard page update/create views.

Refactor tests - perform tests of form logic directly rather than indirectly through view code.

Fixes #2559.
master
Samir Shah 8 лет назад
Родитель
Сommit
89ff0b3da4

+ 3
- 0
docs/source/releases/v1.6.rst Просмотреть файл

47
    initiation.
47
    initiation.
48
  - Added ``get_stock_info`` hook to ``oscar.apps.basket.models.Basket``  for
48
  - Added ``get_stock_info`` hook to ``oscar.apps.basket.models.Basket``  for
49
    implementing strategies that depend on product options.
49
    implementing strategies that depend on product options.
50
+ - Fixed the page create/update views in the dashboard to correctly validate
51
+   URLs. Invalid characters in URLs will raise a validation error, as will
52
+   URLs longer than 100 characters.
50
 
53
 
51
 .. _incompatible_in_1.6:
54
 .. _incompatible_in_1.6:
52
 
55
 

+ 13
- 2
src/oscar/apps/dashboard/pages/forms.py Просмотреть файл

22
     and *content* field. The specified URL will be validated and check if
22
     and *content* field. The specified URL will be validated and check if
23
     the same URL already exists in the system.
23
     the same URL already exists in the system.
24
     """
24
     """
25
-    url = forms.CharField(max_length=128, required=False, label=_("URL"),
26
-                          help_text=_("Example: '/about/contact/'."))
25
+    url = forms.RegexField(
26
+        label=_("URL"),
27
+        max_length=100,
28
+        regex=r'^[-\w/\.~]+$',
29
+        required=False,
30
+        help_text=_("Example: '/about/contact/'."),
31
+        error_messages={
32
+            "invalid": _(
33
+                "This value must contain only letters, numbers, dots, "
34
+                "underscores, dashes, slashes or tildes."
35
+            ),
36
+        },
37
+    )
27
 
38
 
28
     def clean_url(self):
39
     def clean_url(self):
29
         """
40
         """

+ 23
- 35
src/oscar/apps/dashboard/pages/views.py Просмотреть файл

63
         return context
63
         return context
64
 
64
 
65
 
65
 
66
-class PageCreateView(generic.CreateView):
66
+class PageCreateUpdateMixin(object):
67
+
67
     template_name = 'dashboard/pages/update.html'
68
     template_name = 'dashboard/pages/update.html'
68
     model = FlatPage
69
     model = FlatPage
69
     form_class = forms.PageUpdateForm
70
     form_class = forms.PageUpdateForm
70
     context_object_name = 'page'
71
     context_object_name = 'page'
71
 
72
 
73
+    def get_success_url(self):
74
+        msg = render_to_string('oscar/dashboard/pages/messages/saved.html',
75
+                               {'page': self.object})
76
+        messages.success(self.request, msg, extra_tags='safe noicon')
77
+        return reverse('dashboard:page-list')
78
+
79
+    def form_valid(self, form):
80
+        # Ensure saved page is added to the current site
81
+        page = form.save()
82
+        if not page.sites.exists():
83
+            page.sites.add(Site.objects.get_current())
84
+        self.object = page
85
+        return HttpResponseRedirect(self.get_success_url())
86
+
87
+
88
+class PageCreateView(PageCreateUpdateMixin, generic.CreateView):
89
+
72
     def get_context_data(self, **kwargs):
90
     def get_context_data(self, **kwargs):
73
         ctx = super(PageCreateView, self).get_context_data(**kwargs)
91
         ctx = super(PageCreateView, self).get_context_data(**kwargs)
74
         ctx['title'] = _('Create New Page')
92
         ctx['title'] = _('Create New Page')
76
 
94
 
77
     def form_valid(self, form):
95
     def form_valid(self, form):
78
         """
96
         """
79
-        Store new flatpage from form data. Checks wether a site
80
-        is specified for the flatpage or sets the current site by
81
-        default. Additionally, if URL is left blank, a slugified
97
+        Store new flatpage from form data.
98
+        Additionally, if URL is left blank, a slugified
82
         version of the title will be used as URL after checking
99
         version of the title will be used as URL after checking
83
         if it is valid.
100
         if it is valid.
84
         """
101
         """
93
         except ValidationError:
110
         except ValidationError:
94
             pass
111
             pass
95
         else:
112
         else:
96
-            # use current site as default for new page
97
-            page.save()
98
-            page.sites.add(Site.objects.get_current())
99
-
100
-            return HttpResponseRedirect(self.get_success_url(page))
113
+            return super(PageCreateView, self).form_valid(form)
101
 
114
 
102
         ctx = self.get_context_data()
115
         ctx = self.get_context_data()
103
         ctx['form'] = form
116
         ctx['form'] = form
104
         return self.render_to_response(ctx)
117
         return self.render_to_response(ctx)
105
 
118
 
106
-    def get_success_url(self, page):
107
-        msg = render_to_string('oscar/dashboard/pages/messages/saved.html',
108
-                               {'page': page})
109
-        messages.success(self.request, msg, extra_tags='safe noicon')
110
-        return reverse('dashboard:page-list')
111
 
119
 
112
-
113
-class PageUpdateView(generic.UpdateView):
120
+class PageUpdateView(PageCreateUpdateMixin, generic.UpdateView):
114
     """
121
     """
115
     View for updating flatpages from the dashboard.
122
     View for updating flatpages from the dashboard.
116
     """
123
     """
117
-    template_name = 'dashboard/pages/update.html'
118
-    model = FlatPage
119
-    form_class = forms.PageUpdateForm
120
-    context_object_name = 'page'
121
-
122
     def get_context_data(self, **kwargs):
124
     def get_context_data(self, **kwargs):
123
         ctx = super(PageUpdateView, self).get_context_data(**kwargs)
125
         ctx = super(PageUpdateView, self).get_context_data(**kwargs)
124
         ctx['title'] = self.object.title
126
         ctx['title'] = self.object.title
125
         return ctx
127
         return ctx
126
 
128
 
127
-    def form_valid(self, form):
128
-        # Ensure saved page is added to the current site
129
-        page = form.save(commit=False)
130
-        if not page.sites.exists():
131
-            page.sites.add(Site.objects.get_current())
132
-        page.save()
133
-        return HttpResponseRedirect(self.get_success_url())
134
-
135
-    def get_success_url(self):
136
-        msg = render_to_string('oscar/dashboard/pages/messages/saved.html',
137
-                               {'page': self.object})
138
-        messages.success(self.request, msg, extra_tags='safe noicon')
139
-        return reverse('dashboard:page-list')
140
-
141
 
129
 
142
 class PageDeleteView(generic.DeleteView):
130
 class PageDeleteView(generic.DeleteView):
143
     template_name = 'dashboard/pages/delete.html'
131
     template_name = 'dashboard/pages/delete.html'

+ 109
- 55
tests/functional/dashboard/test_page.py Просмотреть файл

1
 from django.core.urlresolvers import reverse
1
 from django.core.urlresolvers import reverse
2
 from django.contrib.flatpages.models import FlatPage
2
 from django.contrib.flatpages.models import FlatPage
3
+from django.test import TestCase
4
+
5
+from oscar.apps.dashboard.pages.forms import PageUpdateForm
3
 from oscar.test.testcases import WebTestCase
6
 from oscar.test.testcases import WebTestCase
4
 
7
 
5
 
8
 
28
         self.assertTrue(self.flatpage_1 in objects)
31
         self.assertTrue(self.flatpage_1 in objects)
29
         self.assertTrue(self.flatpage_2 in objects)
32
         self.assertTrue(self.flatpage_2 in objects)
30
 
33
 
31
-    def test_doesnt_allow_existing_pages_to_be_clobbered(self):
32
-        self.assertEqual(FlatPage.objects.count(), 2)
33
-
34
-        page = self.get(reverse('dashboard:page-create'))
35
-        form = page.form
36
-        form['title'] = 'test'
37
-        form['url'] = '/dashboard/pages/'
38
-        response = form.submit()
39
-        self.assertEqual(200, response.status_code)
40
-        self.assertEqual("Specified page already exists!",
41
-                         response.context['form'].errors['url'][0])
42
-        self.assertEqual(FlatPage.objects.count(), 2)
43
-
44
-    def test_allows_page_to_be_created(self):
45
-        page = self.get(reverse('dashboard:page-create'))
46
-        form = page.form
47
-        form['title'] = 'test'
48
-        form['url'] = '/my-new-url/'
49
-        form['content'] = 'my content here'
50
-        response = form.submit()
34
+    def test_dashboard_delete_pages(self):
35
+        page = self.get(reverse('dashboard:page-list'))
36
+        delete_page = page.click(linkid="delete_page_%s" % self.flatpage_1.id)
37
+        response = delete_page.form.submit()
51
 
38
 
52
         self.assertIsRedirect(response)
39
         self.assertIsRedirect(response)
53
-        self.assertEqual(FlatPage.objects.count(), 3)
40
+        self.assertEqual(FlatPage.objects.count(), 1)
54
 
41
 
55
     def test_dashboard_create_page_with_slugified_url(self):
42
     def test_dashboard_create_page_with_slugified_url(self):
56
         page = self.get(reverse('dashboard:page-create'))
43
         page = self.get(reverse('dashboard:page-create'))
60
         response = form.submit()
47
         response = form.submit()
61
 
48
 
62
         self.assertIsRedirect(response)
49
         self.assertIsRedirect(response)
63
-        self.assertEqual(FlatPage.objects.count(), 3)
64
 
50
 
65
-    def test_dashboard_create_page_with_exisiting_url_does_not_work(self):
51
+    def test_dashboard_create_page_with_duplicate_slugified_url_fails(self):
66
         page = self.get(reverse('dashboard:page-create'))
52
         page = self.get(reverse('dashboard:page-create'))
67
         form = page.form
53
         form = page.form
68
-        form['title'] = 'test'
69
-        form['url'] = '/url1/'  # already exists
54
+        form['title'] = 'url1'  # This will slugify to url1
70
         form['content'] = 'my content here'
55
         form['content'] = 'my content here'
71
         response = form.submit()
56
         response = form.submit()
72
 
57
 
73
         self.assertEqual(200, response.status_code)
58
         self.assertEqual(200, response.status_code)
74
-        self.assertEqual("Specified page already exists!",
75
-                         response.context['form'].errors['url'][0])
76
-        self.assertEqual(FlatPage.objects.count(), 2)
77
 
59
 
78
-    def test_dashboard_update_page_valid_url(self):
79
-        page = self.get(reverse('dashboard:page-update',
80
-                                kwargs={'pk': self.flatpage_1.pk}))
60
+    def test_default_site_added_for_new_pages(self):
61
+        page = self.get(reverse('dashboard:page-create'))
81
         form = page.form
62
         form = page.form
82
         form['title'] = 'test'
63
         form['title'] = 'test'
83
-        form['url'] = '/new/url/'  # already exists
84
-        form['content'] = 'my content here'
85
-        response = form.submit()
64
+        form['url'] = '/hello-world/'
65
+        form.submit()
86
 
66
 
87
-        self.assertIsRedirect(response)
67
+        p = FlatPage.objects.get(url='/hello-world/')
68
+        self.assertEqual(p.sites.count(), 1)
88
 
69
 
89
-        page = FlatPage.objects.get(pk=self.flatpage_1.pk)
90
-        self.assertEqual(page.title, 'test')
91
-        self.assertEqual(page.url, '/new/url/')
92
-        self.assertEqual(page.content, "my content here")
93
-        self.assertEqual(page.sites.count(), 1)
94
 
70
 
95
-    def test_dashboard_update_page_invalid_url(self):
96
-        page = self.get(reverse('dashboard:page-update',
97
-                                kwargs={'pk': self.flatpage_1.pk}))
98
-        form = page.form
99
-        form['url'] = '/url2/'  # already exists
100
-        response = form.submit()
71
+class DashboardPageUpdateFormTestCase(TestCase):
101
 
72
 
102
-        self.assertEqual(200, response.status_code)
103
-        self.assertEqual("Specified page already exists!",
104
-                         response.context['form'].errors['url'][0])
73
+    def setUp(self):
74
+        self.flatpage_1 = FlatPage.objects.create(
75
+            title='title1', url='/url1/',
76
+            content='some content')
77
+        self.flatpage_2 = FlatPage.objects.create(
78
+            title='title2', url='/url2/',
79
+            content='other content')
105
 
80
 
106
-    def test_dashboard_delete_pages(self):
107
-        page = self.get(reverse('dashboard:page-list'))
108
-        delete_page = page.click(linkid="delete_page_%s" % self.flatpage_1.id)
109
-        response = delete_page.form.submit()
81
+    def test_doesnt_allow_existing_pages_to_be_clobbered(self):
82
+        form = PageUpdateForm(data={
83
+            'title': 'test',
84
+            'url': '/dashboard/pages/',
85
+        })
86
+        self.assertFalse(form.is_valid())
87
+        self.assertEqual(
88
+            form.errors['url'],
89
+            ['Specified page already exists!']
90
+        )
110
 
91
 
111
-        self.assertIsRedirect(response)
112
-        self.assertEqual(FlatPage.objects.count(), 1)
92
+    def test_allows_page_to_be_created(self):
93
+        form = PageUpdateForm(data={
94
+            'title': 'test',
95
+            'url': '/my-new-url/',
96
+            'content': 'my content here'
97
+        })
98
+
99
+        self.assertTrue(form.is_valid())
100
+        form.save()
101
+        self.assertEqual(FlatPage.objects.count(), 3)
102
+
103
+    def test_create_page_with_slugified_url(self):
104
+        form = PageUpdateForm(data={
105
+            'title': 'test',
106
+            'content': 'my content here'
107
+        })
108
+
109
+        self.assertTrue(form.is_valid())
110
+        form.save()
111
+        self.assertEqual(FlatPage.objects.count(), 3)
112
+
113
+    def test_create_page_with_existing_url_does_not_work(self):
114
+        form = PageUpdateForm(data={
115
+            'title': 'test',
116
+            'url': '/url1/',  # already exists
117
+            'content': 'my content here'
118
+        })
119
+
120
+        self.assertFalse(form.is_valid())
121
+        self.assertEqual(
122
+            form.errors['url'],
123
+            ['Specified page already exists!']
124
+        )
125
+
126
+    def test_update_page_valid_url(self):
127
+        form = PageUpdateForm(instance=self.flatpage_1, data={
128
+            'title': 'test',
129
+            'url': '/new/url/',
130
+            'content': 'my content here'
131
+        })
132
+
133
+        form.save()
134
+
135
+        self.flatpage_1.refresh_from_db()
136
+        page = self.flatpage_1
137
+        self.assertEqual(page.title, 'test')
138
+        self.assertEqual(page.url, '/new/url/')
139
+        self.assertEqual(page.content, "my content here")
140
+
141
+    def test_invalid_chars_in_url(self):
142
+        form = PageUpdateForm(data={
143
+            'url': '/%* /',
144
+            'title': 'Title',
145
+            'content': 'Content',
146
+        })
147
+
148
+        self.assertFalse(form.is_valid())
149
+        self.assertEqual(
150
+            form.errors['url'],
151
+            ['This value must contain only letters, numbers, dots, underscores, dashes, slashes or tildes.']
152
+        )
153
+
154
+    def test_invalid_url_length(self):
155
+        form = PageUpdateForm(data={
156
+            'url': '/this_url_is_more_than_100_characters_long_which_is_invalid'
157
+                   '_because_the_model_field_has_a_max_length_of_100',
158
+            'title': 'Title',
159
+            'content': 'Content',
160
+        })
161
+
162
+        self.assertFalse(form.is_valid())
163
+        self.assertEqual(
164
+            form.errors['url'],
165
+            ['Ensure this value has at most 100 characters (it has 107).']
166
+        )

Загрузка…
Отмена
Сохранить