forms.py 11.8 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
from django.forms import (BooleanField, CharField, ChoiceField, DateTimeField,
David Haynes's avatar
David Haynes committed
12 13
                          ModelForm, RadioSelect, 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 -------------------------------------------------------------------
David Haynes's avatar
David Haynes committed
44
    short = CharField(
45 46
        required=False,
        label='Short URL (Optional)',
47
        widget=TextInput(),
48
        max_length=20,
49
        min_length=1,
50 51
    )

David Haynes's avatar
David Haynes committed
52
    # expires -----------------------------------------------------------------
53 54 55
    DAY = '1 Day'
    WEEK = '1 Week'
    MONTH = '1 Month'
56
    CUSTOM = 'Custom Date'
57 58
    NEVER = 'Never'

David Haynes's avatar
David Haynes committed
59
    # Define a tuple of string date standards to be used as our date choices
60 61 62 63 64
    EXPIRATION_CHOICES = (
        (DAY, DAY),
        (WEEK, WEEK),
        (MONTH, MONTH),
        (NEVER, NEVER),
65
        (CUSTOM, CUSTOM),
66 67
    )

68
    expires = ChoiceField(
69 70 71 72
        required=True,
        label='Expiration (Required)',
        choices=EXPIRATION_CHOICES,
        initial=NEVER,
73
        widget=RadioSelect(),
74 75
    )

Matthew Rodgers's avatar
Matthew Rodgers committed
76
    def valid_date(value):
David Haynes's avatar
David Haynes committed
77 78 79
        """
        Check if the selected date is a valid date
        """
David Haynes's avatar
David Haynes committed
80
        # a valid date is one that is greater than today
Matthew Rodgers's avatar
Matthew Rodgers committed
81 82
        if value > timezone.now():
            return
David Haynes's avatar
David Haynes committed
83
        # raise a ValidationError if the date is invalid
Matthew Rodgers's avatar
Matthew Rodgers committed
84
        else:
85
            raise ValidationError('Date must be after today.')
Matthew Rodgers's avatar
Matthew Rodgers committed
86

87
    expires_custom = DateTimeField(
88 89 90 91
        required=False,
        label='Custom Date',
        input_formats=['%m-%d-%Y'],
        validators=[valid_date],
David Haynes's avatar
David Haynes committed
92
        initial=lambda: datetime.now() + timedelta(days=1)
93 94
    )

95
    def __init__(self, *args, **kwargs):
David Haynes's avatar
David Haynes committed
96
        """
David Haynes's avatar
David Haynes committed
97
        On initialization of the form, crispy forms renders this layout.
David Haynes's avatar
David Haynes committed
98
        """
99 100
        # Grab that host info
        self.host = kwargs.pop('host', None)
101
        super(URLForm, self).__init__(*args, **kwargs)
102
        # Define the basics for crispy-forms
103
        self.helper = FormHelper()
104
        self.helper.form_method = 'POST'
105

David Haynes's avatar
David Haynes committed
106
        # Some extra vars for form css purposes
107 108 109 110
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-1'
        self.helper.field_class = 'col-md-6'

111
        # The main "layout" defined
112
        self.helper.layout = Layout(
113
            Fieldset('',
David Haynes's avatar
David Haynes committed
114 115 116 117 118 119
                     #######################
                     Accordion(
                         # Step 1: Long URL
                         AccordionGroup('Step 1: Long URL',
                                        Div(
                                            HTML("""
120
                                <h4>Paste the URL you would like to shorten:</h4>
121
                                <br />"""),
David Haynes's avatar
David Haynes committed
122
                                            'destination',
David Haynes's avatar
David Haynes committed
123 124 125 126 127 128 129 130
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # Step 2: Short URL
                         AccordionGroup('Step 2: Short URL',
                                        Div(
                                            HTML("""
131
                                <h4>Create a custom Go address:</h4>
132
                                <br />"""),
David Haynes's avatar
David Haynes committed
133 134 135 136 137 138 139 140 141 142
                                            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("""
143
                                <h4>Set when you would like your Go address to expire:</h4>
144
                                <br />"""),
David Haynes's avatar
David Haynes committed
145 146 147 148 149 150 151 152 153 154
                                            'expires',
                                            Field('expires_custom'),
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

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

158
    class Meta:
David Haynes's avatar
David Haynes committed
159 160 161
        """
        Metadata about this ModelForm
        """
David Haynes's avatar
David Haynes committed
162
        # what model this form is for
163
        model = URL
David Haynes's avatar
David Haynes committed
164
        # what attributes are included
David Haynes's avatar
David Haynes committed
165
        fields = ['destination']
166

David Haynes's avatar
David Haynes committed
167

168
class EditForm(URLForm):
David Haynes's avatar
David Haynes committed
169 170
    """
    The form that is used in editing URLs.
171

David Haynes's avatar
David Haynes committed
172 173 174
    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
175

176 177
    def __init__(self, *args, **kwargs):
        """
David Haynes's avatar
David Haynes committed
178
        On initialization of the form, crispy forms renders this layout.
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
        """
        # 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
195 196 197 198 199 200
                     #######################
                     Accordion(
                         # Step 1: Long URL
                         AccordionGroup('Step 1: Long URL',
                                        Div(
                                            HTML("""
201 202
                                <h4>Modify the URL you would like to shorten:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
203
                                            'destination',
David Haynes's avatar
David Haynes committed
204 205 206 207 208 209 210 211
                                            style="background: rgb(#F6F6F6);"),
                                        active=True,
                                        template='crispy/accordian-group.html'),

                         # Step 2: Short URL
                         AccordionGroup('Step 2: Short URL',
                                        Div(
                                            HTML("""
212 213
                                <h4>Modify the Go address:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
214 215 216 217 218 219 220 221 222 223
                                            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("""
224 225
                                <h4>Modify the expiration date:</h4>
                                <br />"""),
David Haynes's avatar
David Haynes committed
226 227 228 229 230 231 232 233 234 235 236
                                            '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("""
237
                <br />"""),
David Haynes's avatar
David Haynes committed
238
                     StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit')))
David Haynes's avatar
David Haynes committed
239

240
    class Meta(URLForm.Meta):
David Haynes's avatar
David Haynes committed
241 242 243
        """
        Metadata about this ModelForm
        """
244 245 246
        # what attributes are included
        fields = URLForm.Meta.fields

David Haynes's avatar
David Haynes committed
247

248
class SignupForm(ModelForm):
249
    """
David Haynes's avatar
David Haynes committed
250
    The form that is used when a user is signing up to be a RegisteredUser
251
    """
252
    full_name = CharField(
253 254 255
        required=True,
        label='Full Name (Required)',
        max_length=100,
256
        widget=TextInput(),
David Haynes's avatar
David Haynes committed
257
        help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.",
258
    )
David Haynes's avatar
David Haynes committed
259

260
    organization = CharField(
261 262 263
        required=True,
        label='Organization (Required)',
        max_length=100,
264
        widget=TextInput(),
David Haynes's avatar
David Haynes committed
265
        help_text="Or whatever \"group\" you would associate with on campus.",
266
    )
David Haynes's avatar
David Haynes committed
267

268
    description = CharField(
269 270 271
        required=False,
        label='Description (Optional)',
        max_length=200,
272
        widget=Textarea(),
David Haynes's avatar
David Haynes committed
273
        help_text="Describe what type of links you would intend to create with Go.",
274
    )
David Haynes's avatar
David Haynes committed
275

276
    # A user becomes registered when they agree to the TOS
277
    registered = BooleanField(
278
        required=True,
David Haynes's avatar
David Haynes committed
279 280
        # ***Need to replace lower url with production URL***
        # ie. go.gmu.edu/about#terms
281
        label=mark_safe(
David Haynes's avatar
David Haynes committed
282
            'Do you accept the <a href="about">Terms of Service</a>?'
283
        ),
David Haynes's avatar
David Haynes committed
284
        help_text="Esssentially the GMU Responsible Use of Computing policies.",
285 286
    )

287
    def __init__(self, request, *args, **kwargs):
David Haynes's avatar
David Haynes committed
288
        """
David Haynes's avatar
David Haynes committed
289
        On initialization of the form, crispy forms renders this layout.
David Haynes's avatar
David Haynes committed
290 291 292
        """
        # Necessary to call request in forms.py, is otherwise restricted to
        # views.py and models.py
293 294
        self.request = request
        super(SignupForm, self).__init__(*args, **kwargs)
David Haynes's avatar
David Haynes committed
295
        self.helper = FormHelper()
296 297 298 299 300
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-4'
        self.helper.field_class = 'col-md-6'

        self.helper.layout = Layout(
301
            Fieldset('',
David Haynes's avatar
David Haynes committed
302 303 304 305 306 307 308 309 310 311 312 313 314
                     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
315

316
    class Meta:
David Haynes's avatar
David Haynes committed
317 318 319
        """
        Metadata about this ModelForm
        """
David Haynes's avatar
David Haynes committed
320
        # what model this form is for
321
        model = RegisteredUser
David Haynes's avatar
David Haynes committed
322
        # what attributes are included
323
        fields = ['full_name', 'organization', 'description', 'registered']