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

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

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

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

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

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

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

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

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

Jean Michel Rouly's avatar
Jean Michel Rouly committed
165

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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