views.py 19.1 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
def index(request):
25 26 27 28 29 30 31 32
    """
        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.
    """

33
    # If the user is blocked, redirect them to the blocked page.
34
    # If the user is not authenticated, show them a public landing page.
35
    if not request.user.is_authenticated():
Zosman's avatar
Zosman committed
36
        return render(request, 'public_landing.html')
David Haynes's avatar
David Haynes committed
37
    # If the user isn't approved, then display the you're not approved page.
38
    elif not request.user.registereduser.approved:
39
        if request.user.registereduser.blocked:
Zosman's avatar
Zosman committed
40
            return render(request, 'banned.html')
41
        else:
Zosman's avatar
Zosman committed
42
            return render(request, 'not_registered.html')
43

44

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

David Haynes's avatar
David Haynes committed
48 49
    # 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
50
    if request.method == 'POST':
51 52
        # Now we initialize the form again but this time we have the POST
        # request
53
        url_form = URLForm(request.POST, host=request.META.get('HTTP_HOST'))
54 55 56

        # Django will check the form to make sure it's valid
        if url_form.is_valid():
57
            # Call our post method to assemble our new URL object
58
            res = post(request, url_form)
59

60 61 62 63 64 65
            # If there is a 500 error returned, handle it
            if res == 500:
                return HttpResponseServerError(
                    render(request, 'admin/500.html', {})
                )

66
            # Redirect to the shiny new URL
67
            return redirect('view', res.short)
68

69 70 71 72 73
        # 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,
74
            })
75

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,
80
    })
Jean Michel Rouly's avatar
Jean Michel Rouly committed
81

82 83 84
#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)
85
def post(request, url_form):
86 87 88 89
    """
        function that handles POST requests for the URL creation ProcessLookupError
    """

90 91
    # We don't commit the url object yet because we need to add its
    # owner, and parse its date field.
92
    url = url_form.save(commit=False)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
    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
111
        else:
112 113 114 115 116 117 118 119 120
            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:
121
        url.expires = timezone.now() + timedelta(days=1)
122
    elif expires == URLForm.WEEK:
123
        url.expires = timezone.now() + timedelta(weeks=1)
124
    elif expires == URLForm.MONTH:
125
        url.expires = timezone.now() + timedelta(weeks=3)
126 127 128 129 130 131 132 133 134 135
    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
136

David Haynes's avatar
David Haynes committed
137
def view(request, short):
138 139 140 141
    """
        This view allows the user to view details about a URL. Note that they
        do not need to be logged in to view info.
    """
Jean Michel Rouly's avatar
Jean Michel Rouly committed
142

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

David Haynes's avatar
David Haynes committed
146
    # Get the URL that is being requested
147
    url = get_object_or_404(URL, short__iexact=short)
148

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

David Haynes's avatar
David Haynes committed
155 156
@login_required
def my_links(request):
157 158 159 160
    """
        This view displays all the information about all of your URLs. You
        obviously need to be logged in to view your URLs.
    """
Jean Michel Rouly's avatar
Jean Michel Rouly committed
161

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

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

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

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

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

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

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

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

202
@login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
203
def signup(request):
204 205 206 207
    """
        This view presents the user with a registration form. You can register yourself.
    """

208
    # Do not display signup page to registered or approved users
209
    if request.user.registereduser.blocked:
210
        return render(request, 'banned.html')
211
    elif request.user.registereduser.approved:
David Haynes's avatar
David Haynes committed
212
        return redirect('/')
213
    elif request.user.registereduser.registered:
David Haynes's avatar
David Haynes committed
214
        return redirect('registered')
215

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

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

David Haynes's avatar
David Haynes committed
227 228
    # 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
229
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
230 231
        # Now we initialize the form again but this time we have the POST
        # request
232 233 234 235 236 237
        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
238 239

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

David Haynes's avatar
David Haynes committed
242
        # Django will check the form to make sure it's valid
243
        if signup_form.is_valid():
David Haynes's avatar
David Haynes committed
244
            # Grab data from the form and store into variables
245
            description = signup_form.cleaned_data.get('description')
246
            full_name = signup_form.cleaned_data.get('full_name')
247
            organization = signup_form.cleaned_data.get('organization')
248

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

David Haynes's avatar
David Haynes committed
291 292
            # Make sure that our new RegisteredUser object is clean, then save
            # it and let's redirect to tell the user they have registered.
293
            signup_form.save()
294
            return redirect('registered')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
295

David Haynes's avatar
David Haynes committed
296 297
    # render signup.html passing along the form and the current registered
    # status
298
    return render(request, 'core/signup.html', {
299
        'form': signup_form,
300
        'registered': False,
301
    })
Jean Michel Rouly's avatar
Jean Michel Rouly committed
302

David Haynes's avatar
David Haynes committed
303
def redirection(request, short):
304 305 306
    """
        This view redirects a user based on the short URL they requested.
    """
Jean Michel Rouly's avatar
Jean Michel Rouly committed
307

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

David Haynes's avatar
David Haynes committed
311
    # Get the URL object that relates to the requested Go link
312
    url = get_object_or_404(URL, short__iexact=short)
David Haynes's avatar
David Haynes committed
313 314
    # Increment our clicks by one
    url.clicks += 1
315

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

David Haynes's avatar
David Haynes committed
320
    # If the user is coming from a QR request then increment qrclicks
321 322 323
    if 'qr' in request.GET:
        url.qrclicks += 1

David Haynes's avatar
David Haynes committed
324
    # If the user is coming from a social media request then increment qrclicks
