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
@login_required
Zach Knox's avatar
Zach Knox committed
32
def new_link(request):
David Haynes's avatar
David Haynes committed
33
    # If the user isn't approved, then display the you're not approved page.
34
    if not request.user.registereduser.approved:
35
        if request.user.registereduser.blocked:
Zosman's avatar
Zosman committed
36
            return render(request, 'banned.html')
37
        else:
Zosman's avatar
Zosman committed
38
            return render(request, 'not_registered.html')
39

40

David Haynes's avatar
David Haynes committed
41
    # Initialize a URL form
42
    url_form = URLForm(host = request.META.get('HTTP_HOST'))  # unbound form
43

David Haynes's avatar
David Haynes committed
44 45
    # 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
46
    if request.method == 'POST':
47
        return redirect('view', post(request).short)
48

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

Zach Knox's avatar
Zach Knox committed
55 56 57 58
# for compatibility, just in case
def my_links(request):
    return index(request)

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
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
141

142 143 144 145
def index(request):
    # If the user is not authenticated, show them a public landing page.
    if not request.user.is_authenticated():
        return render(request, 'public_landing.html')
David Haynes's avatar
David Haynes committed
146
    # Do not display this page to unapproved users
147
    if not request.user.registereduser.approved:
148
        return render(request, 'not_registered.html')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
149

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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