views.py 18.2 KB
Newer Older
1
# Django Imports
2
from django.conf import settings
3
from django.http import HttpResponseServerError  # Http404
4
from django.http import HttpResponseRedirect
5
from django.utils import timezone
6
from django.core.exceptions import PermissionDenied  # ValidationError
7
from django.core.mail import send_mail, EmailMessage
8
from django.contrib.auth import REDIRECT_FIELD_NAME
David Haynes's avatar
David Haynes committed
9
from django.contrib.auth.models import User
10
from django.contrib.auth.decorators import user_passes_test, login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
11
from django.shortcuts import render, get_object_or_404, redirect
12

13 14 15 16 17 18
# 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
19

David Haynes's avatar
David Haynes committed
20
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
21 22 23 24 25
    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
26 27
"""
def index(request):
28
    # If the user is blocked, redirect them to the blocked page.
29
    # If the user is not authenticated, show them a public landing page.
30
    if not request.user.is_authenticated():
Zosman's avatar
Zosman committed
31
        return render(request, 'public_landing.html')
David Haynes's avatar
David Haynes committed
32
    # If the user isn't approved, then display the you're not approved page.
33
    elif not request.user.registereduser.approved:
34
        if request.user.registereduser.blocked:
Zosman's avatar
Zosman committed
35
            return render(request, 'banned.html')
36
        else:
Zosman's avatar
Zosman committed
37
            return render(request, 'not_registered.html')
38

39

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

David Haynes's avatar
David Haynes committed
43 44
    # 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
45
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
46 47 48 49 50
        # 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
51 52
        if url_form.is_valid():

Jean Michel Rouly's avatar
Jean Michel Rouly committed
53 54
            # We don't commit the url object yet because we need to add its
            # owner, and parse its date field.
55
            url = url_form.save(commit=False)
56
            url.owner = request.user.registereduser
57

Jean Michel Rouly's avatar
Jean Michel Rouly committed
58
            # If the user entered a short url, it's already been validated,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
59 60
            # so accept it. If they did not, however, then generate a
            # random one and use that instead.
61
            short = url_form.cleaned_data.get('short').strip()
David Haynes's avatar
David Haynes committed
62 63

            # Check if a short URL was entered
64 65 66
            if len(short) > 0:
                url.short = short
            else:
67 68 69 70 71 72
                # 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(
David Haynes's avatar
David Haynes committed
73
                        render(request, 'admin/500.html', {})
74 75 76
                    )
                else:
                    url.short = random_short
77

Jean Michel Rouly's avatar
Jean Michel Rouly committed
78 79 80
            # 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.
81
            expires = url_form.cleaned_data.get('expires')
82

David Haynes's avatar
David Haynes committed
83
            # Determine what the expiration date is
84 85 86 87 88 89
            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)
Matthew Rodgers's avatar
Matthew Rodgers committed
90 91
            elif expires == URLForm.CUSTOM:
                url.expires = url_form.cleaned_data.get('expires_custom')
92
            else:
93
                pass  # leave the field NULL
94

Jean Michel Rouly's avatar
Jean Michel Rouly committed
95 96
            # Make sure that our new URL object is clean, then save it and
            # let's redirect to view this baby.
97 98
            url.full_clean()
            url.save()
David Haynes's avatar
David Haynes committed
99

Jean Michel Rouly's avatar
Jean Michel Rouly committed
100
            return redirect('view', url.short)
101

David Haynes's avatar
David Haynes committed
102
    # Render index.html passing the form to the template
103
    return render(request, 'core/index.html', {
104
        'form': url_form,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
105 106 107
    },
    )

David Haynes's avatar
David Haynes committed
108
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
109 110
    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
111 112
"""
def view(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
113

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

David Haynes's avatar
David Haynes committed
117
    # Get the URL that is being requested
118
    url = get_object_or_404(URL, short__iexact=short)
119

David Haynes's avatar
David Haynes committed
120
    # Render view.html passing the specified URL and Domain to the template
Jean Michel Rouly's avatar
Jean Michel Rouly committed
121
    return render(request, 'view.html', {
122
        'url': url,
123
        'domain': domain,
124 125 126
    },
    )

David Haynes's avatar
David Haynes committed
127
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
128 129
    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
130 131 132
"""
@login_required
def my_links(request):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
133

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

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

David Haynes's avatar
David Haynes committed
141 142 143 144
    # Grab a list of all the URL's that are currently owned by the user
    urls = URL.objects.filter(owner=request.user.registereduser)

    # Render my_links.html passing the list of URL's and Domain to the template
145
    return render(request, 'my_links.html', {
146 147
        'urls': urls,
        'domain': domain,
148 149 150
    },
    )

Jean Michel Rouly's avatar
Jean Michel Rouly committed
151

David Haynes's avatar
David Haynes committed
152
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
153 154
    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
155 156 157
"""
@login_required
def delete(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
158

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

David Haynes's avatar
David Haynes committed
163
    # Get the URL that is going to be deleted
164
    url = get_object_or_404(URL, short__iexact=short)
David Haynes's avatar
David Haynes committed
165 166

    # If the RegisteredUser is the owner of the URL
167
    if url.owner == request.user.registereduser:
David Haynes's avatar
David Haynes committed
168
        # remove the URL
169
        url.delete()
David Haynes's avatar
David Haynes committed
170
        # rediret to my_links
171 172
        return redirect('my_links')
    else:
David Haynes's avatar
David Haynes committed
173
        # do not allow them to delete
174
        raise PermissionDenied()
175

David Haynes's avatar
David Haynes committed
176 177 178
"""
    This view presents the user with a registration form. You can register yourself.
"""
179
@login_required
Jean Michel Rouly's avatar
Jean Michel Rouly committed
180
def signup(request):
181
    # Do not display signup page to registered or approved users
182
    if request.user.registereduser.blocked:
183
        return render(request, 'banned.html')
184
    elif request.user.registereduser.approved:
David Haynes's avatar
David Haynes committed
185
        return redirect('/')
186
    elif request.user.registereduser.registered:
David Haynes's avatar
David Haynes committed
187
        return redirect('registered')
188

David Haynes's avatar
David Haynes committed
189
    # Initialize our signup form
190 191
    signup_form = SignupForm(request,
        initial={'full_name': request.user.first_name + " " + request.user.last_name})
David Haynes's avatar
David Haynes committed
192 193

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

David Haynes's avatar
David Haynes committed
196 197
    # 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
198
    if request.method == 'POST':
David Haynes's avatar
David Haynes committed
199 200
        # Now we initialize the form again but this time we have the POST
        # request
201 202
        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
203 204

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

David Haynes's avatar
David Haynes committed
207
        # Django will check the form to make sure it's valid
208
        if signup_form.is_valid():
David Haynes's avatar
David Haynes committed
209
            # Grab data from the form and store into variables
210
            description = signup_form.cleaned_data.get('description')
211
            full_name = signup_form.cleaned_data.get('full_name')
212
            organization = signup_form.cleaned_data.get('organization')
213
            registered = signup_form.cleaned_data.get('registered')
214

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

David Haynes's avatar
David Haynes committed
253 254
            # Make sure that our new RegisteredUser object is clean, then save
            # it and let's redirect to tell the user they have registered.
255
            signup_form.save()
256
            return redirect('registered')
Jean Michel Rouly's avatar
Jean Michel Rouly committed
257

David Haynes's avatar
David Haynes committed
258 259
    # render signup.html passing along the form and the current registered
    # status
260
    return render(request, 'core/signup.html', {
261
        'form': signup_form,
262
        'registered': False,
Jean Michel Rouly's avatar
Jean Michel Rouly committed
263 264
    },
    )
Jean Michel Rouly's avatar
Jean Michel Rouly committed
265

David Haynes's avatar
David Haynes committed
266
"""
Jean Michel Rouly's avatar
Jean Michel Rouly committed
267
    This view redirects a user based on the short URL they requested.
David Haynes's avatar
David Haynes committed
268 269
"""
def redirection(request, short):
Jean Michel Rouly's avatar
Jean Michel Rouly committed
270

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

David Haynes's avatar
David Haynes committed
274
    # Get the URL object that relates to the requested Go link
275
    url = get_object_or_404(URL, short__iexact=short)
David Haynes's avatar
David Haynes committed
276 277
    # Increment our clicks by one
    url.clicks += 1
278

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

David Haynes's avatar
David Haynes committed
283
    # If the user is coming from a QR request then increment qrclicks
284 285 286
    if 'qr' in request.GET:
        url.qrclicks += 1

David Haynes's avatar
David Haynes committed
287
    # If the user is coming from a social media request then increment qrclicks
288 289 290
    if 'social' in request.GET:
        url.socialclicks += 1

David Haynes's avatar
David Haynes committed
291
    # Save our data and redirect the user towards thier destination
Jean Michel Rouly's avatar
Jean Michel Rouly committed
292
    url.save()
293
    return redirect(url.target)
Jean Michel Rouly's avatar
Jean Michel Rouly committed
294

295 296 297 298
"""
    Decorator function for views that checks that the user is logged in and is
    a staff member, displaying the login page if necessary.
"""
299
def staff_member_required(view_func, redirect_field_name=REDIRECT_FIELD_NAME, login_url='/'):
300 301 302 303 304 305
    return user_passes_test(
        lambda u: u.is_active and u.is_staff,
        login_url=login_url,
        redirect_field_name=redirect_field_name
    )(view_func)

David Haynes's avatar
David Haynes committed
306
"""
307 308
    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
309 310 311 312 313
"""
@staff_member_required
def useradmin(request):

    # If we receive a POST request
314
    if request.POST:
David Haynes's avatar
David Haynes committed
315
        # Get a list of the potential victims (users)
316
        userlist = request.POST.getlist('username')
David Haynes's avatar
David Haynes committed
317
        # If we're approving users
318
        if '_approve' in request.POST:
319
            for name in userlist:
320 321 322
                toApprove = RegisteredUser.objects.get(user__username__exact=name)
                toApprove.approved = True
                toApprove.save()
David Haynes's avatar
David Haynes committed
323 324

                # Send an email letting them know they are approved
325
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
326
                    user_mail = toApprove.user.username + settings.EMAIL_DOMAIN
327 328 329 330 331 332 333 334
                    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'
335
                        % (str(toApprove.full_name)),
336 337 338 339
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
Zosman's avatar
Zosman committed
340

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

366
        # If we're blocking users
Zosman's avatar
draft 1  
Zosman committed
367 368
        elif '_block' in request.POST:
            for name in userlist:
369
                toBlock = RegisteredUser.objects.get(user__username__exact=name)
Zosman's avatar
draft 1  
Zosman committed
370
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
371
                    user_mail = toBlock.user.username + settings.EMAIL_DOMAIN
Zosman's avatar
draft 1  
Zosman committed
372 373 374 375 376 377 378 379 380
                    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'
381
                        % (str(toBlock.full_name)),
Zosman's avatar
draft 1  
Zosman committed
382 383 384 385
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
386 387 388
                # toBlock.user.delete()
                toBlock.blocked = True
                toBlock.save()
Zosman's avatar
Zosman committed
389

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

415
        # If we're removing existing users
416 417
        elif '_remove' in request.POST:
            for name in userlist:
418
                toRemove = RegisteredUser.objects.get(user__username__exact=name)
419
                if settings.EMAIL_HOST and settings.EMAIL_PORT:
420
                    user_mail = toRemove.user.username + settings.EMAIL_DOMAIN
421 422 423 424 425 426 427 428
                    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'
429
                        % (str(toRemove.full_name)),
430 431 432 433
                        ######################
                        settings.EMAIL_FROM,
                        [user_mail]
                    )
434
                toRemove.user.delete()
435
                return HttpResponseRedirect('useradmin')
Zosman's avatar
Zosman committed
436

437
    # Get a list of all RegisteredUsers that need to be approved
Zosman's avatar
Zosman committed
438
    need_approval = RegisteredUser.objects.filter(registered=True).filter(approved=False).filter(blocked=False)
Zosman's avatar
Zosman committed
439
    # Get a list of all RegisteredUsers that are currently users
440
    current_users = RegisteredUser.objects.filter(approved=True).filter(registered=True).filter(blocked=False)
Zosman's avatar
Zosman committed
441
    # Get a list of all RegisteredUsers that are blocked
442
    blocked_users = RegisteredUser.objects.filter(blocked=True)
443

David Haynes's avatar
David Haynes committed
444
    # Pass that list to the template
445
    return render(request, 'admin/useradmin.html', {
446 447 448
        'need_approval': need_approval,
        'current_users': current_users,
        'blocked_users': blocked_users
449 450
    },
    )