Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
go
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
21
Issues
21
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
SRCT
go
Commits
34623c47
Commit
34623c47
authored
Jul 29, 2018
by
David Haynes
🙆
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '173-api-create-links' into 'go-three'
Resolve "API endpoint for creating links" See merge request
!123
parents
02560ef1
84f7d732
Pipeline
#2638
passed with stage
in 51 seconds
Changes
7
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
63 additions
and
726 deletions
+63
-726
docker-compose.yml
docker-compose.yml
+0
-1
docker-startup.sh
docker-startup.sh
+1
-3
go/go/cas_callbacks.py
go/go/cas_callbacks.py
+22
-24
go/go/models.py
go/go/models.py
+1
-1
go/go/serializers.py
go/go/serializers.py
+7
-10
go/go/views.py
go/go/views.py
+18
-646
go/settings/urls.py
go/settings/urls.py
+14
-41
No files found.
docker-compose.yml
View file @
34623c47
...
...
@@ -36,4 +36,3 @@ services:
-
GO_EMAIL_HOST_PASSWORD=
-
GO_EMAIL_FROM=
-
GO_EMAIL_TO=
-
SUPERUSER=dhaynes3
docker-startup.sh
View file @
34623c47
...
...
@@ -8,11 +8,9 @@ export GO_SECRET_KEY
export
GO_CREATE_SUPERUSER
GO_SECRET_KEY
=
$(
dd
if
=
/dev/urandom
count
=
100 |
tr
-dc
"A-Za-z0-9"
|
fold
-w
60 |
head
-n1
2>/dev/null
)
GO_CREATE_SUPERUSER
=
"from django.contrib.auth import get_user_model; User = get_user_model(); me = User.objects.get(username='
$SUPERUSER
'); me.first_name = 'David'; me.last_name = 'Haynes'; me.save(); "
python go/manage.py makemigrations
python go/manage.py makemigrations go
python go/manage.py migrate
python go/manage.py createsuperuser
--noinput
--username
=
"
$SUPERUSER
"
--email
=
"
$SUPERUSER$GO_EMAIL_DOMAIN
"
echo
"
$GO_CREATE_SUPERUSER
"
| python go/manage.py shell
python go/manage.py runserver 0.0.0.0:8000
\ No newline at end of file
go/go/cas_callbacks.py
View file @
34623c47
...
...
@@ -5,6 +5,8 @@ Parse the CAS/PF responses and create users in the database.
"""
# Other Imports
import
requests
import
os
# Django Imports
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
...
...
@@ -77,36 +79,32 @@ def create_user(tree: list):
Create a django user based off of the peoplefinder info we parsed earlier.
"""
print
(
"Parsing CAS information."
)
try
:
username
=
tree
[
0
][
0
].
text
user
,
user_created
=
User
.
objects
.
get_or_create
(
username
=
username
)
except
Exception
as
ex
:
print
(
"CAS callback unsuccessful:"
,
ex
)
# error handling in pfinfo function
info_name
=
pfinfo
(
username
)
# set and save the user's email
email_str
=
"%s%s"
%
(
username
,
settings
.
EMAIL_DOMAIN
)
# error handling in pfinfo function
info_name
=
pfinfo
(
username
)
user
,
user_created
=
User
.
objects
.
get_or_create
(
username
=
username
,
email
=
email_str
,
first_name
=
info_name
[
0
],
last_name
=
info_name
[
1
]
)
# Password is a required User object field, though doesn't matter for our
# purposes because all user auth is handled through CAS, not Django's login.
user
.
set_password
(
'cas_used_instead'
)
if
os
.
environ
[
'GO_ENV'
]
!=
'production'
:
user
.
is_staff
=
True
user
.
is_superuser
=
True
user
.
save
()
try
:
if
user_created
:
print
(
"Created user object %s."
%
username
)
# set and save the user's email
email_str
=
"%s%s"
%
(
username
,
settings
.
EMAIL_DOMAIN
)
user
.
email
=
email_str
# Password is a required User object field, though doesn't matter for our
# purposes because all user auth is handled through CAS, not Django's login.
user
.
set_password
(
'cas_used_instead'
)
user
.
save
()
print
(
"Added user's email, %s."
%
email_str
)
user
.
first_name
=
info_name
[
0
]
user
.
last_name
=
info_name
[
1
]
user
.
save
()
print
(
"Added user's name, %s %s."
%
(
info_name
[
0
],
info_name
[
1
]))
print
(
"User object creation process completed."
)
else
:
print
(
"User object already exists."
)
print
(
"CAS callback successful."
)
except
Exception
as
ex
:
print
(
"
Unhandled user creation error
:"
,
ex
)
print
(
"
CAS callback unsuccessful
:"
,
ex
)
go/go/models.py
View file @
34623c47
...
...
@@ -90,7 +90,7 @@ def handle_reguser_creation(sender, instance, created, **kwargs):
is called to create an associated RegisteredUser.
"""
if
created
:
RegisteredUser
.
objects
.
create
(
user
=
instance
)
RegisteredUser
.
objects
.
create
(
user
=
instance
,
full_name
=
instance
.
get_full_name
()
)
@
receiver
(
post_save
,
sender
=
settings
.
AUTH_USER_MODEL
)
def
create_auth_token
(
sender
,
instance
=
None
,
created
=
False
,
**
kwargs
):
...
...
go/go/serializers.py
View file @
34623c47
...
...
@@ -3,16 +3,13 @@ go/serializers.py
Define how data is translated from the database to json/API representation.
"""
from
django.contrib.auth.models
import
User
,
Group
from
rest_framework
import
serializers
class
UserSerializer
(
serializers
.
HyperlinkedModelSerializer
):
class
Meta
:
model
=
User
fields
=
(
'url'
,
'username'
,
'email'
,
'groups'
)
# App Imports
from
.models
import
URL
,
RegisteredUser
# Third Party Imports
from
rest_framework
import
serializers
class
Group
Serializer
(
serializers
.
HyperlinkedModelSerializer
):
class
URL
Serializer
(
serializers
.
HyperlinkedModelSerializer
):
class
Meta
:
model
=
Group
fields
=
(
'
url'
,
'name
'
)
model
=
URL
fields
=
(
'
destination'
,
'short'
,
'date_expires
'
)
go/go/views.py
View file @
34623c47
...
...
@@ -27,657 +27,29 @@ from .models import URL, RegisteredUser
from
django.contrib.auth.models
import
User
,
Group
from
rest_framework
import
viewsets
from
.serializers
import
UserSerializer
,
GroupSerializer
class
UserViewSet
(
viewsets
.
ModelViewSet
):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset
=
User
.
objects
.
all
().
order_by
(
'-date_joined'
)
serializer_class
=
UserSerializer
class
GroupViewSet
(
viewsets
.
ModelViewSet
):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset
=
Group
.
objects
.
all
()
serializer_class
=
GroupSerializer
def
index
(
request
):
"""
If a user is logged in, this view displays all the information about all
of their URLs. Otherwise, it will show the public landing page.
"""
if
not
request
.
user
.
is_authenticated
:
return
render
(
request
,
'landing.html'
)
if
not
request
.
user
.
registereduser
.
approved
:
return
render
(
request
,
'not_registered.html'
)
# List of sort methods and their display name "Column" : "Name"
SORT_METHODS
=
{
"-date_created"
:
"Most Recent"
,
"date_created"
:
"Oldest"
,
"short"
:
"Alphabetical (A-Z)"
,
"-short"
:
"Alphabetical (Z-A)"
,
"-clicks"
:
"Most Popular"
,
"clicks"
:
"Least Popular"
,
"-expires"
:
"Expiring Soon"
}
# Get the requested sort method, default to "-date_created" : "Most Recent"
sort_method
=
request
.
GET
.
get
(
'sort'
,
'-date_created'
)
# Get the current domain info
domain
=
"%ss://%s"
%
(
request
.
scheme
,
request
.
META
.
get
(
'HTTP_HOST'
))
+
"/"
# Grab a list of all the URLs that are currently owned by the user
urls
=
URL
.
objects
.
filter
(
owner
=
request
.
user
.
registereduser
)
# Check if provided sort method is valid, otherwise default
if
sort_method
in
SORT_METHODS
:
urls
=
urls
.
order_by
(
sort_method
)
else
:
urls
=
urls
.
order_by
(
"-date_created"
)
# Render my_links passing the list of URLs, Domain, and Sort Methods to
# the template
return
render
(
request
,
'core/index.html'
,
{
'urls'
:
urls
,
'domain'
:
domain
,
'sort_methods'
:
SORT_METHODS
})
@
login_required
def
new_link
(
request
):
"""
This view handles the homepage that the user is presented with when
they request '/newLink'. 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.
"""
# If the user isn't approved, then display the you're not approved page.
if
not
request
.
user
.
registereduser
.
approved
:
if
request
.
user
.
registereduser
.
blocked
:
return
render
(
request
,
'banned.html'
)
else
:
return
render
(
request
,
'not_registered.html'
)
# Initialize a URL form
url_form
=
URLForm
(
host
=
request
.
META
.
get
(
'HTTP_HOST'
))
# unbound form
# 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
if
request
.
method
==
'POST'
:
# 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
():
# Call our post method to assemble our new URL object
res
=
post
(
request
,
url_form
)
# 500 error
if
res
==
500
:
return
HttpResponseServerError
(
render
(
request
,
'500.html'
))
# Redirect to the shiny new URL
return
redirect
(
'view'
,
res
.
short
)
# Else, there is an error, redisplay the form with the validation
# errors
else
:
# Render index.html passing the form to the template
return
render
(
request
,
'core/new.html'
,
{
'form'
:
url_form
,
})
# Render index.html passing the form to the template
return
render
(
request
,
'core/new.html'
,
{
'form'
:
url_form
,
})
from
rest_framework
import
permissions
from
.serializers
import
URLSerializer
class
URLPermission
(
permissions
.
BasePermission
):
message
=
"You do not have the necessary approvals to perform that action."
def
has_permission
(
self
,
request
,
view
):
return
request
.
user
.
registereduser
.
approved
or
request
.
user
.
is_staff
@
login_required
def
my_links
(
request
):
"""
for compatibility, just in case
shows the same thing as /, but requires login to be consistent with
/newLink
"""
if
not
request
.
user
.
registereduser
.
approved
:
if
request
.
user
.
registereduser
.
blocked
:
return
render
(
request
,
'banned.html'
)
else
:
return
render
(
request
,
'not_registered.html'
)
return
index
(
request
)
# 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
,
url_form
):
"""
Helper function that handles POST requests for the URL creation
"""
# 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
500
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
def
has_object_permission
(
self
,
request
,
view
,
obj
):
return
obj
.
owner
==
request
.
user
.
registereduser
or
request
.
user
.
is_staff
def
view
(
request
,
shor
t
):
class
URLViewSet
(
viewsets
.
ModelViewSe
t
):
"""
This view allows the user to "view details" about a URL. Note that they
do not need to be logged in to view this information.
API endpoint that handles creation/read/update/deletion of URL objects.
"""
serializer_class
=
URLSerializer
permission_classes
=
(
URLPermission
,)
# Get the current domain info
domain
=
"%ss://%s"
%
(
request
.
scheme
,
request
.
META
.
get
(
'HTTP_HOST'
))
+
"/"
# Get the URL that is being requested
url
=
get_object_or_404
(
URL
,
short__iexact
=
short
)
# Render view.html passing the specified URL and Domain to the template
return
render
(
request
,
'view.html'
,
{
'url'
:
url
,
'domain'
:
domain
,
})
@
login_required
def
edit
(
request
,
short
):
"""
This view allows a logged in user to edit the details of a Go link that they
own. They can modify any value that they wish. If `short` is modified then
we will need to create a new link and copy over stats from the previous.
"""
# Do not allow unapproved users to edit links
if
not
request
.
user
.
registereduser
.
approved
:
if
request
.
user
.
registereduser
.
blocked
:
return
render
(
request
,
'banned.html'
)
else
:
return
render
(
request
,
'not_registered.html'
)
# Get the URL that is going to be edited
url
=
get_object_or_404
(
URL
,
short__iexact
=
short
)
# If the RegisteredUser is the owner of the URL
if
url
.
owner
==
request
.
user
.
registereduser
:
# If a POST request is received, then the user has submitted a form and it's
# time to parse the form and edit that URL object
if
request
.
method
==
'POST'
:
# Now we initialize the form again but this time we have the POST
# request
url_form
=
EditForm
(
request
.
POST
,
host
=
request
.
META
.
get
(
'HTTP_HOST'
))
# Make a copy of the old URL
copy
=
url
# Remove the old one
url
.
delete
()
# Django will check the form to make sure it's valid
if
url_form
.
is_valid
():
# If the short changed then we need to create a new object and
# migrate some data over
if
url_form
.
cleaned_data
.
get
(
'short'
).
strip
()
!=
copy
.
short
:
# Parse the form and create a new URL object
res
=
post
(
request
,
url_form
)
# If there is a 500 error returned, handle it
if
res
==
500
:
return
HttpResponseServerError
(
render
(
request
,
'500.html'
))
# We can procede with the editing process
else
:
# Migrate clicks data
res
.
clicks
=
copy
.
clicks
res
.
qrclicks
=
copy
.
qrclicks
res
.
socialclicks
=
copy
.
socialclicks
# Save the new URL
res
.
save
()
# Redirect to the shiny new *edited URL
return
redirect
(
'view'
,
res
.
short
)
# The short was not edited and thus, we can directly edit the url
else
:
if
url_form
.
cleaned_data
.
get
(
'destination'
).
strip
()
!=
copy
.
destination
:
copy
.
destination
=
url_form
.
cleaned_data
.
get
(
'destination'
).
strip
()
copy
.
save
()
# 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
:
edited_expires
=
timezone
.
now
()
+
timedelta
(
days
=
1
)
elif
expires
==
URLForm
.
WEEK
:
edited_expires
=
timezone
.
now
()
+
timedelta
(
weeks
=
1
)
elif
expires
==
URLForm
.
MONTH
:
edited_expires
=
timezone
.
now
()
+
timedelta
(
weeks
=
3
)
elif
expires
==
URLForm
.
CUSTOM
:
edited_expires
=
url_form
.
cleaned_data
.
get
(
'expires_custom'
)
else
:
pass
# leave the field NULL
if
edited_expires
!=
copy
.
expires
:
copy
.
expires
=
edited_expires
copy
.
save
()
# Redirect to the shiny new *edited URL
return
redirect
(
'view'
,
copy
.
short
)
# Else, there is an error, redisplay the form with the validation errors
else
:
# Render index.html passing the form to the template
return
render
(
request
,
'core/edit_link.html'
,
{
'form'
:
url_form
})
def
get_queryset
(
self
):
if
not
self
.
request
.
user
.
is_staff
:
return
URL
.
objects
.
filter
(
owner
=
self
.
request
.
user
.
registereduser
)
else
:
# Initial data set here
if
url
.
expires
!=
None
:
# Initialize a URL form with an expire date
url_form
=
EditForm
(
host
=
request
.
META
.
get
(
'HTTP_HOST'
),
initial
=
{
'destination'
:
url
.
destination
,
'short'
:
url
.
short
,
'expires'
:
'Custom Date'
,
'expires_custom'
:
url
.
expires
})
# unbound form
else
:
# Initialize a URL form without an expire date
url_form
=
EditForm
(
host
=
request
.
META
.
get
(
'HTTP_HOST'
),
initial
=
{
'destination'
:
url
.
destination
,
'short'
:
url
.
short
,
'expires'
:
'Never'
,
})
# unbound form
# Render index.html passing the form to the template
return
render
(
request
,
'core/edit_link.html'
,
{
'form'
:
url_form
})
else
:
# do not allow them to edit
raise
PermissionDenied
()
@
login_required
def
delete
(
request
,
short
):
"""
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.
"""
# Do not allow unapproved users to delete links
if
not
request
.
user
.
registereduser
.
approved
:
return
render
(
request
,
'not_registered.html'
)
# Get the URL that is going to be deleted
url
=
get_object_or_404
(
URL
,
short__iexact
=
short
)
# If the RegisteredUser is the owner of the URL
if
url
.
owner
==
request
.
user
.
registereduser
:
# remove the URL
url
.
delete
()
# redirect to my_links
return
redirect
(
'my_links'
)
else
:
# do not allow them to delete
raise
PermissionDenied
()
@
login_required
def
signup
(
request
):
"""
This view presents the user with a registration form. You can register
yourself.
"""
# Do not display signup page to registered or approved users
if
request
.
user
.
registereduser
.
blocked
:
return
render
(
request
,
'banned.html'
)
elif
request
.
user
.
registereduser
.
approved
:
return
redirect
(
'/'
)
elif
request
.
user
.
registereduser
.
registered
:
return
redirect
(
'registered'
)
# Initialize our signup form
signup_form
=
SignupForm
(
request
,
initial
=
{
'full_name'
:
request
.
user
.
first_name
+
" "
+
request
.
user
.
last_name
}
)
# Set the full_name field to readonly since CAS will fill that in for them
signup_form
.
fields
[
'full_name'
].
widget
.
attrs
[
'readonly'
]
=
'readonly'
# 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
if
request
.
method
==
'POST'
:
# Now we initialize the form again but this time we have the POST
# request
signup_form
=
SignupForm
(
request
,
request
.
POST
,
instance
=
request
.
user
.
registereduser
,
initial
=
{
'full_name'
:
request
.
user
.
first_name
+
" "
+
request
.
user
.
last_name
}
)
# set the readonly flag again for good measure
signup_form
.
fields
[
'full_name'
].
widget
.
attrs
[
'readonly'
]
=
'readonly'
# Django will check the form to make sure it's valid
if
signup_form
.
is_valid
():
# Grab data from the form and store into variables
description
=
signup_form
.
cleaned_data
.
get
(
'description'
)
full_name
=
signup_form
.
cleaned_data
.
get
(
'full_name'
)
organization
=
signup_form
.
cleaned_data
.
get
(
'organization'
)
# Only send mail if we've defined the mailserver
if
settings
.
EMAIL_HOST
and
settings
.
EMAIL_PORT
:
user_mail
=
request
.
user
.
username
+
settings
.
EMAIL_DOMAIN
# Email sent to notify Admins
to_admin
=
EmailMessage
(
'Signup from %s'
%
(
request
.
user
.
registereduser
.
user
),
######################
"""
%s signed up at %s
\n\n
Username: %s
\n
Organization: %s
\n\n
Message: %s
\n\n
You can contact the user directly by replying to this email or reply all to contact the user and notify the mailing list.
\n
Please head to go.gmu.edu/manage to approve or deny this application.'
"""
%
(
str
(
full_name
),
str
(
timezone
.
now
()).
strip
(),
str
(
request
.
user
.
registereduser
.
user
),
str
(
organization
),
str
(
description
)
),
######################
settings
.
EMAIL_FROM
,
[
settings
.
EMAIL_TO
],
reply_to
=
[
user_mail
]
)
to_admin
.
send
()
# Confirmation email sent to Users
send_mail
(
'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
]
)
# Make sure that our new RegisteredUser object is clean, then save
# it and let's redirect to tell the user they have registered.
signup_form
.
save
()
return
redirect
(
'registered'
)
# render signup.html passing along the form and the current registered
# status
return
render
(
request
,
'core/signup.html'
,
{
'form'
:
signup_form
,
'registered'
:
False
,
})
def
redirection
(
request
,
short
):
"""
This view redirects a user based on the short URL they requested.
"""
# Get the current domain info
domain
=
"%s://%s"
%
(
request
.
scheme
,
request
.
META
.
get
(
'HTTP_HOST'
))
+
"/"
# Get the URL object that relates to the requested Go link
url
=
get_object_or_404
(
URL
,
short__iexact
=
short
)
# Increment our clicks by one
url
.
clicks
+=
1
# Get the URL short link
doesExist
=
URL
.
objects
.
get
(
short__iexact
=
short
)
# Checks to see if the link exists, if not we 404 the user.
if
doesExist
.
destination
is
None
:
return
redirect
(
'go/404.html'
)
# If the user is trying to make a Go link to itself, we 404 them
if
url
.
destination
==
domain
+
short
:
return
redirect
(
'404.html'
)
# If the user is coming from a QR request then increment qrclicks
if
'qr'
in
request
.
GET
:
url
.
qrclicks
+=
1
# If the user is coming from a social media request then increment qrclicks
if
'social'
in
request
.
GET
:
url
.
socialclicks
+=
1
# Save our data and redirect the user towards their destination
url
.
save
()
return
redirect
(
url
.
destination
)
def
staff_member_required
(
view_func
,
redirect_field_name
=
REDIRECT_FIELD_NAME
,
login_url
=
'/'
):
"""
Decorator function for views that checks that the user is logged in and is
a staff member, displaying the login page if necessary.
"""
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
)
@
staff_member_required
def
useradmin
(
request
):
"""
This view is a simplified admin panel, so that staff don't need to log in
to approve links
"""