initial commit

This commit is contained in:
iou1name 2018-08-10 08:39:51 -04:00
commit 0f141f3525
37 changed files with 635 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
__pycache__/
*.swp
settings_secret.py

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# Titivillus
By popular demand, I'm building a better anonkun. It doesn't do much right now though. Actual results in the decades to come.
## Requirements
Python 3.6+
PostgreSQL 10.4+
Python packages: `django psycopg2 channels jinja2`
## Install
```
$ psql
postgres=# CREATE DATABASE "titivillus";
postgres=# CREATE USER "titivillus" WITH PASSWORD 'password';
postgres=# ALTER ROLE "titivillus" SET client_encoding TO 'utf8';
postgres=# ALTER ROLE "titivillus" SET default_transaction_isolation TO 'read committed';
postgres=# ALTER ROLE "titivillus" SET timezone TO 'UTC';
postgres=# GRANT ALL PRIVILEGES ON DATABASE "titivillus" TO "titivillus";
postgres=# \q
```
1. Get on the floor
2. Walk the dinosaur
3. Set `STATIC_ROOT` under `settings.py` appropriately
4. Run `python manage.py collectstatic`
## Usage
`gunicorn -b localhost:5100 -e SCRIPT_NAME=/titivillus titivillus.wsgi`

0
create_quest/__init__.py Normal file
View File

3
create_quest/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
create_quest/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class CreateQuestConfig(AppConfig):
name = 'create_quest'

View File

@ -0,0 +1,10 @@
{% extends "base.html" %}
{% block title %}Start a new quest{% endblock %}
{% block content %}
<h1>New Quest</h1>
<form method="post" action="{# url_for('.create_quest') #}">
<input type="text" placeholder="Quest Title" name="quest_title" maxlength="300" required/><br/>
<textarea id="create_textarea" name="quest_body"></textarea>
<input type="submit" name="submit" value="Submit"/>
</form>
{% endblock %}

View File

3
create_quest/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
create_quest/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
create_quest/urls.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
create_quest app URL configuration.
"""
from django.urls import path
from . import views
app_name = 'create_quest'
urlpatterns = [
path('', views.index, name='index'),
]

12
create_quest/views.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
/create_quest app views.
"""
from django.shortcuts import render
def index(request):
"""
The index page for creating new quests.
"""
context = {}
return render(request, 'create_quest/index.html', context)

0
homepage/__init__.py Normal file
View File

3
homepage/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
homepage/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class HomepageConfig(AppConfig):
name = 'homepage'

View File

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<h1>Quests 'n Shiet</h1>
<a href="./quest/1">Unga Bunga Quest</a><br />
<a href="./create_quest">Create New Quest</a><br />
<a href="./signup">Sign up</a><br />
<a href="./login">Login</a><br />
{% endblock %}

View File

