views.py 18.5 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
        return redirect('view', post(request).short)
51

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

58 59 60 61 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
#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
118
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
119 120
    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
121 122
"""
def view(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
123

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

David Haynes's avatar
David Haynes committed
127
    # Get the URL that is being requested
David Haynes's avatar
David Haynes committed
128
    url = get_object_or_404(URL, short__iexact = short)
129

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

David Haynes's avatar
David Haynes committed
137
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
138 139
    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
140 141 142
"""
@login_required
def my_links(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
143

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

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

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

    # Render my_links.html passing the list of URL's and Domain to the template
155
    return render(request, 'my_links.html', {
156 157
        'urls': urls,
        'domain': domain,
158 159 160
    },
    )

Jean Michel Rouly's avatar
Jean Michel Rouly committed
161

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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