views.py 14.2 KB
Newer Older
1
# Django Imports
2
from django.conf import settings
3
from django.http import HttpResponseServerError  # Http404
4
from django.utils import timezone
5
from django.core.exceptions import PermissionDenied  # ValidationError
6
from django.core.mail import send_mail, EmailMessage
7
from django.contrib.auth import REDIRECT_FIELD_NAME
David Haynes's avatar
David Haynes committed
8
from django.contrib.auth.models import User
9
from django.contrib.auth.decorators import user_passes_test, login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
10
from django.shortcuts import render, get_object_or_404, redirect
11

12 13 14 15 16 17
# App Imports
from go.models import URL, RegisteredUser
from go.forms import URLForm, SignupForm

# Other Imports
from datetime import timedelta
Jean Michel Rouly's avatar
Jean Michel Rouly committed
18

David Haynes's avatar
David Haynes committed
19
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
20 21 22 23 24
    This view handles the homepage that the user is presented with when
    they request '/'. If they're not logged in, they're redirected to
    login. If they're logged in but not registered, they're given the
    not_registered error page. If they are logged in AND registered, they
    get the URL registration form.
David Haynes's avatar
David Haynes committed
25 26
"""
def index(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
27

28 29 30
    # If the user is not authenticated, show them a public landing page.
    if not request.user.is_authenticated():
        return render(request, 'public_landing.html')
David Haynes's avatar
David Haynes committed
31
    # If the user isn't approved, then display the you're not approved page.
32
    elif not request.user.registereduser.approved:
33 34
        return render(request, 'not_registered.html')

David Haynes's avatar
David Haynes committed
35
    # Initialize a URL form
36
    url_form = URLForm(host=request.META.get('HTTP_HOST'))  # unbound form
37

David Haynes's avatar
David Haynes committed
38 39
    # If a POST request is received, then the user has submitted a form and it's
    # time to parse the form and create a new URL object
40
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
41 42 43 44 45
        # Now we initialize the form again but this time we have the POST
        # request
        url_form = URLForm(request.POST, host=request.META.get('HTTP_HOST'))

        # Django will check the form to make sure it's valid
46 47
        if url_form.is_valid():

Jean Michel Rouly's avatar
Jean Michel Rouly committed
48 49
            # We don't commit the url object yet because we need to add its
            # owner, and parse its date field.
50
            url = url_form.save(commit=False)
51
            url.owner = request.user.registereduser
52

Jean Michel Rouly's avatar
Jean Michel Rouly committed
53
            # If the user entered a short url, it's already been validated,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
54 55
            # so accept it. If they did not, however, then generate a
            # random one and use that instead.
56
            short = url_form.cleaned_data.get('short').strip()
David Haynes's avatar
David Haynes committed
57 58

            # Check if a short URL was entered
59 60 61
            if len(short) > 0:
                url.short = short
            else:
62 63 64 65 66 67
                # If the user didn't enter a short url, generate a random
                # one. However, if a random one can't be generated, return
                # a 500 server error.
                random_short = URL.generate_valid_short()
                if random_short is None:
                    return HttpResponseServerError(
David Haynes's avatar
David Haynes committed
68
                        render(request, 'admin/500.html', {})
69 70 71
                    )
                else:
                    url.short = random_short
72

Jean Michel Rouly's avatar
Jean Michel Rouly committed
73 74 75
            # Grab the expiration field value. It's currently an unsable
            # string value, so we need to parse it into a datetime object
            # relative to right now.
76
            expires = url_form.cleaned_data.get('expires')
77

David Haynes's avatar
David Haynes committed
78
            # Determine what the expiration date is
79 80 81 82 83 84
            if expires == URLForm.DAY:
                url.expires = timezone.now() + timedelta(days=1)
            elif expires == URLForm.WEEK:
                url.expires = timezone.now() + timedelta(weeks=1)
            elif expires == URLForm.MONTH:
                url.expires = timezone.now() + timedelta(weeks=3)
Matthew Rodgers's avatar
Matthew Rodgers committed
85 86
            elif expires == URLForm.CUSTOM:
                url.expires = url_form.cleaned_data.get('expires_custom')
87
            else:
88
                pass  # leave the field NULL
89

Jean Michel Rouly's avatar
Jean Michel Rouly committed
90 91
            # Make sure that our new URL object is clean, then save it and
            # let's redirect to view this baby.
92 93
            url.full_clean()
            url.save()
David Haynes's avatar
David Haynes committed
94

Jean Michel Rouly's avatar
Jean Michel Rouly committed
95
            return redirect('view', url.short)
96

David Haynes's avatar
David Haynes committed
97
    # Render index.html passing the form to the template
98
    return render(request, 'core/index.html', {
99
        'form': url_form,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
100 101 102
    },
    )

David Haynes's avatar
David Haynes committed
103
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
104 105
    This view allows the user to view details about a URL. Note that they
    do not need to be logged in to view info.
David Haynes's avatar
David Haynes committed
106 107
"""
def view(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
108

David Haynes's avatar
David Haynes committed
109
    # Get the current domain info
Nicholas Anderson's avatar
Nicholas Anderson committed
110
    domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST')) + "/"
111

David Haynes's avatar
David Haynes committed
112
    # Get the URL that is being requested
113
    url = get_object_or_404(URL, short__iexact=short)
114

David Haynes's avatar
David Haynes committed
115
    # Render view.html passing the specified URL and Domain to the template
Jean Michel Rouly's avatar
Jean Michel Rouly committed
116
    return render(request, 'view.html', {
117
        'url': url,
118
        'domain': domain,
119 120 121
    },
    )

David Haynes's avatar
David Haynes committed
122
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
123 124
    This view displays all the information about all of your URLs. You
    obviously need to be logged in to view your URLs.
David Haynes's avatar
David Haynes committed
125 126 127
"""
@login_required
def my_links(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
128

David Haynes's avatar
David Haynes committed
129
    # Do not display this page to unapproved users
130
    if not request.user.registereduser.approved:
131
        return render(request, 'not_registered.html')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
132

David Haynes's avatar
David Haynes committed
133
    # Get the current domain info
134
    domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST')) + "/"
135

David Haynes's avatar
David Haynes committed
136 137 138 139
    # Grab a list of all the URL's that are currently owned by the user
    urls = URL.objects.filter(owner=request.user.registereduser)

    # Render my_links.html passing the list of URL's and Domain to the template
140
    return render(request, 'my_links.html', {
141 142
        'urls': urls,
        'domain': domain,
143 144 145
    },
    )

Jean Michel Rouly's avatar
Jean Michel Rouly committed
146

David Haynes's avatar
David Haynes committed
147
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
148 149
    This view deletes a URL if you have the permission to. User must be
    logged in and registered, and must also be the owner of the URL.
David Haynes's avatar
David Haynes committed
150 151 152
"""
@login_required
def delete(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
153

David Haynes's avatar
David Haynes committed
154
    # Do not allow unapproved users to delete links
155
    if not request.user.registereduser.approved:
156
        return render(request, 'not_registered.html')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
157

David Haynes's avatar
David Haynes committed
158
    # Get the URL that is going to be deleted
159
    url = get_object_or_404(URL, short__iexact=short)
David Haynes's avatar
David Haynes committed
160 161

    # If the RegisteredUser is the owner of the URL
162
    if url.owner == request.user.registereduser:
David Haynes's avatar
David Haynes committed
163
        # remove the URL
164
        url.delete()
David Haynes's avatar
David Haynes committed
165
        # rediret to my_links
166 167
        return redirect('my_links')
    else:
David Haynes's avatar
David Haynes committed
168
        # do not allow them to delete
169
        raise PermissionDenied()
170

David Haynes's avatar
David Haynes committed
171 172 173
"""
    This view presents the user with a registration form. You can register yourself.
"""
174
@login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
175
def signup(request):
176 177
    # Do not display signup page to registered or approved users
    if request.user.registereduser.approved:
David Haynes's avatar
David Haynes committed
178
        return redirect('/')
179
    elif request.user.registereduser.registered:
David Haynes's avatar
David Haynes committed
180
        return redirect('registered')
181

David Haynes's avatar
David Haynes committed
182
    # Initialize our signup form
183 184
    signup_form = SignupForm(request,
        initial={'full_name': request.user.first_name + " " + request.user.last_name})
David Haynes's avatar
David Haynes committed
185 186

    # Set the full_name field to readonly since CAS will fill that in for them
187
    signup_form.fields['full_name'].widget.attrs['readonly'] = 'readonly'
Jean Michel Rouly's avatar
Jean Michel Rouly committed
188

David Haynes's avatar
David Haynes committed
189 190
    # If a POST request is received, then the user has submitted a form and it's
    # time to parse the form and create a new RegisteredUser
Jean Michel Rouly's avatar
Jean Michel Rouly committed
191
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
192 193
        # Now we initialize the form again but this time we have the POST
        # request
194 195
        signup_form = SignupForm(request, request.POST, instance=request.user.registereduser,
            initial={'full_name': request.user.first_name + " " + request.user.last_name})
David Haynes's avatar
David Haynes committed
196 197

        # set the readonly flag again for good measure
198
        signup_form.fields['full_name'].widget.attrs['readonly'] = 'readonly'
199

David Haynes's avatar
David Haynes committed
200
        # Django will check the form to make sure it's valid
201
        if signup_form.is_valid():
David Haynes's avatar
David Haynes committed
202
            # Grab data from the form and store into variables
203
            description = signup_form.cleaned_data.get('description')
204
            full_name = signup_form.cleaned_data.get('full_name')
205
            organization = signup_form.cleaned_data.get('organization')
206
            registered = signup_form.cleaned_data.get('registered')
207

208 209
            # Only send mail if we've defined the mailserver
            if settings.EMAIL_HOST and settings.EMAIL_PORT:
root's avatar
root committed
210
                user_mail = request.user.username + settings.EMAIL_DOMAIN
211
                # Email sent to notify Admins
212
                to_admin = EmailMessage(
213
                    'Signup from %s' % (request.user.registereduser.user),
214 215 216 217 218
                    ######################
                    '%s signed up at %s\n\n'
                    'Username: %s\n'
                    'Organization: %s\n\n'
                    'Message: %s\n\n'
219 220
                    'You can contact the user directly by replying to this email or '
                    'reply all to contact the user and notfiy the mailing list.\n'
221 222 223
                    'Please head to go.gmu.edu/useradmin to approve or '
                    'deny this application.'
                    % (str(full_name), str(timezone.now()).strip(),
224
                    str(request.user.registereduser.user), str(organization), str(description)),
225 226
                    ######################
                    settings.EMAIL_FROM,
227 228
                    [settings.EMAIL_TO],
                    reply_to=[user_mail]
229
                ).send()
230
                # Confirmation email sent to Users
231
                send_mail(
232 233 234 235 236 237 238 239 240 241 242 243 244
                    'We have received your Go application!',
                    ######################
                    'Hey there %s,\n\n'
                    'The Go admins have received your application and are '
                    'currently in the process of reviewing it.\n\n'
                    'You will receive another email when you have been '
                    'approved.\n\n'
                    '- Go Admins'
                    % (str(full_name)),
                    ######################
                    settings.EMAIL_FROM,
                    [user_mail]
                )
245

David Haynes's avatar
David Haynes committed
246 247
            # Make sure that our new RegisteredUser object is clean, then save
            # it and let's redirect to tell the user they have registered.
248
            signup_form.save()
249
            return redirect('registered')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
250

David Haynes's avatar
David Haynes committed
251 252
    # render signup.html passing along the form and the current registered
    # status
253
    return render(request, 'core/signup.html', {
254
        'form': signup_form,
255
        'registered': False,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
256 257
    },
    )
Jean Michel Rouly's avatar
Jean Michel Rouly committed
258

David Haynes's avatar
David Haynes committed
259
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
260
    This view redirects a user based on the short URL they requested.
David Haynes's avatar
David Haynes committed
261 262
"""
def redirection(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
263

David Haynes's avatar
David Haynes committed
264 265 266 267
    # Get the current domain info
    domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST')) + "/"

    # Get the URL object that relates to the requested Go link
268
    url = get_object_or_404(URL, short__iexact=short)
David Haynes's avatar
David Haynes committed
269 270
    # Increment our clicks by one
    url.clicks += 1
271

David Haynes's avatar
David Haynes committed
272
    # If the user is trying to make a Go link to itself, we 404 them
273
    if url.target == domain + short:
David Haynes's avatar
David Haynes committed
274
        return redirect('admin/404.html')
275

David Haynes's avatar
David Haynes committed
276
    # If the user is coming from a QR request then increment qrclicks
277 278 279
    if 'qr' in request.GET:
        url.qrclicks += 1

David Haynes's avatar
David Haynes committed
280
    # If the user is coming from a social media request then increment qrclicks
281 282 283
    if 'social' in request.GET:
        url.socialclicks += 1

David Haynes's avatar
David Haynes committed
284
    # Save our data and redirect the user towards thier destination
Jean Michel Rouly's avatar
Jean Michel Rouly committed
285
    url.save()
286
    return redirect(url.target)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
287

David Haynes's avatar
David Haynes committed
288
"""
289 290
    This view is a simplified admin panel, so that staff don't need to log in
    to approve links
David Haynes's avatar
David Haynes committed
291 292 293 294 295
"""
@staff_member_required
def useradmin(request):

    # If we receive a POST request
296
    if request.POST:
David Haynes's avatar
David Haynes committed
297
        # Get a list of the potential victims (users)
298
        userlist = request.POST.getlist('username')
David Haynes's avatar
David Haynes committed
299 300

        # If we're approving users
301
        if '_approve' in request.POST:
302
            for name in userlist:
303
                toapprove = RegisteredUser.objects.get(user__username__exact=name)
304 305
                toapprove.approved = True
                toapprove.save()
David Haynes's avatar
David Haynes committed
306 307

                # Send an email letting them know they are approved
308
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
root's avatar
root committed
309
                    user_mail = toapprove.user.username + settings.EMAIL_DOMAIN
310 311 312 313 314 315 316 317 318 319 320 321 322
                    send_mail(
                        'Your Account has been Approved!',
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
                        'approved you to use Go!\n\n'
                        'Head over to go.gmu.edu to create your first address.\n\n'
                        '- Go Admins'
                        % (str(toapprove.full_name)),
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
David Haynes's avatar
David Haynes committed
323
        # If we're denying users
324
        elif '_deny' in request.POST:
325
            for name in userlist:
326
                todeny = RegisteredUser.objects.get(user__username__exact=name)
327
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
David Haynes's avatar
David Haynes committed
328
                    user_mail = todeny.user.username + settings.EMAIL_DOMAIN
David Haynes's avatar
David Haynes committed
329 330

                    # Send an email letting them know they are denied
331 332 333 334 335 336 337 338 339 340 341 342 343 344
                    send_mail(
                        'Your Account has been Denied!',
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
                        'decided to not approve you to use Go.\n\n'
                        'Please reach out to srct@gmu.edu to appeal '
                        'this decision.\n\n'
                        '- Go Admins'
                        % (str(todeny.full_name)),
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
David Haynes's avatar
David Haynes committed
345
                # Delete their associated RegisteredUsers
346
                todeny.user.delete()
David Haynes's avatar
David Haynes committed
347 348

    # Get a list of all RegisteredUsers tthat need to be approved
349
    need_approval = RegisteredUser.objects.filter(registered=True).filter(approved=False)
David Haynes's avatar
David Haynes committed
350 351

    # Pass that list to the template
352
    return render(request, 'admin/useradmin.html', {
353 354 355
        'need_approval': need_approval
    },
    )
David Haynes's avatar
David Haynes committed
356 357 358 359 360 361 362 363 364 365 366

"""
    Decorator function for views that checks that the user is logged in and is
    a staff member, displaying the login page if necessary.
"""
def staff_member_required(view_func, redirect_field_name=REDIRECT_FIELD_NAME, login_url='/'):
    return user_passes_test(
        lambda u: u.is_active and u.is_staff,
        login_url=login_url,
        redirect_field_name=redirect_field_name
    )(view_func)