forms.py 12.3 KB
Newer Older
1
2
"""
go/forms.py
3

David Haynes's avatar
David Haynes committed
4
5
Configure the layout and styling of the Go's forms.
"""
6
# Python stdlib Imports
David Haynes's avatar
David Haynes committed
7
from datetime import datetime, timedelta
8

9
# Django Imports
10
from django.core.exceptions import ValidationError
11
12
13
from django.forms import (BooleanField, CharField, ChoiceField, DateTimeField,
                          ModelForm, RadioSelect, SlugField, Textarea,
                          TextInput, URLField, URLInput)
Matthew Rodgers's avatar
Matthew Rodgers committed
14
from django.utils import timezone
David Haynes's avatar
David Haynes committed
15
from django.utils.safestring import mark_safe
16
17

# App Imports
18
from .models import URL, RegisteredUser
19
20

# Other Imports
David Haynes's avatar
David Haynes committed
21
22
23
from crispy_forms.bootstrap import (Accordion, AccordionGroup, PrependedText,
                                    StrictButton)
from crispy_forms.helper import FormHelper
David Haynes's avatar
David Haynes committed
24
from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout
David Haynes's avatar
David Haynes committed
25

26

27
class URLForm(ModelForm):
28
    """
David Haynes's avatar
David Haynes committed
29
    The form that is used in URL creation.
David Haynes's avatar
David Haynes committed
30

David Haynes's avatar
David Haynes committed
31
32
    Define custom fields and then render them onto the template.
    """
David Haynes's avatar
David Haynes committed
33
34
    # destination ------------------------------------------------------------------
    destination = URLField(
35
        required=True,
36
        label='Long URL (Required)',
37
        max_length=1000,
38
        widget=URLInput(attrs={
39
            'placeholder': 'https://yoursite.com/'
40
41
42
        })
    )

David Haynes's avatar
David Haynes committed
43
    # short -------------------------------------------------------------------
44
    def unique_short(value):
David Haynes's avatar
David Haynes committed
45
46
47
        """
        Check to make sure the short url has not been used
        """
48
        try:
David Haynes's avatar
David Haynes committed
49
            # if we're able to get a URL with the same short url
50
            URL.objects.get(short__iexact=value)
51
        except URL.DoesNotExist as ex:
David Haynes's avatar
David Haynes committed
52
            print(ex)
53
            return
David Haynes's avatar
David Haynes committed
54

David Haynes's avatar
David Haynes committed
55
        # then raise a ValidationError
56
57
        raise ValidationError('Short url already exists.')

David Haynes's avatar
David Haynes committed
58
    short = CharField(
59
60
        required=False,
        label='Short URL (Optional)',
61
        widget=TextInput(),
62
63
        validators=[unique_short],
        max_length=20,
64
        min_length=1,
65
66
    )

David Haynes's avatar
David Haynes committed
67
    # expires -----------------------------------------------------------------
68
69
70
    DAY = '1 Day'
    WEEK = '1 Week'
    MONTH = '1 Month'
71
    CUSTOM = 'Custom Date'
72
73
    NEVER = 'Never'

David Haynes's avatar
David Haynes committed
74
    # Define a tuple of string date standards to be used as our date choices
75
76
77
78
79
    EXPIRATION_CHOICES = (
        (DAY, DAY),
        (WEEK, WEEK),
        (MONTH, MONTH),
        (NEVER, NEVER),
80
        (CUSTOM, CUSTOM),
81
82
    )

83
    expires = ChoiceField(
84
85
86
87
        required=True,
        label='Expiration (Required)',
        choices=EXPIRATION_CHOICES,
        initial=NEVER,
88
        widget=RadioSelect(),
89
90
    )

Matthew Rodgers's avatar
Matthew Rodgers committed
91
    def valid_date(value):
David Haynes's avatar
David Haynes committed
92
93
94
        """
        Check if the selected date is a valid date
        """
David Haynes's avatar
David Haynes committed
95
        # a valid date is one that is greater than today
Matthew Rodgers's avatar
Matthew Rodgers committed
96
97
        if value > timezone.now():
            return
David Haynes's avatar
David Haynes committed
98
        # raise a ValidationError if the date is invalid
Matthew Rodgers's avatar
Matthew Rodgers committed
99
        else:
100
            raise ValidationError('Date must be after today.')
Matthew Rodgers's avatar
Matthew Rodgers committed
101

