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

4
# Django Imports
5
from django.conf import settings
6
from django.http import HttpResponseServerError  # Http404
7
from django.http import HttpResponseRedirect
8
from django.utils import timezone
9
from django.core.exceptions import PermissionDenied  # ValidationError
10
from django.core.mail import send_mail, EmailMessage
11
from django.contrib.auth import REDIRECT_FIELD_NAME
David Haynes's avatar
David Haynes committed
12
from django.contrib.auth.models import User
13
from django.contrib.auth.decorators import user_passes_test, login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
14
from django.shortcuts import render, get_object_or_404, redirect
15
from ratelimit.decorators import ratelimit
16

17 18 19 20 21 22
# 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
23

David Haynes's avatar
David Haynes committed
24
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
25 26 27 28 29
    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
30 31
"""
def index(request):
32
    # If the user is blocked, redirect them to the blocked page.
33
    # If the user is not authenticated, show them a public landing page.
34
    if not request.user.is_authenticated():
Zosman's avatar
Zosman committed
35
        return render(request, 'public_landing.html')
David Haynes's avatar
David Haynes committed
36
    # If the user isn't approved, then display the you're not approved page.
37
    elif not request.user.registereduser.approved:
38
        if request.user.registereduser.blocked:
Zosman's avatar
Zosman committed
39
            return render(request, 'banned.html')
40
        else:
Zosman's avatar
Zosman committed
41
            return render(request, 'not_registered.html')
42

43

David Haynes's avatar
David Haynes committed
44
    # Initialize a URL form
45
    url_form = URLForm(host = request.META.get('HTTP_HOST'))  # unbound form
46

David Haynes's avatar
David Haynes committed
47 48
    # 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
49
    if request.method == 'POST':
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        # 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
        if url_form.is_valid():
            # Call our post method to assemble our new URL object 
            res = post(request, url_form)
            
            # If there is a 500 error returned, handle it
            if res == 500:
                return HttpResponseServerError(
                    render(request, 'admin/500.html', {})
                )

            # Redirect to the shiny new URL 
            return redirect('view', res.short)
        
        # Else, there is an error, redisplay the form with the validation errors
        else:
            # Render index.html passing the form to the template
            return render(request, 'core/index.html', {
                'form': url_form,
            },
            )

76

David Haynes's avatar
David Haynes committed
77
    # Render index.html passing the form to the template
78
    return render(request, 'core/index.html', {
79
        'form': url_form,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
80 81 82
    },
    )

83 84 85
#rate limits are completely arbitrary
@ratelimit(key='user', rate='3/m', method='POST', block=True)
@ratelimit(key='user', rate='25/d', method='POST', block=True)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
def post(request, url_form):
    # We don't commit the url object yet because we need to add its
    # owner, and parse its date field.
    url = url_form.save(commit = False)
    url.owner = request.user.registereduser

    # If the user entered a short url, it's already been validated,
    # so accept it. If they did not, however, then generate a
    # random one and use that instead.
    short = url_form.cleaned_data.get('short').strip()

    # Check if a short URL was entered
    if len(short) > 0:
        url.short = short
    else:
        # 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 500
108
        else:
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
            url.short = random_short

    # 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.
    expires = url_form.cleaned_data.get('expires')

    # Determine what the expiration date is
    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)
    elif expires == URLForm.CUSTOM:
        url.expires = url_form.cleaned_data.get('expires_custom')
    else:
        pass  # leave the field NULL

    # Make sure that our new URL object is clean, then save it and
    # let's redirect to view this baby.
    url.full_clean()
    url.save()
    return url
133

David Haynes's avatar
David Haynes committed
134
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
135 136
    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
137 138
"""
def view(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
139

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

David Haynes's avatar
David Haynes committed
143
    # Get the URL that is being requested
David Haynes's avatar
David Haynes committed
144
    url = get_object_or_404(URL, short__iexact = short)
145

David Haynes's avatar
David Haynes committed
146
    # Render view.html passing the specified URL and Domain to the template
Jean Michel Rouly's avatar
Jean Michel Rouly committed
147
    return render(request, 'view.html', {
148
        'url': url,
149
        'domain': domain,
150 151 152
    },
    )

David Haynes's avatar
David Haynes committed
153
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
154 155
    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
156 157 158
"""
@login_required
def my_links(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
159

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

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

David Haynes's avatar
David Haynes committed
167
    # Grab a list of all the URL's that are currently owned by the user
David Haynes's avatar
David Haynes committed
168
    urls = URL.objects.filter(owner = request.user.registereduser)
David Haynes's avatar
David Haynes committed
169 170

    # Render my_links.html passing the list of URL's and Domain to the template
171
    return render(request, 'my_links.html', {
172 173
        'urls': urls,
        'domain': domain,
174 175 176
    },
    )

David Haynes's avatar
David Haynes committed
177
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
178 179
    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
180 181 182
"""
@login_required
def delete(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
183

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

David Haynes's avatar
David Haynes committed
188
    # Get the URL that is going to be deleted
David Haynes's avatar
David Haynes committed
189
    url = get_object_or_404(URL, short__iexact = short)
David Haynes's avatar
David Haynes committed
190 191

    # If the RegisteredUser is the owner of the URL
192
    if url.owner == request.user.registereduser:
David Haynes's avatar
David Haynes committed
193
        # remove the URL
194
        url.delete()
David Haynes's avatar
David Haynes committed
195
        # rediret to my_links
196 197
        return redirect('my_links')
    else:
David Haynes's avatar
David Haynes committed
198
        # do not allow them to delete
199
        raise PermissionDenied()
200

David Haynes's avatar
David Haynes committed
201 202 203
"""
    This view presents the user with a registration form. You can register yourself.
"""
204
@login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
205
def signup(request):
206
    # Do not display signup page to registered or approved users
207
    if request.user.registereduser.blocked:
208
        return render(request, 'banned.html')
209
    elif request.user.registereduser.approved:
David Haynes's avatar
David Haynes committed
210
        return redirect('/')
211
    elif request.user.registereduser.registered:
David Haynes's avatar
David Haynes committed
212
        return redirect('registered')
213

David Haynes's avatar
David Haynes committed
214
    # Initialize our signup form
215 216
    signup_form = SignupForm(request,
        initial={'full_name': request.user.first_name + " " + request.user.last_name})
David Haynes's avatar
David Haynes committed
217 218

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

David Haynes's avatar
David Haynes committed
221 222
    # 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
223
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
224 225
        # Now we initialize the form again but this time we have the POST
        # request
David Haynes's avatar
David Haynes committed
226 227
        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
228 229

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

David Haynes's avatar
David Haynes committed
232
        # Django will check the form to make sure it's valid
233
        if signup_form.is_valid():
David Haynes's avatar
David Haynes committed
234
            # Grab data from the form and store into variables
235
            description = signup_form.cleaned_data.get('description')
236
            full_name = signup_form.cleaned_data.get('full_name')
237
            organization = signup_form.cleaned_data.get('organization')
238
            registered = signup_form.cleaned_data.get('registered')
239

240 241
            # Only send mail if we've defined the mailserver
            if settings.EMAIL_HOST and settings.EMAIL_PORT:
root's avatar
root committed
242
                user_mail = request.user.username + settings.EMAIL_DOMAIN
243
                # Email sent to notify Admins
244
                to_admin = EmailMessage(
245
                    'Signup from %s' % (request.user.registereduser.user),
246 247 248 249 250
                    ######################
                    '%s signed up at %s\n\n'
                    'Username: %s\n'
                    'Organization: %s\n\n'
                    'Message: %s\n\n'
251 252
                    'You can contact the user directly by replying to this email or '
                    'reply all to contact the user and notfiy the mailing list.\n'
253 254 255
                    'Please head to go.gmu.edu/useradmin to approve or '
                    'deny this application.'
                    % (str(full_name), str(timezone.now()).strip(),
256
                    str(request.user.registereduser.user), str(organization), str(description)),
257 258
                    ######################
                    settings.EMAIL_FROM,
259
                    [settings.EMAIL_TO],
David Haynes's avatar
David Haynes committed
260
                    reply_to = [user_mail]
261
                ).send()
262
                # Confirmation email sent to Users
263
                send_mail(
264 265 266 267 268 269 270 271 272 273 274 275 276
                    '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]
                )
277

David Haynes's avatar
David Haynes committed
278 279
            # Make sure that our new RegisteredUser object is clean, then save
            # it and let's redirect to tell the user they have registered.
280
            signup_form.save()
281
            return redirect('registered')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
282

David Haynes's avatar
David Haynes committed
283 284
    # render signup.html passing along the form and the current registered
    # status
285
    return render(request, 'core/signup.html', {
286
        'form': signup_form,
287
        'registered': False,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
288 289
    },
    )
Jean Michel Rouly's avatar
Jean Michel Rouly committed
290

David Haynes's avatar
David Haynes committed
291
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
292
    This view redirects a user based on the short URL they requested.
David Haynes's avatar
David Haynes committed
293 294
"""
def redirection(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
295

David Haynes's avatar
David Haynes committed
296 297
    # Get the current domain info
    domain = "%s://%s" % (request.scheme, request.META.get('HTTP_HOST')) + "/"
Jean Michel Rouly's avatar
Jean Michel Rouly committed
298

David Haynes's avatar
David Haynes committed
299
    # Get the URL object that relates to the requested Go link
David Haynes's avatar
David Haynes committed
300
    url = get_object_or_404(URL, short__iexact = short)
David Haynes's avatar
David Haynes committed
301 302
    # Increment our clicks by one
    url.clicks += 1
303

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

David Haynes's avatar
David Haynes committed
308
    # If the user is coming from a QR request then increment qrclicks
309 310 311
    if 'qr' in request.GET:
        url.qrclicks += 1

David Haynes's avatar
David Haynes committed
312
    # If the user is coming from a social media request then increment qrclicks
313 314 315
    if 'social' in request.GET:
        url.socialclicks += 1

David Haynes's avatar
David Haynes committed
316
    # Save our data and redirect the user towards thier destination
Jean Michel Rouly's avatar
Jean Michel Rouly committed
317
    url.save()
318
    return redirect(url.target)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
319

320 321 322 323
"""
    Decorator function for views that checks that the user is logged in and is
    a staff member, displaying the login page if necessary.
"""
David Haynes's avatar
David Haynes committed
324
def staff_member_required(view_func, redirect_field_name = REDIRECT_FIELD_NAME, login_url = '/'):
325 326
    return user_passes_test(
        lambda u: u.is_active and u.is_staff,
David Haynes's avatar
David Haynes committed
327 328
        login_url = login_url,
        redirect_field_name = redirect_field_name
329 330
    )(view_func)

David Haynes's avatar
David Haynes committed
331
"""
332 333
    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
334 335 336 337 338
"""
@staff_member_required
def useradmin(request):

    # If we receive a POST request
339
    if request.POST:
David Haynes's avatar
David Haynes committed
340
        # Get a list of the potential victims (users)
341
        userlist = request.POST.getlist('username')
David Haynes's avatar
David Haynes committed
342
        # If we're approving users
343
        if '_approve' in request.POST:
344
            for name in userlist:
David Haynes's avatar
David Haynes committed
345
                toApprove = RegisteredUser.objects.get(user__username__exact = name)
346 347
                toApprove.approved = True
                toApprove.save()
David Haynes's avatar
David Haynes committed
348 349

                # Send an email letting them know they are approved
350
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
351
                    user_mail = toApprove.user.username + settings.EMAIL_DOMAIN
352 353 354 355 356 357 358 359
                    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'
360
                        % (str(toApprove.full_name)),
361 362 363 364
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
Zosman's avatar
Zosman committed
365

David Haynes's avatar
David Haynes committed
366
        # If we're denying users
367
        elif '_deny' in request.POST:
368
            for name in userlist:
David Haynes's avatar
David Haynes committed
369
                toDeny = RegisteredUser.objects.get(user__username__exact = name)
370
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
371
                    user_mail = toDeny.user.username + settings.EMAIL_DOMAIN
David Haynes's avatar
David Haynes committed
372
                    # Send an email letting them know they are denied
373 374 375 376 377 378 379 380 381
                    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'
382
                        % (str(toDeny.full_name)),
383 384 385 386
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
David Haynes's avatar
David Haynes committed
387
                # Delete their associated RegisteredUsers
388
                toDeny.user.delete()
389
                return HttpResponseRedirect('useradmin')
Zosman's avatar
Zosman committed
390

391
        # If we're blocking users
Zosman's avatar
draft 1  
Zosman committed
392 393
        elif '_block' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
394
                toBlock = RegisteredUser.objects.get(user__username__exact = name)
Zosman's avatar
draft 1  
Zosman committed
395
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
396
                    user_mail = toBlock.user.username + settings.EMAIL_DOMAIN
Zosman's avatar
draft 1  
Zosman committed
397 398 399 400 401 402 403 404 405
                    send_mail(
                        'Your Account has been Blocked!',
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
                        'blocked you from using Go.\n\n'
                        'Please reach out to srct@gmu.edu to appeal '
                        'this decision.\n\n'
                        '- Go Admins'
406
                        % (str(toBlock.full_name)),
Zosman's avatar
draft 1  
Zosman committed
407 408 409 410
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
411 412
                # toBlock.user.delete()
                toBlock.blocked = True
413 414
                toBlock.approved = False
                toBlock.registered = False
415
                toBlock.save()
Zosman's avatar
Zosman committed
416

417
        # If we're un-blocking users
418 419
        elif '_unblock' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
420
                toUnBlock = RegisteredUser.objects.get(user__username__exact = name)
421
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
422
                    user_mail = toUnBlock.user.username + settings.EMAIL_DOMAIN
423
                    send_mail(
424
                        'Your Account has been Un-Blocked!',
425 426 427
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
428
                        'Un-Blocked you from using Go.\n\n'
429
                        'If you wish to continue Go use please register again. \n\n'
430 431
                        'Congratulations! '
                        '- Go Admins'
432
                        % (str(toUnBlock.full_name)),
433 434 435 436
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
437
                # toUNblock.user.delete()
438 439
                toUnBlock.blocked = False
                toUnBlock.save()
440
                return HttpResponseRedirect('useradmin')
441

442
        # If we're removing existing users
443 444
        elif '_remove' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
445
                toRemove = RegisteredUser.objects.get(user__username__exact = name)
446
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
447
                    user_mail = toRemove.user.username + settings.EMAIL_DOMAIN
448 449 450 451 452 453 454 455
                    send_mail(
                        'Your Account has been Deleted!',
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have decided to remove you from Go. \n\n'
                        'Please reach out to srct@gmu.edu to appeal '
                        'this decision.\n\n'
                        '- Go Admins'
456
                        % (str(toRemove.full_name)),
457 458 459 460
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
461
                toRemove.user.delete()
462
                return HttpResponseRedirect('useradmin')
463

464
    # Get a list of all RegisteredUsers that need to be approved
Zosman's avatar
Zosman committed
465
    need_approval = RegisteredUser.objects.filter(registered=True).filter(approved=False).filter(blocked=False)
Zosman's avatar
Zosman committed
466
    # Get a list of all RegisteredUsers that are currently users
467
    current_users = RegisteredUser.objects.filter(approved=True).filter(registered=True).filter(blocked=False)
Zosman's avatar
Zosman committed
468
    # Get a list of all RegisteredUsers that are blocked
469
    blocked_users = RegisteredUser.objects.filter(blocked=True)
470

David Haynes's avatar
David Haynes committed
471
    # Pass that list to the template
472
    return render(request, 'admin/useradmin.html', {
473 474 475
        'need_approval': need_approval,
        'current_users': current_users,
        'blocked_users': blocked_users
David Haynes's avatar
David Haynes committed
476
    })