Commit bce41124 authored by Zac Wood's avatar Zac Wood

V2.3

parent 823fe4ea
GO_ENV=development
GO_ALLOWED_HOSTS=*
GO_EMAIL_DOMAIN=@masonlive.gmu.edu
GO_CAS_URL=https://login.gmu.edu/
GO_DB_NAME=go
GO_DB_USER=go
GO_DB_PASSWORD=go
GO_DB_HOST=localhost
GO_DB_PORT=3306
GO_EMAIL_HOST=
GO_EMAIL_PORT=
GO_EMAIL_HOST_USER=
GO_EMAIL_HOST_PASSWORD=
GO_EMAIL_FROM=
GO_EMAIL_TO=
GO_SECRET_KEY=spookyspecret
GO_SLACK_URL=
superuser=zwood2
......@@ -5,6 +5,7 @@ services:
stages:
- test
- lint
- build
variables:
MYSQL_DATABASE: go
......@@ -49,3 +50,15 @@ Go-flake8:
script:
- pip install flake8
- flake8 go/ --statistics --exit-zero
build:
stage: build
retry: 2
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile.prod --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
only:
- tags
FROM python:3.6
FROM python:3.6.9
ENV PYTHONUNBUFFERED 1
RUN apt-get update
RUN apt-get install netcat python3-dev default-libmysqlclient-dev -y
# downgrade openssl security for login.gmu.edu compatibility
RUN sed -i -e 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
RUN mkdir /go
WORKDIR /go
ADD /requirements/ /go/
......
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN apt-get update
RUN apt-get install netcat python3-dev default-libmysqlclient-dev -y
# downgrade openssl security for login.gmu.edu compatibility
RUN sed -i -e 's/DEFAULT@SECLEVEL=2/DEFAULT@SECLEVEL=1/g' /etc/ssl/openssl.cnf
RUN mkdir /go
WORKDIR /go
ADD /requirements/ /go/
RUN pip install -r prod.txt
ADD . /go/
RUN mkdir /static
......@@ -5,12 +5,13 @@ services:
restart: always
ports:
- '8000:8000'
command: /bin/bash ./startup.sh -python go/manage.py runserver 0.0.0.0:8000
command: /bin/bash ./startup.sh
volumes:
- .:/go
depends_on:
- db
environment:
- DOCKER=true
- GO_ENV=development
- GO_ALLOWED_HOSTS=*
- GO_EMAIL_DOMAIN=@masonlive.gmu.edu
......
......@@ -14,23 +14,9 @@ from django.utils import timezone
from django.utils.safestring import mark_safe
# App Imports
from .models import URL, RegisteredUser
# Other Imports
# from bootstrap3_datetime.widgets import DateTimePicker
from crispy_forms.bootstrap import (Accordion, AccordionGroup, PrependedText,
StrictButton)
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Div, Field, Fieldset, Layout
from .models import URL
class URLForm(ModelForm):
"""
The form that is used in URL creation.
"""
# target -------------------------------------------------------------------
def clean_target(self):
"""
Prevent redirect loop links
......@@ -45,7 +31,8 @@ class URLForm(ModelForm):
label='Long URL (Required)',
max_length=1000,
widget=URLInput(attrs={
'placeholder': 'https://yoursite.com/'
'placeholder': 'https://yoursite.com/',
'class': 'urlinput form-control',
})
)
......@@ -60,16 +47,20 @@ class URLForm(ModelForm):
# if we're able to get a URL with the same short url
URL.objects.get(short__iexact=value)
except URL.DoesNotExist as ex:
return
return
# then raise a ValidationError
raise ValidationError('Short url already exists.')
raise ValidationError('Short URL already exists.')
# Custom short-url field with validators.
short = SlugField(
required=False,
label='Short URL (Optional)',
widget=TextInput(),
widget=TextInput(
attrs={
'class': 'urlinput form-control',
}
),
validators=[unique_short],
max_length=20,
min_length=3,
......@@ -99,7 +90,7 @@ class URLForm(ModelForm):
label='Expiration (Required)',
choices=EXPIRATION_CHOICES,
initial=NEVER,
widget=RadioSelect(),
widget=RadioSelect(attrs={'class': 'radios'}),
)
def valid_date(value):
......@@ -114,25 +105,6 @@ class URLForm(ModelForm):
else:
raise ValidationError('Date must be after today.')
# Add a custom expiration choice.
# expires_custom = DateTimeField(
# 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,
# },
# icon_attrs={
# "class": "fa fa-calendar",
# },
# )
# )
def __init__(self, *args, **kwargs):
"""
On initialization of the form, crispy forms renders this layout
......@@ -141,60 +113,11 @@ class URLForm(ModelForm):
# Grab that host info
self.host = kwargs.pop('host', None)
super(URLForm, self).__init__(*args, **kwargs)
# Define the basics for crispy-forms
self.helper = FormHelper()
self.helper.form_method = 'POST'
# Some xtra vars for form css purposes
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-1'
self.helper.field_class = 'col-md-6'
# The main "layout" defined
self.helper.layout = Layout(
Fieldset('',
#######################
Accordion(
# Step 1: Long URL
AccordionGroup('Step 1: Long URL',
Div(
HTML("""
<h4>Paste the URL you would like to shorten:</h4>
<br />"""),
'target',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# Step 2: Short URL
AccordionGroup('Step 2: Short URL',
Div(
HTML("""
<h4>Create a custom Go address:</h4>
<br />"""),
PrependedText(
'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
# Step 3: Expiration
AccordionGroup('Step 3: URL Expiration',
Div(
HTML("""
<h4>Set when you would like your Go address to expire:</h4>
<br />"""),
'expires',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# FIN
template='crispy/accordian.html'),
#######################
HTML("""
<br />"""),
StrictButton('Shorten', css_class="btn btn-primary btn-md col-md-4", type='submit')))
self.target_title = 'Paste the URL you would like to shorten:'
self.short_title = 'Create a custom Go address:'
self.expires_title = 'Set when you would like your Go address to expire:'
self.action = '/newLink'
class Meta:
"""
......@@ -207,153 +130,11 @@ class URLForm(ModelForm):
fields = ['target']
class EditForm(URLForm):
def __init__(self, *args, **kwargs):
"""
On initialization of the form, crispy forms renders this layout
"""
# Grab that host info
self.host = kwargs.pop('host', None)
super(URLForm, self).__init__(*args, **kwargs)
# Define the basics for crispy-forms
self.helper = FormHelper()
self.helper.form_method = 'POST'
# Some xtra vars for form css purposes
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-1'
self.helper.field_class = 'col-md-6'
# The main "layout" defined
self.helper.layout = Layout(
Fieldset('',
#######################
Accordion(
# Step 1: Long URL
AccordionGroup('Step 1: Long URL',
Div(
HTML("""
<h4>Modify the URL you would like to shorten:</h4>
<br />"""),
'target',
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# Step 2: Short URL
AccordionGroup('Step 2: Short URL',
Div(
HTML("""
<h4>Modify the Go address:</h4>
<br />"""),
PrependedText(
'short', 'https://go.gmu.edu/', template='crispy/customPrepended.html'),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html',),
# Step 3: Expiration
AccordionGroup('Step 3: URL Expiration',
Div(
HTML("""
<h4>Modify the expiration date:</h4>
<br />"""),
'expires',
Field('expires_custom', template="crispy/customDateField.html"),
style="background: rgb(#F6F6F6);"),
active=True,
template='crispy/accordian-group.html'),
# FIN
template='crispy/accordian.html'),
#######################
HTML("""
<br />"""),
StrictButton('Submit Changes', css_class="btn btn-primary btn-md col-md-4", type='submit')))
class Meta(URLForm.Meta):
# what attributes are included
fields = URLForm.Meta.fields
class SignupForm(ModelForm):
"""
The form that is used when a user is signing up to be a RegisteredUser
"""
super(EditForm, self).__init__(*args, **kwargs)
# The full name of the RegisteredUser
full_name = CharField(
required=True,
label='Full Name (Required)',
max_length=100,
widget=TextInput(),
help_text="We can fill in this field based on information provided by https://peoplefinder.gmu.edu.",
)
# The RegisteredUser's chosen organization
organization = CharField(
required=True,
label='Organization (Required)',
max_length=100,
widget=TextInput(),
help_text="Or whatever \"group\" you would associate with on campus.",
)
# The RegisteredUser's reason for signing up to us Go
description = CharField(
required=False,
label='Description (Optional)',
max_length=200,
widget=Textarea(),
help_text="Describe what type of links you would intend to create with Go.",
)
# A user becomes registered when they agree to the TOS
registered = 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>?'
),
help_text="Esssentially the GMU Responsible Use of Computing policies.",
)
def __init__(self, request, *args, **kwargs):
"""
On initialization of the form, crispy forms renders this layout
"""
# Necessary to call request in forms.py, is otherwise restricted to
# views.py and models.py
self.request = request
super(SignupForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-4'
self.helper.field_class = 'col-md-6'
self.helper.layout = Layout(
Fieldset('',
Div(
# Place in form fields
Div(
'full_name',
'organization',
'description',
'registered',
css_class='well'),
# Extras at bottom
StrictButton('Submit',css_class='btn btn-primary btn-md col-md-4', type='submit'),
css_class='col-md-6')))
class Meta:
"""
Metadata about this ModelForm
"""
# what model this form is for
model = RegisteredUser
# what attributes are included
fields = ['full_name', 'organization', 'description', 'registered']
self.target_title = 'Modify the URL you would like to shorten:'
self.short_title = 'Modify the Go address:'
self.expires_title = 'Modify the expiration date:'
if 'initial' in kwargs:
self.action = '/edit/' + kwargs['initial']['short']
\ No newline at end of file
<!-- include the base html template -->
{% extends 'layouts/base.html' %}
<!-- Tell Django to load static files -->
{% load staticfiles %}
<!-- load django crispy forms' tags -->
{% load crispy_forms_tags %}
<!-- define the page title block -->
{% block title %}
SRCT Go &bull; Edit Link
{% endblock %}
<!-- define the content block for the page -->
{% block content %}
<!-- define the page header div -->
<div class="page-header" id="banner">
<div class="row">
<div class="col-md-12">
<h1>
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-wrench fa-stack-1x fa-inverse"></i>
</span>
Edit Link
</h1>
</div>
</div>
</div>
<!-- call django crispy forms to render the go link creation form here -->
{% crispy form %}
<!-- load some JS to hide/show the custom date field -->
<script src="{% static "js/new_link.js" %}"></script>
{% endblock %}
<!-- include the base html template -->
{% extends 'layouts/base.html' %}
<!-- Tell Django to load static files -->
{% load staticfiles %}
<!-- load django crispy forms' tags -->
{% load crispy_forms_tags %}
<!-- define the page title block -->
{% block title %}
SRCT Go &bull; New Link
{% endblock %}
<!-- define the content block for the page -->
{% block content %}
<!-- define the page header div -->
<div class="page-header" id="banner">
<div class="row">
<div class="col-md-12">
<h1>
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-link fa-stack-1x fa-inverse"></i>
</span>
Shorten URL
</h1>
</div>
</div>
</div>
<!-- call django crispy forms to render the go link creation form here -->
{% crispy form %}
<!-- load some JS to hide/show the custom date field -->
<script src="{% static "js/new_link.js" %}"></script>
{% endblock %}
<!-- include the base html template -->
{% extends 'layouts/base.html' %}
<!-- load django crispy forms' tags -->
{% load crispy_forms_tags %}
<!-- define the page title block -->
{% block title %}
SRCT Go &bull; Apply
{% endblock %}
<!-- define the content block for the page -->
{% block content %}
<!-- define the page header div -->
<div class="page-header" id="banner">
<div class="row">
<div class="col-md-12">
<h1>
<span class="fa-stack fa-lg">
<i class="fa fa-circle fa-stack-2x"></i>
<i class="fa fa-user-plus fa-stack-1x fa-inverse"></i>
</span>
Apply to Go
</h1>
</div>
</div>
</div>
<!-- Define the div that describes why we need a signup process -->
<div class="row">
<div class="col-md-12">
<p>
In order to prevent abuse of the URL Shortner, users must be manually approved.
<br></br>
You will need to list the organization you represent and, while optional, we would like to know
<br />
why you would like to use Go and
some examples where you would use this service.
<br></br>
Please indicate below if you are interested.
</p>
<br />
<legend></legend>
</div>
</div>
<!-- call django crispy forms to render the go signup form here -->
{% crispy form %}
{% endblock %}
<!-- Custom definition for accordian-group -->
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title">
{{ div.name }}
</h4>
</div>
<div id="{{ div.css_id }}" class="panel-collapse collapse{% if div.active %} in{% endif %}" >
<div class="panel-body">
{{ fields|safe }}
</div>
</div>
</div>
<br />
<!-- Custom definition for accordian -->
<div class="panel-group" id="{{ accordion.css_id }}">
{{ content|safe }}
</div>
<!-- Custom definition for the date field in the link creation form -->
{% load crispy_forms_field %}
{% if field.is_hidden %}
{{ field }}
{% else %}
{% if field|is_checkbox %}
<div class="form-group">
{% if label_class %}
<div class="controls col-{{ bootstrap_device_type }}-offset-{{ label_size }} {{ field_class }}">
{% endif %}
{% endif %}
<{% if tag %}{{ tag }}{% else %}div{% endif %} id="div_{{ field.auto_id }}" {% if not field|is_checkbox %}class="form-group{% else %}class="checkbox{% endif %}{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors%}{% if field.errors %} has-error{% endif %}{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and not field|is_checkbox and form_show_labels %}
<label for="{{ field.id_for_label }}" class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}
</label>
{% endif %}
{% if field|is_checkboxselectmultiple %}
{% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
{% endif %}
{% if field|is_radioselect %}
{% include 'bootstrap3/layout/radioselect.html' %}
{% endif %}
{% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
{% if field|is_checkbox and form_show_labels %}
<label for="{{ field.id_for_label }}" class="{% if field.field.required %} requiredField{% endif %}">
{% crispy_field field %}
{{ field.label|safe }}
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</label>
{% else %}
<div class="controls col-md-2">
{% crispy_field field %}
{% include 'bootstrap3/layout/help_text_and_errors.html' %}
</div>
{% endif %}
{% endif %}
</{% if tag %}{{ tag }}{% else %}div{% endif %}>
{% if field|is_checkbox %}
{% if label_class %}
</div>
{% endif %}
</div>
{% endif %}
{% endif %}
<!-- Custom definition for prepended text -->
{% load crispy_forms_field %}
{% if field.is_hidden %}
{{ field }}
{% else %}
<div id="div_{{ field.auto_id }}" class="form-group{% if wrapper_class %} {{ wrapper_class }}{% endif %}{% if form_show_errors and field.errors %} has-error{% endif %}{% if field.css_classes %} {{ field.css_classes }}{% endif %}">
{% if field.label and form_show_labels %}
<label for="{{ field.id_for_label }}" class="control-label {{ label_class }}{% if field.field.required %} requiredField{% endif %}">
{{ field.label|safe }}{% if field.field.required %}<span class="asteriskField">*</span>{% endif %}