views.py 18.6 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
"""
Zach Knox's avatar
Zach Knox committed
31
def new_link(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
        return redirect('view', post(request).short)
51

David Haynes's avatar
David Haynes committed
52
    # Render index.html passing the form to the template
Zach Knox's avatar
Zach Knox committed
53
    return render(request, 'core/new_link.html', {
54
        'form': url_form,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
55 56 57
    },
    )

Zach Knox's avatar
Zach Knox committed
58 59 60 61
# for compatibility, just in case
def my_links(request):
    return index(request)

62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
#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)
def post(request):
    # 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():

        # 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 HttpResponseServerError(
                    render(request, 'admin/500.html', {})
                )
            else:
                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

David Haynes's avatar
David Haynes committed
121
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
122 123
    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
124 125
"""
def view(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
126

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

David Haynes's avatar
David Haynes committed
130
    # Get the URL that is being requested
David Haynes's avatar
David Haynes committed
131
    url = get_object_or_404(URL, short__iexact = short)
132

David Haynes's avatar
David Haynes committed
133
    # Render view.html passing the specified URL and Domain to the template
Jean Michel Rouly's avatar
Jean Michel Rouly committed
134
    return render(request, 'view.html', {
135
        'url': url,
136
        'domain': domain,
137 138 139
    },
    )

David Haynes's avatar
David Haynes committed
140
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
141 142
    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
143 144
"""
@login_required
Zach Knox's avatar
Zach Knox committed
145
def index(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
146

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

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

David Haynes's avatar
David Haynes committed
154
    # Grab a list of all the URL's that are currently owned by the user
David Haynes's avatar
David Haynes committed
155
    urls = URL.objects.filter(owner = request.user.registereduser)
David Haynes's avatar
David Haynes committed
156 157

    # Render my_links.html passing the list of URL's and Domain to the template
Zach Knox's avatar
Zach Knox committed
158
    return render(request, 'core/index.html', {
159 160
        'urls': urls,
        'domain': domain,
161 162 163
    },
    )

David Haynes's avatar
David Haynes committed
164
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
165 166
    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
167 168 169
"""
@login_required
def delete(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
170

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

David Haynes's avatar
David Haynes committed
175
    # Get the URL that is going to be deleted
David Haynes's avatar
David Haynes committed
176
    url = get_object_or_404(URL, short__iexact = short)
David Haynes's avatar
David Haynes committed
177 178

    # If the RegisteredUser is the owner of the URL
179
    if url.owner == request.user.registereduser:
David Haynes's avatar
David Haynes committed
180
        # remove the URL
181
        url.delete()
David Haynes's avatar
David Haynes committed
182
        # rediret to my_links
183 184
        return redirect('my_links')
    else:
David Haynes's avatar
David Haynes committed
185
        # do not allow them to delete
186
        raise PermissionDenied()
187

David Haynes's avatar
David Haynes committed
188 189 190
"""
    This view presents the user with a registration form. You can register yourself.
"""
191
@login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
192
def signup(request):
193
    # Do not display signup page to registered or approved users
194
    if request.user.registereduser.blocked:
195
        return render(request, 'banned.html')
196
    elif request.user.registereduser.approved:
David Haynes's avatar
David Haynes committed
197
        return redirect('/')
198
    elif request.user.registereduser.registered:
David Haynes's avatar
David Haynes committed
199
        return redirect('registered')
200

David Haynes's avatar
David Haynes committed
201
    # Initialize our signup form
202 203
    signup_form = SignupForm(request,
        initial={'full_name': request.user.first_name + " " + request.user.last_name})
David Haynes's avatar
David Haynes committed
204 205

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

David Haynes's avatar
David Haynes committed
208 209
    # 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
210
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
211 212
        # Now we initialize the form again but this time we have the POST
        # request
David Haynes's avatar
David Haynes committed
213 214
        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
215 216

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

David Haynes's avatar
David Haynes committed
219
        # Django will check the form to make sure it's valid
220
        if signup_form.is_valid():
David Haynes's avatar
David Haynes committed
221
            # Grab data from the form and store into variables
222
            description = signup_form.cleaned_data.get('description')
223
            full_name = signup_form.cleaned_data.get('full_name')
224
            organization = signup_form.cleaned_data.get('organization')
225
            registered = signup_form.cleaned_data.get('registered')
226

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

David Haynes's avatar
David Haynes committed
265 266
            # Make sure that our new RegisteredUser object is clean, then save
            # it and let's redirect to tell the user they have registered.
267
            signup_form.save()
268
            return redirect('registered')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
269

David Haynes's avatar
David Haynes committed
270 271
    # render signup.html passing along the form and the current registered
    # status
272
    return render(request, 'core/signup.html', {
273
        'form': signup_form,
274
        'registered': False,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
275 276
    },
    )
Jean Michel Rouly's avatar
Jean Michel Rouly committed
277

David Haynes's avatar
David Haynes committed
278
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
279
    This view redirects a user based on the short URL they requested.
David Haynes's avatar
David Haynes committed
280 281
"""
def redirection(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
282

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

David Haynes's avatar
David Haynes committed
286
    # Get the URL object that relates to the requested Go link
David Haynes's avatar
David Haynes committed
287
    url = get_object_or_404(URL, short__iexact = short)
David Haynes's avatar
David Haynes committed
288 289
    # Increment our clicks by one
    url.clicks += 1
290

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

David Haynes's avatar
David Haynes committed
295
    # If the user is coming from a QR request then increment qrclicks
296 297 298
    if 'qr' in request.GET:
        url.qrclicks += 1

David Haynes's avatar
David Haynes committed
299
    # If the user is coming from a social media request then increment qrclicks
300 301 302
    if 'social' in request.GET:
        url.socialclicks += 1

David Haynes's avatar
David Haynes committed
303
    # Save our data and redirect the user towards thier destination
Jean Michel Rouly's avatar
Jean Michel Rouly committed
304
    url.save()
305
    return redirect(url.target)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
306

307 308 309 310
"""
    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
311
def staff_member_required(view_func, redirect_field_name = REDIRECT_FIELD_NAME, login_url = '/'):
312 313
    return user_passes_test(
        lambda u: u.is_active and u.is_staff,
David Haynes's avatar
David Haynes committed
314 315
        login_url = login_url,
        redirect_field_name = redirect_field_name
316 317
    )(view_func)

David Haynes's avatar
David Haynes committed
318
"""
319 320
    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
321 322 323 324 325
"""
@staff_member_required
def useradmin(request):

    # If we receive a POST request
326
    if request.POST:
David Haynes's avatar
David Haynes committed
327
        # Get a list of the potential victims (users)
328
        userlist = request.POST.getlist('username')
David Haynes's avatar
David Haynes committed
329
        # If we're approving users
330
        if '_approve' in request.POST:
331
            for name in userlist:
David Haynes's avatar
David Haynes committed
332
                toApprove = RegisteredUser.objects.get(user__username__exact = name)
333 334
                toApprove.approved = True
                toApprove.save()
David Haynes's avatar
David Haynes committed
335 336

                # Send an email letting them know they are approved
337
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
338
                    user_mail = toApprove.user.username + settings.EMAIL_DOMAIN
339 340 341 342 343 344 345 346
                    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'
347
                        % (str(toApprove.full_name)),
348 349 350 351
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
Zosman's avatar
Zosman committed
352

David Haynes's avatar
David Haynes committed
353
        # If we're denying users
354
        elif '_deny' in request.POST:
355
            for name in userlist:
David Haynes's avatar
David Haynes committed
356
                toDeny = RegisteredUser.objects.get(user__username__exact = name)
357
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
358
                    user_mail = toDeny.user.username + settings.EMAIL_DOMAIN
David Haynes's avatar
David Haynes committed
359
                    # Send an email letting them know they are denied
360 361 362 363 364 365 366 367 368
                    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'
369
                        % (str(toDeny.full_name)),
370 371 372 373
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
David Haynes's avatar
David Haynes committed
374
                # Delete their associated RegisteredUsers
375
                toDeny.user.delete()
376
                return HttpResponseRedirect('useradmin')
Zosman's avatar
Zosman committed
377

378
        # If we're blocking users
Zosman's avatar
draft 1  
Zosman committed
379 380
        elif '_block' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
381
                toBlock = RegisteredUser.objects.get(user__username__exact = name)
Zosman's avatar
draft 1  
Zosman committed
382
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
383
                    user_mail = toBlock.user.username + settings.EMAIL_DOMAIN
Zosman's avatar
draft 1  
Zosman committed
384 385 386 387 388 389 390 391 392
                    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'
393
                        % (str(toBlock.full_name)),
Zosman's avatar
draft 1  
Zosman committed
394 395 396 397
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
398 399
                # toBlock.user.delete()
                toBlock.blocked = True
400 401
                toBlock.approved = False
                toBlock.registered = False
402
                toBlock.save()
Zosman's avatar
Zosman committed
403

404
        # If we're un-blocking users
405 406
        elif '_unblock' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
407
                toUnBlock = RegisteredUser.objects.get(user__username__exact = name)
408
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
409
                    user_mail = toUnBlock.user.username + settings.EMAIL_DOMAIN
410
                    send_mail(
411
                        'Your Account has been Un-Blocked!',
412 413 414
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
415
                        'Un-Blocked you from using Go.\n\n'
416
                        'If you wish to continue Go use please register again. \n\n'
417 418
                        'Congratulations! '
                        '- Go Admins'
419
                        % (str(toUnBlock.full_name)),
420 421 422 423
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
424
                # toUNblock.user.delete()
425 426
                toUnBlock.blocked = False
                toUnBlock.save()
427
                return HttpResponseRedirect('useradmin')
428

429
        # If we're removing existing users
430 431
        elif '_remove' in request.POST:
            for name in userlist:
David Haynes's avatar
David Haynes committed
432
                toRemove = RegisteredUser.objects.get(user__username__exact = name)
433
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
434
                    user_mail = toRemove.user.username + settings.EMAIL_DOMAIN
435 436 437 438 439 440 441 442
                    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'
443
                        % (str(toRemove.full_name)),
444 445 446 447
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
448
                toRemove.user.delete()
449
                return HttpResponseRedirect('useradmin')
450

451
    # Get a list of all RegisteredUsers that need to be approved
Zosman's avatar
Zosman committed
452
    need_approval = RegisteredUser.objects.filter(registered=True).filter(approved=False).filter(blocked=False)
Zosman's avatar
Zosman committed
453
    # Get a list of all RegisteredUsers that are currently users
454
    current_users = RegisteredUser.objects.filter(approved=True).filter(registered=True).filter(blocked=False)
Zosman's avatar
Zosman committed
455
    # Get a list of all RegisteredUsers that are blocked
456
    blocked_users = RegisteredUser.objects.filter(blocked=True)
457

David Haynes's avatar
David Haynes committed
458
    # Pass that list to the template
459
    return render(request, 'admin/useradmin.html', {
460 461 462
        'need_approval': need_approval,
        'current_users': current_users,
        'blocked_users': blocked_users
David Haynes's avatar
David Haynes committed
463
    })