3
homepage/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
homepage/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
homepage/urls.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
Homepage URL configuration.
"""
from django.urls import path
from . import views
app_name = 'homepage'
urlpatterns = [
path('', views.index, name='index'),
]

12
homepage/views.py Normal file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env python3
"""
Homepage views.
"""
from django.shortcuts import render
def index(request):
"""
The main index page.
"""
context = {}
return render(request, 'homepage/index.html', context)

38
jinja2/base.html Normal file
View File

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %} - Titivillus</title>
<link rel="stylesheet" type="text/css" href="{{ static('base.css') }}">
<script>
function toggleHeaderCookie(state) {
let xhr = new XMLHttpRequest();
xhr.open('GET', '{# url_for(".set_session", hide_header="on") #}', true);
xhr.send();
}
function toggleHeader() {
if (document.getElementById('header').style.display == 'initial') {
document.getElementById('header').style.display = 'none';
document.getElementById('headerHidden').style.display = 'initial';
//toggleHeaderCookie('on');
}
else {
document.getElementById('header').style.display = 'initial';
document.getElementById('headerHidden').style.display = 'none';
//toggleHeaderCookie('off');
}
}
</script>
{% block head %}{% endblock %}
</head>
<body>
<ul id="header" class="header" style="{# if session.get("hide_header") == True %}display:none;{% else %}display:initial;{% endif #}">
<li><a onclick="toggleHeader();" href="javascript:void(0);"></a></li>
<li><a href="{{ url('homepage:index') }}">Home</a></li>
{% block header %}{% endblock %}
</ul>
<ul id="headerHidden" class="header" style="{# if session.get("hide_header") == True %}display:initial;{% else #}display:none;{# endif #}">
<li><a onclick="toggleHeader();" href="javascript:void(0);"></a></li>
</ul>
<div id="content">{% block content %}{% endblock %} </div>
</body>
</html>

15
manage.py Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "titivillus.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)

0
quest/__init__.py Normal file
View File

3
quest/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
quest/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class QuestConfig(AppConfig):
name = 'quest'

View File

3
quest/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
quest/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

14
quest/urls.py Normal file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
"""
Quest and quest accesssories URL configuration.
"""
from django.urls import path
from . import views
app_name = 'quest'
urlpatterns = [
path('', views.index, name='index'),
path('<int:quest_id>', views.quest, name='quest'),
path('<int:quest_id>/<int:page_num>', views.quest, name='quest'),
]

20
quest/views.py Normal file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
"""
Quest and quest accessory views.
"""
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
"""
/quest page index. Possibly not needed.
"""
#return render(request, "Hello, world. You're at the quest index.", {})
return HttpResponse("Hello, world. You're at the quest index.")
def quest(request, quest_id, page_num=1):
"""
Arbituary quest page view.
"""
return HttpResponse(f"Quest_ID: {quest_id} Page_Num: {page_num}")

232
static/base.css Normal file
View File

@ -0,0 +1,232 @@
img {
max-width:100%;
max-height:100%;
}
h3 {
margin-top: 0px;
margin-bottom: 0.5em;
}
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
list-style-type: none;
margin: 0;
padding: 0;
background-color: #dddddd;
}
.header li {
display: inline;
}
.header a {
text-decoration: none;
}
#headerHidden {
width: auto;
}
#questContainer {
display: flex;
overflow: auto;
}
#questPane {
padding-left: 5%;
padding-right: 35%;
min-width: 0;
}
.questPost {
display: flex;
}
.questPostMeta {
white-space: nowrap;
padding-right: 15px;
}
.questPostData {
word-wrap: break-word;
min-width: 0;
width: 100%;
}
#QMPostPane {
display: flex;
}
#QMPostTabs {
display: inline-block;
list-style-type: none;
padding: 0;
margin: 0;
background-color: #f1f1f1;
height: 100%;
}
.QMPostTab {
display: block;
padding: 8px 16px;
text-decoration: none;
}
.QMPostTab:hover {
background-color: #555;
color: white;
}
.QMPostTab.active {
background-color: #555;
color: white;
}
.QMPostTabContent {
flex: auto;
padding: 0px 12px;
border: 1px solid #ccc;
border-left: none;
width: 100px;
}
#postTextArea {
max-width: 100%;
}
.tooltip {
border-bottom: 1px dotted black;
}
#QMPollPostForm a {
text-decoration: none;
}
.pollOption {
width: 100%;
margin: 0.1em;
}
.pollPost {
width: 100%;
}
.poll {
border-collapse: collapse;
width: 100%;
table-layout: fixed;
border: 1px solid #ddd;
}
.optionVotes {
width: 4em;
text-align: center;
}
.poll td {
padding: 0.5em;
word-wrap: break-word;
border-bottom: 1px solid #ddd;
}
.pollCheckBox input {
display: none;
}
.pollCheckBox {
width: 1.5em;
}
.pollCheckBox label {
cursor: pointer;
background: #eee;
border: 1px solid #ddd;
padding: 0.2em;
}
.pollCheckBox label::after {
content: "\2713";
color: #bfbfbf;
opacity: 0.3;
}
.pollCheckBox label:hover::after {
opacity: 0.5;
}
.pollCheckBox input:checked + label::after {
opacity: 1;
color: black;
}
.pollCheckBox input:disabled + label {
opacity: 0.3;
}
#writeinInput {
width: 100%;
}
#chatPane {
height: 100%;
width: 30%;
right: 0;
position: fixed;
display: flex;
flex-direction: column;
}
#chatWindow {
border: 1px solid #ccc;
padding: 0.25em;
overflow: auto;
flex: 1;
}
.messageContent {
width: 100%;
word-wrap: break-word;
}
.msgSrvHr {
width: 95%;
margin-top: 0.2em;
margin-bottom: 0.2em;
margin-left: auto;
margin-right: auto;
border: 0;
border-bottom: 1px solid #ccc;
}
#messageTextDiv {
padding-bottom: 10px;
width: 100%;
display: flex;
flex-direction: column;
}
#messageTextArea {
resize: none;
box-sizing: border-box;
}
#preview {
display: block;
position: fixed;
background: white;
word-wrap: break-word;
border: 1px solid #ccc;
padding: 0.25em;
}
.greenText {
color: green;
}
.quotelink {
color: red;
}

0
titivillus/__init__.py Normal file
View File

22
titivillus/jinja2.py Normal file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
"""
Custom Jinja2 environment.
"""
import jinja2
from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
def environment(**options):
"""
Custom Jinja2 environment.
"""
env = jinja2.Environment(**options)
env.globals.update({
'static': staticfiles_storage.url,
'url': reverse,
'undefined': jinja2.StrictUndefined,
'autoescape': True,
'trim_blocks': True,
'lstrip_blocks': True,
})
return env

121
titivillus/settings.py Normal file
View File

@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""
Django settings for titivillus project.
"""
import os
import titivillus.settings_secret
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = titivillus.settings_secret.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['steelbea.me']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'quest.apps.QuestConfig',
'homepage.apps.HomepageConfig',
'create_quest.apps.CreateQuestConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'titivillus.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': ['jinja2',],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'titivillus.jinja2.environment',
'auto_reload': DEBUG,
},
},
]
WSGI_APPLICATION = 'titivillus.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'titivillus',
'USER': 'titivillus',
'PASSWORD': titivillus.settings_secret.DB_KEY,
'HOST': '',
'PORT': '',
}
}
# Password validation
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/New_York'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = '/usr/local/www/html/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]

View File

@ -0,0 +1,6 @@
#!/usr/bin/env python3
"""
Secrets stop being secret when you tell someone.
"""
SECRET_KEY = """SECRET_KEY"""
DB_KEY = """DB_KEY"""

13
titivillus/urls.py Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""
Main site URL configuration.
"""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('', include('homepage.urls')),
path('admin/', admin.site.urls),
path('quest/', include('quest.urls')),
path('create_quest/', include('create_quest.urls')),
]

16
titivillus/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for titivillus project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "titivillus.settings")
application = get_wsgi_application()