102
    expires_custom = DateTimeField(
103
104
105
106
        required=False,
        label='Custom Date',
        input_formats=['%m-%d-%Y'],
        validators=[valid_date],
David Haynes's avatar
David Haynes committed
107
        initial=lambda: datetime.now() + timedelta(days=1)
108
109
    )

110
    def __init__(self, *args, **kwargs):
David Haynes's avatar
David Haynes committed
111
        """
David Haynes's avatar
David Haynes committed
112
        On initialization of the form, crispy forms renders this layout.
David Haynes's avatar
David Haynes committed
113
        """
114
115
        # Grab that host info
        self.host = kwargs.pop('host', None)
116
        super(URLForm, self).__init__(*args, **kwargs)
117
        # Define the basics for crispy-forms
118
        self.helper = FormHelper()
119
        self.helper.form_method = 'POST'
120

David Haynes's avatar
David Haynes committed
121
        # Some extra vars for form css purposes
122
123
124
125
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-1'
        self.helper.field_class = 'col-md-6'

126
        # The main "layout" defined
127
        self.helper.layout = Layout(
128
            Fieldset('',
David Haynes's avatar
David Haynes committed
129
130
131
132
133
134
                     #######################
                     Accordion(
                         # Step 1: Long URL
                         AccordionGroup('Step 1: Long URL',
                                        Div(
                                            HTML("""
135
                                <h4>Paste the URL you would like to shorten:</h4>
136
                                <br />"""),
David Haynes's avatar
David Haynes committed
137
                                            'destination',
David Haynes's avatar
David Haynes committed
138
139
140
141
142
143
144
145
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # Step 2: Short URL
                         AccordionGroup('Step 2: Short URL',
                                        Div(
                                            HTML("""
146
                                <h4>Create a custom Go address:</h4>
147
                                <br />"""),
David Haynes's avatar
David Haynes committed
148
149
150
151
152
153
154
155
156
157
                                            PrependedText(
                                                'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html',),

                         # Step 3: Expiration
                         AccordionGroup('Step 3: URL Expiration',
                                        Div(
                                            HTML("""
158
                                <h4>Set when you would like your Go address to expire:</h4>
159
                                <br />"""),
David Haynes's avatar
David Haynes committed
160
161
162
163
164
165
166
167
168
169
                                            'expires',
                                            Field('expires_custom'),
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # FIN
                         template='crispy/accordian.html'),
                     #######################
                     HTML("""
170
                <br />"""),
David Haynes's avatar
David Haynes committed
171
                     StrictButton('Shorten', css_class="btn btn-primary btn-md col-md-4", type='submit')))
172

173
    class Meta:
David Haynes's avatar
David Haynes committed
174
175
176
        """
        Metadata about this ModelForm
        """
David Haynes's avatar
David Haynes committed
177
        # what model this form is for
178
        model = URL
David Haynes's avatar
David Haynes committed
179
        # what attributes are included
David Haynes's avatar
David Haynes committed
180
        fields = ['destination']
181

David Haynes's avatar
David Haynes committed
182

183
class EditForm(URLForm):
David Haynes's avatar
David Haynes committed
184
185
    """
    The form that is used in editing URLs.
186

David Haynes's avatar
David Haynes committed
187
188
189
    A modification of the URL creation form... now for editing URLs. Inherit
    custom form fields for DRY purposes.
    """
David Haynes's avatar
David Haynes committed
190

191
192
    def __init__(self, *args, **kwargs):
        """
David Haynes's avatar
David Haynes committed
193
        On initialization of the form, crispy forms renders this layout.
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
        """
        # Grab that host info
        self.host = kwargs.pop('host', None)
        super(URLForm, self).__init__(*args, **kwargs)
        # Define the basics for crispy-forms
        self.helper = FormHelper()
        self.helper.form_method = 'POST'

        # Some xtra vars for form css purposes
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-1'
        self.helper.field_class = 'col-md-6'

        # The main "layout" defined
        self.helper.layout = Layout(
            Fieldset('',
David Haynes's avatar
David Haynes committed
210
211
212
213
214
215
                     #######################
                     Accordion(
                         # Step 1: Long URL
                         AccordionGroup('Step 1: Long URL',
                                        Div(
                                            HTML("""
216
217
                                <h4>Modify the URL you would like to shorten:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
218
                                            'destination',
David Haynes's avatar
David Haynes committed
219
220
221
222
223
224
225
226
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # Step 2: Short URL
                         AccordionGroup('Step 2: Short URL',
                                        Div(
                                            HTML("""
227
228
                                <h4>Modify the Go address:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
229
230
231
232
233
234
235
236
237
238
                                            PrependedText(
                                                'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html',),

                         # Step 3: Expiration
                         AccordionGroup('Step 3: URL Expiration',
                                        Div(
                                            HTML("""
239
240
                                <h4>Modify the expiration date:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
241
242
243
244
245
246
247
248
249
250
251
                                            'expires',
                                            Field('expires_custom',
                                                  template="crispy/customDateField.html"),
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # FIN
                         template='crispy/accordian.html'),
                     #######################
                     HTML("""
252
                <br />"""),
David Haynes's avatar
David Haynes committed
253
                     StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit')))
David Haynes's avatar
David Haynes committed
254

255
    class Meta(URLForm.Meta):
David Haynes's avatar
David Haynes committed
256
257
258
        """
        Metadata about this ModelForm
        """
259
260
261
        # what attributes are included
        fields = URLForm.Meta.fields

David Haynes's avatar
David Haynes committed
262

263
class SignupForm(ModelForm):
264
    """
David Haynes's avatar
David Haynes committed
265
    The form that is used when a user is signing up to be a RegisteredUser
266
    """
267
    full_name = CharField(
268
269
270
        required=True,
        label='Full Name (Required)',
        max_length=100,
271
        widget=TextInput(),
David Haynes's avatar
David Haynes committed
272
        help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.",
273
    )
David Haynes's avatar
David Haynes committed
274

275
    organization = CharField(
276
277
278
        required=True,
        label='Organization (Required)',
        max_length=100,
279
        widget=TextInput(),
David Haynes's avatar
David Haynes committed
280
        help_text="Or whatever \"group\" you would associate with on campus.",
281
    )
David Haynes's avatar
David Haynes committed
282

283
    description = CharField(
284
285
286
        required=False,
        label='Description (Optional)',
        max_length=200,
287
        widget=Textarea(),
David Haynes's avatar
David Haynes committed
288
        help_text="Describe what type of links you would intend to create with Go.",
289
    )
David Haynes's avatar
David Haynes committed
290

291
    # A user becomes registered when they agree to the TOS
292
    registered = BooleanField(
293
        required=True,
David Haynes's avatar
David Haynes committed
294
295
        # ***Need to replace lower url with production URL***
        # ie. go.gmu.edu/about#terms
296
        label=mark_safe(
David Haynes's avatar
David Haynes committed
297
            'Do you accept the <a href="about">Terms of Service</a>?'
298
        ),
David Haynes's avatar
David Haynes committed
299
        help_text="Esssentially the GMU Responsible Use of Computing policies.",
300
301
    )

302
    def __init__(self, request, *args, **kwargs):
David Haynes's avatar
David Haynes committed
303
        """
David Haynes's avatar
David Haynes committed
304
        On initialization of the form, crispy forms renders this layout.
David Haynes's avatar
David Haynes committed
305
306
307
        """
        # Necessary to call request in forms.py, is otherwise restricted to
        # views.py and models.py
308
309
        self.request = request
        super(SignupForm, self).__init__(*args, **kwargs)
David Haynes's avatar
David Haynes committed
310
        self.helper = FormHelper()
311
312
313
314
315
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-4'
        self.helper.field_class = 'col-md-6'

        self.helper.layout = Layout(
316
            Fieldset('',
David Haynes's avatar
David Haynes committed
317
318
319
320
321
322
323
324
325
326
327
328
329
                     Div(
                         # Place in form fields
                         Div(
                             'full_name',
                             'organization',
                             'description',
                             'registered',
                             css_class='well'),

                         # Extras at bottom
                         StrictButton(
                             'Submit', css_class='btn btn-primary btn-md col-md-4', type='submit'),
                         css_class='col-md-6')))
David Haynes's avatar
David Haynes committed
330

331
    class Meta:
David Haynes's avatar
David Haynes committed
332
333
334
        """
        Metadata about this ModelForm
        """
David Haynes's avatar
David Haynes committed
335
        # what model this form is for
336
        model = RegisteredUser
David Haynes's avatar
David Haynes committed
337
        # what attributes are included
338
        fields = ['full_name', 'organization', 'description', 'registered']