forms.py 8.45 KB
Newer Older
1 2 3
# Future Imports
from __future__ import unicode_literals, absolute_import, print_function, division

4
# Django Imports
5
from django import forms
6
from django.core.exceptions import ValidationError
7
from django.utils.safestring import mark_safe
Matthew Rodgers's avatar
Matthew Rodgers committed
8
from django.utils import timezone
9 10 11 12 13

# App Imports
from go.models import URL, RegisteredUser

# Other Imports
David Haynes's avatar
David Haynes committed
14
from crispy_forms.helper import FormHelper
15 16
from crispy_forms.layout import Layout, Fieldset, Submit, HTML, Div, Field
from crispy_forms.bootstrap import StrictButton, PrependedText, Accordion, AccordionGroup
17
from bootstrap3_datetime.widgets import DateTimePicker
18
from datetime import date, datetime, timedelta
19

David Haynes's avatar
David Haynes committed
20 21 22
"""
    The form that is used in URL creation.
"""
23
class URLForm(forms.ModelForm):
24

25 26
    # Prevent redirect loop links
    def clean_target(self):
David Haynes's avatar
David Haynes committed
27
        # get the entered target link
28
        target = self.cleaned_data.get('target')
David Haynes's avatar
David Haynes committed
29
        # if the host (go.gmu.edu) is in the entered target link
30 31 32 33 34
        if self.host in target:
            raise ValidationError("You can't make a Go link to Go silly!")
        else:
            return target

35 36 37
    # Custom target URL field
    target = forms.URLField(
        required=True,
38
        label='Long URL (Required)',
39 40
        max_length=1000,
        widget=forms.URLInput(attrs={
41
            'placeholder': 'https://yoursite.com/'
42 43 44 45 46 47
        })
    )

    # Check to make sure the short url has not been used
    def unique_short(value):
        try:
David Haynes's avatar
David Haynes committed
48
            # if we're able to get a URL with the same short url
49 50 51
            URL.objects.get(short__iexact=value)
        except URL.DoesNotExist:
            return
David Haynes's avatar
David Haynes committed
52
        # then raise a ValidationError
53 54 55 56
        raise ValidationError('Short url already exists.')

    # Custom short-url field with validators.
    short = forms.SlugField(
David Haynes's avatar
David Haynes committed
57 58 59 60 61 62
        required = False,
        label = 'Short URL (Optional)',
        widget = forms.TextInput(),
        validators = [unique_short],
        max_length = 20,
        min_length = 3,
63 64
    )

David Haynes's avatar
David Haynes committed
65
    # define some string date standards
66 67 68
    DAY = '1 Day'
    WEEK = '1 Week'
    MONTH = '1 Month'
69
    CUSTOM = 'Custom Date'
70 71
    NEVER = 'Never'

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

Matthew Rodgers's avatar
Matthew Rodgers committed
81
    # Add preset expiration choices.
82
    expires = forms.ChoiceField(
David Haynes's avatar
David Haynes committed
83 84 85 86 87
        required = True,
        label = 'Expiration (Required)',
        choices = EXPIRATION_CHOICES,
        initial = NEVER,
        widget = forms.RadioSelect(),
88 89
    )

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


Matthew Rodgers's avatar
Matthew Rodgers committed
100
    # Add a custom expiration choice.
101
    expires_custom = forms.DateTimeField(
Matthew Rodgers's avatar
Matthew Rodgers committed
102
        required = False,
David Haynes's avatar
David Haynes committed
103 104 105 106 107
        label = 'Custom Date',
        input_formats = ['%m-%d-%Y'],
        validators = [valid_date],
        initial = lambda: datetime.now() + timedelta(days=1),
        widget = DateTimePicker(
108 109 110 111 112 113
            options={
                "format": "MM-DD-YYYY",
                "pickTime": False,
            },
            icon_attrs={
                "class": "fa fa-calendar",
David Haynes's avatar
David Haynes committed
114 115
            },
        )
116 117
    )

David Haynes's avatar
David Haynes committed
118
    # on initialization of the form, crispy forms renders this layout
119
    def __init__(self, *args, **kwargs):
120 121
        # Grab that host info
        self.host = kwargs.pop('host', None)
122
        super(URLForm, self).__init__(*args, **kwargs)
123
        # Define the basics for crispy-forms
124
        self.helper = FormHelper()
125
        self.helper.form_method = 'POST'
126

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

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

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

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

172 173 174 175 176 177
                    # FIN
                    template='crispy/accordian.html'),
            #######################
            HTML("""
                <br />"""),
            StrictButton('Shorten', css_class="btn btn-primary btn-md col-md-4", type='submit')))
178

David Haynes's avatar
David Haynes committed
179
    # metadata about this ModelForm
180
    class Meta:
David Haynes's avatar
David Haynes committed
181
        # what model this form is for
182
        model = URL
David Haynes's avatar
David Haynes committed
183 184
        # what attributes are included
        fields = ['target',]
185

David Haynes's avatar
David Haynes committed
186 187 188
"""
    The form that is used when a user is signing up to be a RegisteredUser
"""
189
class SignupForm(forms.ModelForm):
190

David Haynes's avatar
David Haynes committed
191
    # The full name of the RegisteredUser
192
    full_name = forms.CharField(
David Haynes's avatar
David Haynes committed
193 194 195 196
        required = True,
        label = 'Full Name (Required)',
        max_length = 100,
        widget = forms.TextInput(),
197
    )
David Haynes's avatar
David Haynes committed
198 199

    # The RegisteredUser's chosen organization
200
    organization = forms.CharField(
David Haynes's avatar
David Haynes committed
201 202 203 204
        required = True,
        label = 'Organization (Required)',
        max_length = 100,
        widget = forms.TextInput(),
205
    )
David Haynes's avatar
David Haynes committed
206 207

    # The RegisteredUser's reason for signing up to us Go
208
    description = forms.CharField(
David Haynes's avatar
David Haynes committed
209 210 211 212
        required = False,
        label = 'Description (Optional)',
        max_length = 200,
        widget = forms.Textarea(),
213
    )
David Haynes's avatar
David Haynes committed
214

215 216
    # A user becomes registered when they agree to the TOS
    registered = forms.BooleanField(
217
        required=True,
218 219
        # ***Need to replace lower url with production URL*** ie. go.gmu.edu/about#terms
        label = mark_safe('Do you accept the <a href="http://127.0.0.1:8000/about#terms">Terms of Service</a>?'),
220 221
    )

David Haynes's avatar
David Haynes committed
222
    # on initialization of the form, crispy forms renders this layout
223 224 225 226
    def __init__(self, request, *args, **kwargs):
        # Necessary to call request in forms.py, is otherwise restricted to views.py and models.py
        self.request = request
        super(SignupForm, self).__init__(*args, **kwargs)
David Haynes's avatar
David Haynes committed
227
        self.helper = FormHelper()
228 229 230 231 232
        self.helper.form_class = 'form-horizontal'
        self.helper.label_class = 'col-md-4'
        self.helper.field_class = 'col-md-6'

        self.helper.layout = Layout(
233
            Fieldset('',
234
                Div(
235
                    # Place in form fields
236 237 238 239
                    Div(
                        'full_name',
                        'organization',
                        'description',
240
                        'registered',
241 242 243
                        css_class='well'),

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

    # metadata about this ModelForm
248
    class Meta:
David Haynes's avatar
David Haynes committed
249
        # what model this form is for
250
        model = RegisteredUser
David Haynes's avatar
David Haynes committed
251 252
        # what attributes are included
        fields = ['full_name', 'organization', 'description', 'registered',]