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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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