Unverified Commit ef15de60 authored by David Haynes's avatar David Haynes
Browse files

Merge branch '2.2-dev' into 109-my-links-to-index

parents 2b95a071 da9d9ec7
Pipeline #1101 passed with stage
in 57 seconds
......@@ -15,3 +15,4 @@ venv
htmlcov/
.idea
__pycache__/
.vscode
\ No newline at end of file
Copyright 2016 George Mason Student-Run Computing and Technology
Copyright 2017 George Mason Student-Run Computing and Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......
......@@ -9,26 +9,29 @@ from django.contrib.auth.models import User
# App Imports
from go.models import URL, RegisteredUser
"""
Define what attributes display in the URL Admin
"""
class URLAdmin(admin.ModelAdmin):
"""
Define what attributes display in the URL Admin
"""
list_display = ("target", "short", "owner", "clicks", "date_created", "expires")
# Register URLAdmin
admin.site.register(URL, URLAdmin)
"""
Define an inline admin descriptor for User model
"""
class RegisteredUserInline(admin.StackedInline):
"""
Define an inline admin descriptor for User model
"""
model = RegisteredUser
can_delete = False
"""
Define a new User admin
"""
class UserAdmin(UserAdmin):
"""
Define a new User admin
"""
# see above class that we defined
inlines = (RegisteredUserInline, )
......
# Future Imports
from __future__ import unicode_literals, absolute_import, print_function, division
# Python stdlib Imports
from datetime import date, datetime, timedelta
from six.moves import urllib
# Django Imports
from django import forms
from django.core.exceptions import ValidationError
......@@ -15,19 +19,30 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, Submit, HTML, Div, Field
from crispy_forms.bootstrap import StrictButton, PrependedText, Accordion, AccordionGroup
from bootstrap3_datetime.widgets import DateTimePicker
from datetime import date, datetime, timedelta
"""
The form that is used in URL creation.
"""
class URLForm(forms.ModelForm):
"""
The form that is used in URL creation.
"""
# Prevent redirect loop links
def clean_target(self):
# get the entered target link
target = self.cleaned_data.get('target')
# if the host (go.gmu.edu) is in the entered target link
if self.host in target:
try:
final_url = urllib.request.urlopen(target).geturl()
# if visiting the provided url results in an HTTP error, or redirects
# to a page that results in an HTTP error
except urllib.error.URLError as e:
# to permit users to enter sites that return most errors, but
# prevent them from entering sites that result in an HTTP 300 error
if any(int(str(e)[11:14]) == errorNum for errorNum in range(300, 308)):
raise ValidationError("Link results in a 300 error")
else:
final_url = ""
# if the host (go.gmu.edu) is in the entered target link or where it
# redirects
if self.host in final_url or self.host in target:
raise ValidationError("You can't make a Go link to Go silly!")
else:
return target
......@@ -54,12 +69,12 @@ class URLForm(forms.ModelForm):
# Custom short-url field with validators.
short = forms.SlugField(
required = False,
label = 'Short URL (Optional)',
widget = forms.TextInput(),
validators = [unique_short],
max_length = 20,
min_length = 3,
required=False,
label='Short URL (Optional)',
widget=forms.TextInput(),
validators=[unique_short],
max_length=20,
min_length=3,
)
# define some string date standards
......@@ -80,11 +95,11 @@ class URLForm(forms.ModelForm):
# Add preset expiration choices.
expires = forms.ChoiceField(
required = True,
label = 'Expiration (Required)',
choices = EXPIRATION_CHOICES,
initial = NEVER,
widget = forms.RadioSelect(),
required=True,
label='Expiration (Required)',
choices=EXPIRATION_CHOICES,
initial=NEVER,
widget=forms.RadioSelect(),
)
# Check if the selected date is a valid date
......@@ -99,12 +114,12 @@ class URLForm(forms.ModelForm):
# Add a custom expiration choice.
expires_custom = forms.DateTimeField(
required = False,
label = 'Custom Date',
input_formats = ['%m-%d-%Y'],
validators = [valid_date],
initial = lambda: datetime.now() + timedelta(days=1),
widget = DateTimePicker(
required=False,
label='Custom Date',
input_formats=['%m-%d-%Y'],
validators=[valid_date],
initial=lambda: datetime.now() + timedelta(days=1),
widget=DateTimePicker(
options={
"format": "MM-DD-YYYY",
"pickTime": False,
......@@ -141,9 +156,9 @@ class URLForm(forms.ModelForm):
<h4>Paste the URL you would like to shorten:</h4>
<br />"""),
'target',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# Step 2: Short URL
AccordionGroup('Step 2: Short URL',
......@@ -153,9 +168,9 @@ class URLForm(forms.ModelForm):
<br />"""),
PrependedText(
'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
# Step 3: Expiration
AccordionGroup('Step 3: URL Expiration',
......@@ -165,12 +180,12 @@ class URLForm(forms.ModelForm):
<br />"""),
'expires',
Field('expires_custom', template="crispy/customDateField.html"),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# FIN
template='crispy/accordian.html'),
# FIN
template='crispy/accordian.html'),
#######################
HTML("""
<br />"""),
......@@ -181,42 +196,44 @@ class URLForm(forms.ModelForm):
# what model this form is for
model = URL
# what attributes are included
fields = ['target',]
fields = ['target']
"""
The form that is used when a user is signing up to be a RegisteredUser
"""
class SignupForm(forms.ModelForm):
"""
The form that is used when a user is signing up to be a RegisteredUser
"""
# The full name of the RegisteredUser
full_name = forms.CharField(
required = True,
label = 'Full Name (Required)',
max_length = 100,
widget = forms.TextInput(),
required=True,
label='Full Name (Required)',
max_length=100,
widget=forms.TextInput(),
)
# The RegisteredUser's chosen organization
organization = forms.CharField(
required = True,
label = 'Organization (Required)',
max_length = 100,
widget = forms.TextInput(),
required=True,
label='Organization (Required)',
max_length=100,
widget=forms.TextInput(),
)
# The RegisteredUser's reason for signing up to us Go
description = forms.CharField(
required = False,
label = 'Description (Optional)',
max_length = 200,
widget = forms.Textarea(),
required=False,
label='Description (Optional)',
max_length=200,
widget=forms.Textarea(),
)
# A user becomes registered when they agree to the TOS
registered = forms.BooleanField(
required=True,
# ***Need to replace lower url with production URL*** ie. go.gmu.edu/about#terms
label = mark_safe('Do you accept the <a href="http://127.0.0.1:8000/about#terms">Terms of Service</a>?'),
label=mark_safe(
'Do you accept the <a href="http://127.0.0.1:8000/about#terms">Terms of Service</a>?'
),
)
# on initialization of the form, crispy forms renders this layout
......@@ -249,4 +266,4 @@ class SignupForm(forms.ModelForm):
# what model this form is for
model = RegisteredUser
# what attributes are included
fields = ['full_name', 'organization', 'description', 'registered',]
fields = ['full_name', 'organization', 'description', 'registered']
......@@ -10,6 +10,10 @@ from go.models import URL
# Define a new custom django-admin command
class Command(BaseCommand):
"""
Remove expired links from the database
"""
# Define help text for this command
help = 'Removes expired links from the database'
......
......@@ -7,14 +7,15 @@ from django.test import TestCase
# App Imports
from .expirelinks import *
"""
Test cases for the functions in expirelinks
"""
class ExpireLinksTest(TestCase):
"""
Default test case, does not actually test anything
Test cases for the functions in expirelinks
"""
def test_Django_Test(self):
"""
Default test case, does not actually test anything
"""
self.assertEqual("Hello World!", "Hello World!")
# Future Imports
from __future__ import unicode_literals, absolute_import, print_function, division
# Python stdlib Imports
import string
# Django Imports
from django.db import models
from django.contrib.auth.models import User
......@@ -12,46 +15,45 @@ from django.utils.encoding import python_2_unicode_compatible
# Other Imports
from hashids import Hashids # http://hashids.org/python/
import string
# generate the salt and initialize Hashids
hashids = Hashids(salt="srct.gmu.edu", alphabet=(string.ascii_lowercase + string.digits))
"""
This is simply a wrapper model for the user object which, if an object
exists, indicates that that user is registered.
"""
@python_2_unicode_compatible
class RegisteredUser(models.Model):
# Is this User Blocked?
blocked = models.BooleanField(default = False)
"""
This is simply a wrapper model for the user object which, if an object
exists, indicates that that user is registered.
"""
# Let's associate a User to this RegisteredUser
user = models.OneToOneField(User)
# What is your name?
full_name = models.CharField(
blank = False,
max_length = 100,
blank=False,
max_length=100,
)
# What organization are you associated with?
organization = models.CharField(
blank = False,
max_length = 100,
blank=False,
max_length=100,
)
# Why do you want to use Go?
description = models.TextField(blank = True)
description = models.TextField(blank=True)
# Have you filled out the registration form?
registered = models.BooleanField(default = False)
registered = models.BooleanField(default=False)
# Are you approved to use Go?
approved = models.BooleanField(default = False)
approved = models.BooleanField(default=False)
# Is this User Blocked?
blocked = models.BooleanField(default=False)
# print(RegisteredUser)
# str(RegisteredUser)
def __str__(self):
return '<Registered User: %s - Approval Status: %s>' % (self.user, self.approved)
......@@ -64,37 +66,37 @@ def handle_regUser_creation(sender, instance, created, **kwargs):
RegisteredUser.objects.create(user=instance)
"""
This model represents a stored URL redirection rule. Each URL has an
owner, target url, short identifier, click counter, and expiration
date.
"""
@python_2_unicode_compatible
class URL(models.Model):
"""
This model represents a stored URL redirection rule. Each URL has an
owner, target url, short identifier, click counter, and expiration
date.
"""
# Who is the owner of this Go link
owner = models.ForeignKey(RegisteredUser)
# When was this link created?
date_created = models.DateTimeField(default = timezone.now)
date_created = models.DateTimeField(default=timezone.now)
# What is the target URL for this Go link
target = models.URLField(max_length = 1000)
target = models.URLField(max_length=1000)
# What is the actual go link (short url) for this URL
short = models.SlugField(max_length = 20, primary_key = True)
short = models.SlugField(max_length=20, primary_key=True)
# how many people have visited this Go link
clicks = models.IntegerField(default = 0)
clicks = models.IntegerField(default=0)
# how many people have visited this Go link through the qr code
qrclicks = models.IntegerField(default = 0)
qrclicks = models.IntegerField(default=0)
# how many people have visited the go link through social media
socialclicks = models.IntegerField(default = 0)
socialclicks = models.IntegerField(default=0)
# does this Go link expire on a certain date
expires = models.DateTimeField(blank = True, null = True)
expires = models.DateTimeField(blank=True, null=True)
# print(URL)
def __str__(self):
return '<%s : %s>' % (self.owner.user, self.target)
return '<Owner: %s - Target URL: %s>' % (self.owner.user, self.target)
# metadata for URL's
class Meta:
......
......@@ -2,9 +2,7 @@
{% extends 'layouts/base.html' %}
<!-- define the page title block -->
{% block title %}
SRCT Go &bull; Administration Panel
{% endblock %}
{% block title %} SRCT Go &bull; Administration Panel {% endblock %}
<!-- define the content block for the page -->
{% block content %}
......@@ -28,286 +26,291 @@ SRCT Go &bull; Administration Panel
<!-- Table 1 -->
<!-- define the div where we can select users from a table to judge them -->
<div class="row">
<div class="col-md-12">
<h3>Users awaiting moderation</h3>
<form method="post" action="useradmin">
<!-- csrf protection -->
{% csrf_token %}
<!-- define out table of users that need approval -->
<table class="table table-striped table-hover">
<!-- define the header row -->
<thead>
<tr>
<th>Selected</th>
<th>Username</th>
<th>Full Name</th>
<th>Description</th>
</tr>
</thead>
<div class="col-md-12">
<input class="inputfilter" type="text" id="appliedInput"
placeholder="Search Usernames or Full Names">
<h3>Users awaiting moderation</h3>
<form method="post" action="useradmin">
<!-- csrf protection -->
{% csrf_token %}
<!-- define out table of users that need approval -->
<table class="table table-striped table-hover" id="appliedTable">
<!-- define the header row -->
<thead>
<tr>
<th>Selected</th>
<th>Username</th>
<th>Full Name</th>
<th>Description</th>
</tr>
</thead>
<!-- define the body rows -->
<tbody>
<!-- loop through all users in the need_approval list -->
{% for unapproved in need_approval %}
<!-- ..and make a new row for each user -->
<tr>
<td><input type="checkbox" name="username" value={{ unapproved.user }}></td>
<td>{{ unapproved.user }}</td>
<td>{{ unapproved.full_name }}</td>
<td>{{ unapproved.description|default:"No description provided" }}</td>
</tr>
<!-- unless it's empty in which case we show nothing -->
{% empty %}
<tr>
<td>none</td>
<td>none</td>
<td>none</td>
<td>none</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- define the body rows -->
<tbody>
<!-- loop through all users in the need_approval list -->
{% for unapproved in need_approval %}
<!-- ..and make a new row for each user -->
<tr>
<td><input type="checkbox" name="username" value={{ unapproved.user }}></td>
<td>{{ unapproved.user }}</td>
<td>{{ unapproved.full_name }}</td>
<td>{{ unapproved.description|default:"No description provided" }}</td>
</tr>
<!-- unless it's empty in which case we show nothing -->
{% empty %}
<tr>
<td>none</td>
<td>none</td>
<td>none</td>
<td>none</td>
</tr>
{% endfor %}
</tbody>
</table>
<!-- a div containing our form submission buttons -->
<div class="form-group">
<!-- the modal for approve doesn't work-->
<a class="btn btn-primary btn-sm" data-target="#approveModal" data-toggle="modal"> Approve </a>
<!-- input type="submit" name="_approve" value="Approve" class="btn btn-primary btn-sm"-->
<a class="btn btn-danger btn-sm" data-target="#denyModal" data-toggle="modal"> Deny </a>
<a class="btn btn-default btn-sm btn-blockUsr" data-target="#blockModal" data-toggle="modal" style="background-color: black; color: white;"> Block </a>
<!-- a div containing our form submission buttons -->
<div class="form-group">
<!-- the modal for approve doesn't work-->
<a class="btn btn-primary btn-sm" data-target="#approveModal" data-toggle="modal"> Approve </a>
<!-- input type="submit" name="_approve" value="Approve" class="btn btn-primary btn-sm"-->
<a class="btn btn-danger btn-sm" data-target="#denyModal" data-toggle="modal"> Deny </a>
<a class="btn btn-default btn-sm btn-blockUsr" data-target="#blockModal" data-toggle="modal"
style="background-color: black; color: white;"> Block </a>
<!--Define Approve Modal-->
<div id="approveModal" class="modal fade" role="dialog" tabindex="-1">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" style="background-color:#f5f5f5; border-radius: 7px">
<div class="modal-header" style="text-align:center;">
<h4 class="modal-title text-center"
style="font-weight:bold; font-size: 21px !important;">
Are you sure you would like to approve a user?</h4>
</div>
<div class="modal-body" style="padding-bottom: 80px">
<a type="button" class="btn btn-success btn-lg" style="border-width: 0px;float:left; width:49%; background-color: #A9B0AD; color: #ffffff; border-radius: 4px;" data-dismiss="modal">Cancel</a>
<input type="submit" name="_approve" value="Approve" class="btn btn-primary btn-lg" style="border-width: 0px;float:right; width:49%; background-color: #00331a; color: #ffffff; border-radius: 4px;">
</div>
</div>
</div>
<!--Define Approve Modal-->
<div id="approveModal" class="modal fade" role="dialog" tabindex="-1">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" style="background-color:#f5f5f5; border-radius: 7px">
<div class="modal-header" style="text-align:center;">
<h4 class="modal-title text-center" style="font-weight:bold; font-size: 21px !important;">
Are you sure you would like to approve a user?</h4>
</div>
<!--Define Deny Modal-->
<div id="denyModal" class="modal fade" role="dialog" tabindex="-1">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" style="background-color:#f5f5f5; border-radius: 7px">
<div class="modal-header" style="text-align:center;">
<h4 class="modal-title text-center"
style="font-weight:bold; font-size: 21px !important;">
Are you sure you would like to deny a user?</h4>
<h6 style="font-weight:500; margin-top: 0px; margin-bottom: 0px;">
Please remember the user's feelings</h6>
</div>
<div class="modal-body" style="padding-bottom: 80px">
<a type="button" class="btn btn-success btn-lg" style="border-width: 0px;float:left; width:49%; background-color: #A9B0AD; color: #ffffff; border-radius: 4px;" data-dismiss="modal">Cancel</a>
<input type="submit" name="_deny" value="Deny" class="btn btn-danger btn-lg" style="border-width: 0px;float:right; width:49%; background-color: #ac1d37; color: #ffffff; border-radius: 4px;">
</div>
</div>
</div>
<div class="modal-body" style="padding-bottom: 80px">
<a type="button" class="btn btn-success btn-lg" style="border-width: 0px;float:left; width:49%; background-color: #A9B0AD; color: #ffffff; border-radius: 4px;"
data-dismiss="modal">Cancel</a>
<input type="submit" name="_approve" value="Approve" class="btn btn-primary btn-lg"
style="border-width: 0px;float:right; width:49%; background-color: #00331a; color: #ffffff; border-radius: 4px;">
</div>
</div>
</div>
</div>
<!--Define Block Modal-->
<!-- Note there is another block modal for the other block button below -->
<div id="blockModal" class="modal fade" role="dialog" tabindex="-1">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" style="background-color:#f5f5f5; border-radius: 7px">
<div class="modal-header" style="text-align:center;">
<h4 class="modal-title text-center"
style="font-weight:bold; font-size: 21px !important;">
Are you sure you would like to block a user?</h4>
<h6 style="font-weight:500; margin-top: 0px; margin-bottom: 0px;">
Please remember the user's feelings</h6>
</div>
<div class="modal-body" style="padding-bottom: 80px">
<a type="button" class="btn btn-success btn-lg" style="border-width: 0px;float:left; width:49%; background-color: #A9B0AD; color: #ffffff; border-radius: 4px;" data-dismiss="modal">Cancel</a>
<input type="submit" name="_block" value="Block" class="btn btn-danger btn-lg" style="border-width: 0px; float:right; width:49%;background-color: #000000; color: #ffffff; border-radius: 4px;">
</div>
</div>
</div>
<!--Define Deny Modal-->
<div id="denyModal" class="modal fade" role="dialog" tabindex="-1">
<div class="modal-dialog">
<!-- Modal content-->
<div class="modal-content" style="background-color:#f5f5f5; border-radius: 7px">
<div class="modal-header" style="text-align:center;">
<h4 class="modal-title text-center" style="font-weight:bold; font-size: 21px !important;">
Are you sure you would like to deny a user?</h4>
<h6 style="font-weight:500; margin-top: 0px; margin-bottom: 0px;">
Please remember the user's feelings</h6>
</div>
<div class="modal-body" style="padding-bottom: 80px">