325 326 327
    if 'social' in request.GET:
        url.socialclicks += 1

David Haynes's avatar
David Haynes committed
328
    # Save our data and redirect the user towards thier destination
Jean Michel Rouly's avatar
Jean Michel Rouly committed
329
    url.save()
330
    return redirect(url.target)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
331

332 333 334 335 336 337
def staff_member_required(view_func, redirect_field_name=REDIRECT_FIELD_NAME, login_url='/'):
    """
        Decorator function for views that checks that the user is logged in and is
        a staff member, displaying the login page if necessary.
    """

338 339
    return user_passes_test(
        lambda u: u.is_active and u.is_staff,
David Haynes's avatar
David Haynes committed
340 341
        login_url = login_url,
        redirect_field_name = redirect_field_name
342 343
    )(view_func)

David Haynes's avatar
David Haynes committed
344 345
@staff_member_required
def useradmin(request):
346 347 348 349
    """
        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
350 351

    # If we receive a POST request
352
    if request.POST:
David Haynes's avatar
David Haynes committed
353
        # Get a list of the potential victims (users)
354
        userlist = request.POST.getlist('username')
David Haynes's avatar
David Haynes committed
355
        # If we're approving users
356
        if '_approve' in request.POST:
357
            for name in userlist:
358 359 360
                to_approve = RegisteredUser.objects.get(user__username__exact=name)
                to_approve.approved = True
                to_approve.save()
David Haynes's avatar
David Haynes committed
361 362

                # Send an email letting them know they are approved
363
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
364
                    user_mail = to_approve.user.username + settings.EMAIL_DOMAIN
365 366 367 368 369 370 371 372
                    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'
373
                        % (str(to_approve.full_name)),
374 375 376 377
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
Zosman's avatar
Zosman committed
378

David Haynes's avatar
David Haynes committed
379
        # If we're denying users
380
        elif '_deny' in request.POST:
381
            for name in userlist:
382
                to_deny = RegisteredUser.objects.get(user__username__exact=name)
383
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
384
                    user_mail = to_deny.user.username + settings.EMAIL_DOMAIN
David Haynes's avatar
David Haynes committed
385
                    # Send an email letting them know they are denied
386 387 388 389 390 391 392 393 394
                    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'
395
                        % (str(to_deny.full_name)),
396 397 398 399
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
David Haynes's avatar
David Haynes committed
400
                # Delete their associated RegisteredUsers
401
                to_deny.user.delete()
402
                return HttpResponseRedirect('useradmin')
Zosman's avatar
Zosman committed
403

404
        # If we're blocking users
Zosman's avatar
draft 1  
Zosman committed
405 406
        elif '_block' in request.POST:
            for name in userlist:
407
                to_block = RegisteredUser.objects.get(user__username__exact=name)
Zosman's avatar
draft 1  
Zosman committed
408
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
409
                    user_mail = to_block.user.username + settings.EMAIL_DOMAIN
Zosman's avatar
draft 1  
Zosman committed
410 411 412 413 414 415 416 417 418
                    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'
419
                        % (str(to_block.full_name)),
Zosman's avatar
draft 1  
Zosman committed
420 421 422 423
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
424 425 426 427
                to_block.blocked = True
                to_block.approved = False
                to_block.registered = False
                to_block.save()
Zosman's avatar
Zosman committed
428

429
        # If we're un-blocking users
430 431
        elif '_unblock' in request.POST:
            for name in userlist:
432
                to_un_block = RegisteredUser.objects.get(user__username__exact=name)
433
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
434
                    user_mail = to_un_block.user.username + settings.EMAIL_DOMAIN
435
                    send_mail(
436
                        'Your Account has been Un-Blocked!',
437 438 439
                        ######################
                        'Hey there %s,\n\n'
                        'The Go admins have reviewed your application and have '
440
                        'Un-Blocked you from using Go.\n\n'
441
                        'If you wish to continue Go use please register again. \n\n'
442 443
                        'Congratulations! '
                        '- Go Admins'
444
                        % (str(to_un_block.full_name)),
445 446 447 448
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
449 450
                to_un_block.blocked = False
                to_un_block.save()
451
                return HttpResponseRedirect('useradmin')
452

453
        # If we're removing existing users
454 455
        elif '_remove' in request.POST:
            for name in userlist:
456
                to_remove = RegisteredUser.objects.get(user__username__exact=name)
457
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
458
                    user_mail = to_remove.user.username + settings.EMAIL_DOMAIN
459 460 461 462 463 464 465 466
                    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'
467
                        % (str(to_remove.full_name)),
468 469 470 471
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
472
                to_remove.user.delete()
473
                return HttpResponseRedirect('useradmin')
474

475
    # Get a list of all RegisteredUsers that need to be approved
476 477
    need_approval = RegisteredUser.objects.filter(registered=True).filter(
        approved=False).filter(blocked=False)
Zosman's avatar
Zosman committed
478
    # Get a list of all RegisteredUsers that are currently users
479 480
    current_users = RegisteredUser.objects.filter(approved=True).filter(
        registered=True).filter(blocked=False)
Zosman's avatar
Zosman committed
481
    # Get a list of all RegisteredUsers that are blocked
482
    blocked_users = RegisteredUser.objects.filter(blocked=True)
483

David Haynes's avatar
David Haynes committed
484
    # Pass that list to the template
485
    return render(request, 'admin/useradmin.html', {
486 487 488
        'need_approval': need_approval,
        'current_users': current_users,
        'blocked_users': blocked_users
David Haynes's avatar
David Haynes committed
489
    })