├── .gitignore
├── README.md
├── analytics-config.json.skel
├── config.py.skel
├── deploy.sh
├── docs
├── admin-dashboard.gif
├── admin-dashboard.png
├── game-review.png
├── home-page.png
├── mobile-menu.png
└── podcast-manager.png
├── event
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_event_type.py
│ ├── 0003_auto_20200825_2001.py
│ ├── 0004_event_end_date.py
│ └── __init__.py
├── models.py
├── templates
│ └── event
│ │ ├── calendar.html
│ │ └── event.html
├── urls.py
├── views.py
└── wagtail_hooks.py
├── game
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_auto_20191201_0749.py
│ ├── 0003_reviewcodes.py
│ ├── 0004_reviewcodes_redeemed_by.py
│ ├── 0005_auto_20191201_1839.py
│ ├── 0006_auto_20191201_1917.py
│ ├── 0007_auto_20191202_0041.py
│ ├── 0008_game_designer.py
│ ├── 0009_auto_20200502_1803.py
│ ├── 0010_auto_20200502_1817.py
│ ├── 0011_game_type.py
│ ├── 0012_auto_20200506_0600.py
│ ├── 0013_auto_20200506_0745.py
│ ├── 0014_auto_20200506_1646.py
│ ├── 0015_auto_20200506_1648.py
│ └── __init__.py
├── models.py
├── site_summary.py
├── templates
│ └── wagtailadmin
│ │ └── home
│ │ └── site_summary_game.html
└── wagtail_hooks.py
├── gulpfile.js
├── home
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ └── __init__.py
├── models.py
└── templates
│ └── home
│ ├── hero_page_listing.html
│ └── home_page.html
├── image
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_customimage_image_credit.py
│ ├── 0003_customimage_game.py
│ └── __init__.py
└── models.py
├── install.py
├── manage.py
├── package.json
├── page
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_blogpage_header_image.py
│ ├── 0003_auto_20191201_0756.py
│ ├── 0004_auto_20191201_0829.py
│ ├── 0005_blogpage_author.py
│ ├── 0006_auto_20200416_2250.py
│ ├── 0007_auto_20200416_2254.py
│ ├── 0008_auto_20200416_2307.py
│ ├── 0009_blogfolder_folder_icon.py
│ ├── 0010_auto_20200424_0451.py
│ ├── 0011_auto_20200424_2329.py
│ ├── 0012_auto_20200501_1841.py
│ ├── 0013_auto_20200501_1856.py
│ ├── 0014_blogpage_legacy_url.py
│ ├── 0015_auto_20200501_2340.py
│ ├── 0016_auto_20200502_0226.py
│ ├── 0017_auto_20200502_0248.py
│ ├── 0018_blogpage_header_video.py
│ ├── 0019_auto_20200505_1848.py
│ ├── 0020_auto_20200505_1928.py
│ ├── 0021_auto_20200505_1952.py
│ ├── 0022_blogpage_enable_comments.py
│ ├── 0023_auto_20200506_0155.py
│ ├── 0024_auto_20200510_1956.py
│ └── __init__.py
├── models.py
├── templates
│ ├── blocks
│ │ ├── image_block.html
│ │ └── review_block.html
│ ├── blog
│ │ ├── large_listing.html
│ │ ├── medium_listing.html
│ │ ├── meta_tags.html
│ │ ├── sidebar_listing.html
│ │ ├── small_listing.html
│ │ └── tags.html
│ └── page
│ │ ├── base_page.html
│ │ ├── basic_page.html
│ │ ├── blog_folder.html
│ │ ├── blog_page.html
│ │ ├── tag_folder.html
│ │ ├── tag_index.html
│ │ ├── tag_page.html
│ │ └── user_page.html
├── views.py
└── wagtail_hooks.py
├── podcast
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_podcastsettings.py
│ ├── 0003_podcastsettings_description.py
│ ├── 0004_auto_20190908_0158.py
│ ├── 0005_podcast_file.py
│ ├── 0006_podcast_publish_date.py
│ ├── 0007_podcast_related_page.py
│ └── __init__.py
├── models.py
├── site_summary.py
├── templates
│ ├── podcast
│ │ ├── list.html
│ │ ├── player.html
│ │ ├── podcast.xml
│ │ ├── results.html
│ │ └── type_index.html
│ └── wagtailadmin
│ │ └── home
│ │ └── site_summary_podcast.html
├── urls.py
├── views.py
└── wagtail_hooks.py
├── requirements.txt
├── search
├── __init__.py
├── templates
│ └── search
│ │ ├── search.html
│ │ └── search_form.html
└── views.py
├── snippet
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_delete_game.py
│ └── __init__.py
├── templates
│ └── wagtailsnippets
│ │ └── snippets
│ │ ├── list.html
│ │ ├── results.html
│ │ └── type_index.html
├── urls.py
└── views.py
├── spritesanddice
├── __init__.py
├── context_processors.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_metadatasettings.py
│ ├── 0003_sidebarsettings.py
│ ├── 0004_delete_sidebarsettings.py
│ ├── 0005_auto_20200509_2228.py
│ └── __init__.py
├── models.py
├── settings
│ ├── __init__.py
│ ├── base.py
│ ├── dev.py
│ └── production.py
├── static
│ ├── css
│ │ ├── admin.css
│ │ ├── main.css
│ │ └── reflex.css
│ ├── favicon.ico
│ ├── fonts
│ │ ├── Roboto-Bold.ttf
│ │ ├── Roboto-BoldItalic.ttf
│ │ ├── Roboto-Italic.ttf
│ │ ├── Roboto-Light.ttf
│ │ ├── Roboto-LightItalic.ttf
│ │ ├── Roboto-Medium.ttf
│ │ ├── Roboto-MediumItalic.ttf
│ │ ├── Roboto-Regular.ttf
│ │ ├── Roboto-Thin.ttf
│ │ ├── Roboto-ThinItalic.ttf
│ │ ├── RobotoSlab-Black.ttf
│ │ ├── RobotoSlab-Bold.ttf
│ │ ├── RobotoSlab-ExtraBold.ttf
│ │ ├── RobotoSlab-ExtraLight.ttf
│ │ ├── RobotoSlab-Light.ttf
│ │ ├── RobotoSlab-Medium.ttf
│ │ ├── RobotoSlab-Regular.ttf
│ │ ├── RobotoSlab-SemiBold.ttf
│ │ ├── RobotoSlab-Thin.ttf
│ │ ├── RobotoSlab-VariableFont_wght.ttf
│ │ ├── jaapokki-regular.woff
│ │ ├── opensans-regular-webfont.woff
│ │ └── wagtail.woff
│ ├── img
│ │ ├── logo-black.png
│ │ ├── logo-white.png
│ │ ├── podcast.jpg
│ │ ├── sd-logo-black.png
│ │ ├── sd-logo-white.png
│ │ └── snd_end_mark.png
│ ├── js
│ │ ├── admin.js
│ │ ├── jquery.js
│ │ ├── navigation.js
│ │ └── spritesanddice.js
│ ├── scss
│ │ ├── admin
│ │ │ ├── admin.scss
│ │ │ ├── analytics.scss
│ │ │ ├── home.scss
│ │ │ ├── login.scss
│ │ │ ├── podcast.scss
│ │ │ ├── summary.scss
│ │ │ └── typography.scss
│ │ ├── colors.scss
│ │ ├── fonts.scss
│ │ ├── icons.scss
│ │ ├── main
│ │ │ ├── author.scss
│ │ │ ├── blog-page.scss
│ │ │ ├── calendar.scss
│ │ │ ├── home-page.scss
│ │ │ ├── main.scss
│ │ │ ├── navigation
│ │ │ │ ├── footer.scss
│ │ │ │ ├── header.scss
│ │ │ │ ├── navbar.scss
│ │ │ │ └── sidebar.scss
│ │ │ ├── password-required.scss
│ │ │ ├── podcast.scss
│ │ │ ├── search.scss
│ │ │ └── typography.scss
│ │ ├── userbar.scss
│ │ └── variables.scss
│ ├── svg
│ │ └── logo-big.svg
│ └── wagtailadmin
│ │ └── css
│ │ └── userbar.css
├── stream_blocks.py
├── templates
│ ├── 404.html
│ ├── 500.html
│ ├── base.html
│ ├── disqus.html
│ ├── google_analytics.html
│ ├── meta_tags.html
│ ├── navigation
│ │ ├── footer.html
│ │ ├── header.html
│ │ ├── mobile_menu.html
│ │ ├── mobile_social_links.html
│ │ ├── navbar.html
│ │ ├── pagination.html
│ │ ├── sidebar.html
│ │ ├── sidebar_posts.html
│ │ └── social-link-icons.html
│ ├── page_meta.html
│ ├── password_required.html
│ ├── robots.txt
│ ├── rss.xml
│ └── wagtailadmin
│ │ ├── admin_base.html
│ │ ├── base.html
│ │ ├── home.html
│ │ ├── login.html
│ │ ├── review_copies.html
│ │ ├── skeleton.html
│ │ └── userbar
│ │ └── base.html
├── templatetags
│ ├── admin_tags.py
│ └── menu_tags.py
├── urls.py
├── wagtail_hooks.py
├── wsgi.py
└── wsgi_dev.py
└── users
├── __init__.py
├── forms.py
├── migrations
├── 0001_initial.py
├── 0002_auto_20200422_0253.py
├── 0003_auto_20200422_0311.py
├── 0004_auto_20200422_0311.py
└── __init__.py
├── models.py
├── templates
├── users
│ ├── author.html
│ ├── author_bio.html
│ ├── author_social_links.html
│ ├── user_grid.html
│ └── user_index.html
├── wagtailadmin
│ └── account
│ │ └── change_bio.html
└── wagtailusers
│ └── users
│ └── edit.html
├── templatetags
└── customuser_tags.py
├── urls.py
├── views.py
└── wagtail_hooks.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # System Files
2 | *.DS_Store
3 |
4 | # Database
5 | db.sqlite3
6 |
7 | # Config Files
8 | .gitconfig
9 | config.py
10 | analytics-config.json
11 |
12 | # Logs
13 | *.log
14 |
15 | # Pycache Files
16 | *.pyc
17 | __pycache__/
18 | *.py[cod]
19 |
20 | # NPM
21 | node_modules/
22 | package-lock.json
23 |
24 | # Compiled Output and temp files
25 | build/
26 | tmp/
27 | bakery/
28 |
29 | # UWSGI Socket Files
30 | *.sock
31 |
32 | # Media / Collected static files
33 | /media/
34 | /static/
35 |
36 | # Server Config
37 | .htaccess
38 | .htpasswd
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Sprites and Dice 3.0
4 |
5 | A gaming blog and news site built with [Wagtail CMS](https://github.com/wagtail/wagtail/).
6 |
7 | -----
8 |
9 | ## UI Highlights
10 |
11 | ### Home Page
12 |
13 | 
14 |
15 |
16 | ### Mobile Menu
17 |
18 | 
19 |
20 |
21 | ### Game Review
22 |
23 | 
24 |
25 |
26 | -----
27 |
28 | ## Admin Features
29 |
30 | 
31 |
32 | ### [Podcast Manager](podcast/)
33 |
34 | 
35 |
36 | In combination with [Wagtailmedia](https://github.com/torchbox/wagtailmedia/)'s ability to manage .mp3 files, admins can use this Wagtail snippet to create new podcast episodes and edit their `podcast.xml` metadata. Podcasts can be embedded on any page using a custom "Podcast" stream block.
37 |
38 | ### [Game Manager](game/)
39 |
40 | A Wagtail snippet that allows admins to store info like game box art, publishers, MSRP, etc.
41 |
42 | ### Built-in Analytics Viewer
43 |
44 | Powered by [Wagalytics](https://github.com/tomdyson/wagalytics).
45 |
--------------------------------------------------------------------------------
/analytics-config.json.skel:
--------------------------------------------------------------------------------
1 | {
2 | "Instructions": "To create a JSON key, follow the instructions at this URL: https://ga-dev-tools.appspot.com/embed-api/server-side-authorization/"
3 | }
4 |
--------------------------------------------------------------------------------
/config.py.skel:
--------------------------------------------------------------------------------
1 | # Google Analytics Config
2 | GA_KEY_FILEPATH = 'analytics-config.json'
3 | GA_VIEW_ID = 'ga:XXXXXXXXX'
4 |
5 | # SECURITY WARNING: keep the secret key used in production secret!
6 | SECRET_KEY = 'some-random-string-goes-here'
7 |
8 | DATABASE_VALUES = {
9 | 'ENGINE': 'django.db.backends.postgresql',
10 | 'NAME': '',
11 | 'USER': '',
12 | 'PASSWORD': '',
13 | 'HOST': 'localhost',
14 | 'PORT': '5432',
15 | }
16 |
--------------------------------------------------------------------------------
/deploy.sh:
--------------------------------------------------------------------------------
1 | echo "Enabling Virtualenv..."
2 | source /home/sprites/sprites/bin/activate
3 |
4 | echo "Downloading updates from Git..."
5 | git reset --hard
6 | git clean -df
7 | git checkout .
8 | git pull
9 |
10 | echo "Installing Python dependencies..."
11 | pip install -r requirements.txt --quiet
12 |
13 | echo "Gathering static files..."
14 | ./manage.py collectstatic --noinput
15 |
16 | echo "Fixing file permissions..."
17 | sudo chown -R sprites:sprites .
18 |
19 | echo "Restarting Gunicorn..."
20 | sudo systemctl restart gunicorn
21 |
22 | echo "Restarting nginx..."
23 | sudo nginx -t && sudo systemctl restart nginx
24 |
25 | echo "Done!"
26 |
--------------------------------------------------------------------------------
/docs/admin-dashboard.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/admin-dashboard.gif
--------------------------------------------------------------------------------
/docs/admin-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/admin-dashboard.png
--------------------------------------------------------------------------------
/docs/game-review.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/game-review.png
--------------------------------------------------------------------------------
/docs/home-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/home-page.png
--------------------------------------------------------------------------------
/docs/mobile-menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/mobile-menu.png
--------------------------------------------------------------------------------
/docs/podcast-manager.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/docs/podcast-manager.png
--------------------------------------------------------------------------------
/event/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/event/__init__.py
--------------------------------------------------------------------------------
/event/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-08-25 19:28
2 |
3 | from django.db import migrations, models
4 | import wagtail.search.index
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Event',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('title', models.CharField(blank=True, max_length=255, null=True)),
20 | ('description', models.TextField(blank=True, null=True)),
21 | ('date', models.DateTimeField(blank=True, null=True)),
22 | ],
23 | options={
24 | 'abstract': False,
25 | },
26 | bases=(wagtail.search.index.Indexed, models.Model),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/event/migrations/0002_event_type.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-08-25 19:59
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('event', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='event',
15 | name='type',
16 | field=models.CharField(choices=[('stream', 'Twitch Stream'), ('online', 'Virtual Game Night'), ('meatspace', 'In-Person Event'), ('convention', 'Convention'), ('other', 'Other')], default='online', max_length=30),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/event/migrations/0003_auto_20200825_2001.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-08-25 20:01
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('event', '0002_event_type'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='event',
15 | old_name='date',
16 | new_name='start_date',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/event/migrations/0004_event_end_date.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-08-25 20:02
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('event', '0003_auto_20200825_2001'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='event',
15 | name='end_date',
16 | field=models.DateTimeField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/event/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/event/migrations/__init__.py
--------------------------------------------------------------------------------
/event/models.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.conf import settings
3 | from django.core.exceptions import ValidationError
4 | from django.core.files.images import ImageFile
5 | from django.core.files.base import ContentFile
6 | from django.core.validators import FileExtensionValidator
7 | from django.db import models
8 | from django.db.models import Q
9 | from django.utils.html import format_html
10 | from django.utils.translation import ugettext_lazy as _
11 |
12 | from modelcluster.contrib.taggit import ClusterTaggableManager
13 | from modelcluster.fields import ParentalKey
14 | from modelcluster.models import ClusterableModel
15 |
16 | from taggit.models import TaggedItemBase
17 |
18 | from wagtail.admin.edit_handlers import InlinePanel, FieldPanel, StreamFieldPanel, MultiFieldPanel, HelpPanel
19 | from wagtail.contrib.modeladmin.options import ModelAdmin, ModelAdminGroup, modeladmin_register
20 | from wagtail.core import blocks
21 | from wagtail.core.fields import StreamField
22 | from wagtail.core.models import Page, Orderable
23 | from wagtail.images.edit_handlers import ImageChooserPanel
24 | from wagtail.images.models import Image
25 | from wagtail.search import index
26 | from wagtail.snippets.models import register_snippet
27 |
28 | # ===== Snippet Models =====
29 |
30 | @register_snippet
31 | class Event(index.Indexed, ClusterableModel):
32 |
33 | title = models.CharField(null=True, blank=True, max_length=255)
34 | description = models.TextField(null=True, blank=True)
35 |
36 | start_date = models.DateTimeField(null=True, blank=True)
37 | end_date = models.DateTimeField(null=True, blank=True)
38 |
39 | type = models.CharField(max_length=30, choices=(
40 | ('stream', 'Twitch Stream'),
41 | ('online', 'Virtual Game Night'),
42 | ('meatspace', 'In-Person Event'),
43 | ('convention', 'Convention'),
44 | ('other', 'Other'),
45 | ), default='online')
46 |
47 | panels = [
48 | FieldPanel('title'),
49 | FieldPanel('description'),
50 | FieldPanel('type'),
51 | FieldPanel('start_date'),
52 | FieldPanel('end_date'),
53 | ]
54 |
55 | def __str__(self):
56 | return self.title
57 |
58 |
59 | # ===== ModelAdmin Models =====
60 |
61 | class EventAdmin(ModelAdmin):
62 | model = Event
63 | list_display = ('title', 'type', 'start_date', 'end_date')
64 | search_fields = ['title', 'description']
65 | list_display_add_buttons = 'title'
66 | menu_icon = 'fa-calendar'
67 |
68 | modeladmin_register(EventAdmin)
69 |
--------------------------------------------------------------------------------
/event/templates/event/calendar.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailcore_tags %}
3 |
4 | {% block body_class %}calendar{% endblock %}
5 |
6 | {% block title %}Events Schedule{% endblock %}
7 | {% block og_title %}Events Schedule{% endblock %}
8 |
9 | {% block before_page_feed%}
10 |
15 | {% endblock %}
16 |
17 | {% block page_feed %}
18 | {% for event in events %}
19 | {% include "event/event.html" %}
20 | {% endfor %}
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/event/templates/event/event.html:
--------------------------------------------------------------------------------
1 |
2 |
{{event.title}}
3 | {{event.start_date|date:"M d"}} - {{event.end_date|date:"M d"}}
4 |
5 | {{event.description}}
6 |
7 |
--------------------------------------------------------------------------------
/event/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import include, path, re_path
2 |
3 | from . import views
4 |
5 | app_name = 'event'
6 |
7 | urlpatterns = [
8 | path('', views.events_calendar, name='calendar'),
9 | ]
10 |
--------------------------------------------------------------------------------
/event/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | from .models import Event
4 |
5 | def events_calendar(request):
6 | return render(request, 'event/calendar.html', {
7 | 'events': Event.objects.all().order_by('start_date')
8 | })
9 |
--------------------------------------------------------------------------------
/event/wagtail_hooks.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/event/wagtail_hooks.py
--------------------------------------------------------------------------------
/game/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/game/__init__.py
--------------------------------------------------------------------------------
/game/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 07:38
2 |
3 | from django.db import migrations, models
4 | import wagtail.search.index
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='Game',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('name', models.CharField(max_length=255)),
20 | ],
21 | options={
22 | 'ordering': ['name'],
23 | },
24 | bases=(wagtail.search.index.Indexed, models.Model),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/game/migrations/0002_auto_20191201_0749.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 07:49
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('game', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='OtherInfo',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
20 | ('label', models.CharField(blank=True, max_length=255)),
21 | ('text', models.CharField(max_length=255)),
22 | ],
23 | options={
24 | 'ordering': ['sort_order'],
25 | 'abstract': False,
26 | },
27 | ),
28 | migrations.AddField(
29 | model_name='game',
30 | name='author',
31 | field=models.CharField(blank=True, max_length=255),
32 | ),
33 | migrations.AddField(
34 | model_name='game',
35 | name='developer',
36 | field=models.CharField(blank=True, max_length=255),
37 | ),
38 | migrations.AddField(
39 | model_name='game',
40 | name='format',
41 | field=models.CharField(blank=True, max_length=255),
42 | ),
43 | migrations.AddField(
44 | model_name='game',
45 | name='number_of_players',
46 | field=models.CharField(blank=True, max_length=255),
47 | ),
48 | migrations.AddField(
49 | model_name='game',
50 | name='platforms',
51 | field=models.CharField(blank=True, max_length=255),
52 | ),
53 | migrations.AddField(
54 | model_name='game',
55 | name='play_time',
56 | field=models.CharField(blank=True, max_length=255),
57 | ),
58 | migrations.AddField(
59 | model_name='game',
60 | name='price',
61 | field=models.CharField(blank=True, max_length=255),
62 | ),
63 | migrations.AddField(
64 | model_name='game',
65 | name='publisher',
66 | field=models.CharField(blank=True, max_length=255),
67 | ),
68 | migrations.AddField(
69 | model_name='game',
70 | name='release_date',
71 | field=models.DateTimeField(blank=True, null=True),
72 | ),
73 | migrations.AddField(
74 | model_name='otherinfo',
75 | name='game',
76 | field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='other_info', to='game.Game'),
77 | ),
78 | ]
79 |
--------------------------------------------------------------------------------
/game/migrations/0003_reviewcodes.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 08:29
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('game', '0002_auto_20191201_0749'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='ReviewCodes',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
20 | ('code', models.CharField(blank=True, max_length=255)),
21 | ('notes', models.TextField()),
22 | ('redeemed', models.BooleanField()),
23 | ('game', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='review_codes', to='game.Game')),
24 | ],
25 | options={
26 | 'ordering': ['sort_order'],
27 | 'abstract': False,
28 | },
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/game/migrations/0004_reviewcodes_redeemed_by.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 08:31
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0003_reviewcodes'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='reviewcodes',
15 | name='redeemed_by',
16 | field=models.CharField(blank=True, max_length=255),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0005_auto_20191201_1839.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 18:39
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('image', '0001_initial'),
11 | ('game', '0004_reviewcodes_redeemed_by'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='game',
17 | name='box_art',
18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='image.CustomImage'),
19 | ),
20 | migrations.AlterField(
21 | model_name='reviewcodes',
22 | name='code',
23 | field=models.CharField(max_length=255, null=True),
24 | ),
25 | migrations.AlterField(
26 | model_name='reviewcodes',
27 | name='notes',
28 | field=models.TextField(blank=True),
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/game/migrations/0006_auto_20191201_1917.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 19:17
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0005_auto_20191201_1839'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='game',
15 | name='release_date',
16 | field=models.DateField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0007_auto_20191202_0041.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-02 00:41
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0006_auto_20191201_1917'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='reviewcodes',
15 | name='code',
16 | field=models.CharField(blank=True, max_length=255, null=True),
17 | ),
18 | migrations.AlterField(
19 | model_name='reviewcodes',
20 | name='redeemed',
21 | field=models.BooleanField(default=False),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/game/migrations/0008_game_designer.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-25 01:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0007_auto_20191202_0041'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='game',
15 | name='designer',
16 | field=models.CharField(blank=True, max_length=255),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0009_auto_20200502_1803.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 18:03
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0008_game_designer'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterModelOptions(
14 | name='game',
15 | options={'ordering': ['title']},
16 | ),
17 | migrations.RenameField(
18 | model_name='game',
19 | old_name='name',
20 | new_name='title',
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/game/migrations/0010_auto_20200502_1817.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 18:17
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.contrib.taggit
6 | import modelcluster.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('taggit', '0003_taggeditem_add_unique_index'),
13 | ('game', '0009_auto_20200502_1803'),
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='GameTag',
19 | fields=[
20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21 | ('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='game_tags', to='game.Game')),
22 | ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='game_gametag_items', to='taggit.Tag')),
23 | ],
24 | options={
25 | 'abstract': False,
26 | },
27 | ),
28 | migrations.AddField(
29 | model_name='game',
30 | name='tags',
31 | field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='game.GameTag', to='taggit.Tag', verbose_name='Tags'),
32 | ),
33 | ]
34 |
--------------------------------------------------------------------------------
/game/migrations/0011_game_type.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 18:36
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0010_auto_20200502_1817'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='game',
15 | name='type',
16 | field=models.CharField(choices=[('video-game', 'Video Game'), ('tabletop-game', 'Tabletop Game'), ('book', 'Book')], default='video-game', max_length=30),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0012_auto_20200506_0600.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-06 06:00
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0011_game_type'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='game',
15 | name='type',
16 | field=models.CharField(choices=[('video-game', 'Video Game'), ('tabletop-game', 'Tabletop'), ('book', 'Book')], default='video-game', max_length=30),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0013_auto_20200506_0745.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-06 07:45
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0012_auto_20200506_0600'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='game',
15 | name='type',
16 | field=models.CharField(choices=[('video-game', 'Video Game'), ('tabletop-game', 'Tabletop'), ('book', 'Book'), ('movie', 'Movie')], default='video-game', max_length=30),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/0014_auto_20200506_1646.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-06 16:46
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('game', '0013_auto_20200506_0745'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='ReviewCopy',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
20 | ('code', models.CharField(blank=True, help_text='A download code, if it exists.', max_length=255, null=True)),
21 | ('notes', models.TextField(blank=True)),
22 | ('redeemed_by', models.CharField(blank=True, help_text='Who is reviewing this game?', max_length=255)),
23 | ],
24 | options={
25 | 'ordering': ['sort_order'],
26 | 'abstract': False,
27 | },
28 | ),
29 | migrations.AlterField(
30 | model_name='game',
31 | name='format',
32 | field=models.CharField(blank=True, help_text='Card game, Miniatures game, Board game, etc.', max_length=255),
33 | ),
34 | migrations.AlterField(
35 | model_name='game',
36 | name='platforms',
37 | field=models.CharField(blank=True, help_text='PC, PS4, Virtual Boy, Vectrex, etc.', max_length=255),
38 | ),
39 | migrations.AlterField(
40 | model_name='game',
41 | name='play_time',
42 | field=models.CharField(blank=True, help_text='Average play session duration for board games, total playtime for some video games.', max_length=255),
43 | ),
44 | migrations.DeleteModel(
45 | name='ReviewCodes',
46 | ),
47 | migrations.AddField(
48 | model_name='reviewcopy',
49 | name='game',
50 | field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='review_copies', to='game.Game'),
51 | ),
52 | ]
53 |
--------------------------------------------------------------------------------
/game/migrations/0015_auto_20200506_1648.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-06 16:48
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('game', '0014_auto_20200506_1646'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='reviewcopy',
15 | name='notes',
16 | field=models.TextField(blank=True, help_text='What version of the game? Is this DLC? An expansion? Who gave it to us?'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/game/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/game/migrations/__init__.py
--------------------------------------------------------------------------------
/game/site_summary.py:
--------------------------------------------------------------------------------
1 | from game.models import Game
2 |
3 | from wagtail.admin.site_summary import SummaryItem
4 |
5 | GAME_ADMIN_LINK = '/admin/game/game/'
6 |
7 | class GameSummaryItem(SummaryItem):
8 | template = 'wagtailadmin/home/site_summary_game.html'
9 |
10 | def get_context(self):
11 | count = Game.objects.all().count()
12 | return {
13 | 'link': GAME_ADMIN_LINK,
14 | 'count': count
15 | }
16 |
--------------------------------------------------------------------------------
/game/templates/wagtailadmin/home/site_summary_game.html:
--------------------------------------------------------------------------------
1 | {% load i18n wagtailadmin_tags %}
2 |
3 |
4 |
5 | {% blocktrans count counter=count with count|intcomma as total %}
6 | {{ total }} Game
7 | {% plural %}
8 | {{ total }} Games
9 | {% endblocktrans %}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/game/wagtail_hooks.py:
--------------------------------------------------------------------------------
1 | from game.site_summary import GameSummaryItem, GAME_ADMIN_LINK
2 |
3 | from wagtail.admin.menu import MenuItem
4 | from wagtail.core import hooks
5 |
6 | @hooks.register('construct_homepage_summary_items')
7 | def add_game_summary_item(request, items):
8 | items.append(GameSummaryItem(request))
9 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var gulp = require('gulp');
4 | var clean = require('gulp-clean');
5 | var sass = require('gulp-sass');
6 | var csso = require('gulp-csso');
7 |
8 |
9 | gulp.task('sass:main', function () {
10 | return gulp.src('./spritesanddice/static/scss/main/main.scss')
11 | .pipe(sass().on('error', sass.logError))
12 | .pipe(csso()) // Minify CSS
13 | .pipe(gulp.dest('./spritesanddice/static/css'));
14 | });
15 |
16 | gulp.task('sass:admin', function () {
17 | return gulp.src('./spritesanddice/static/scss/admin/admin.scss')
18 | .pipe(sass().on('error', sass.logError))
19 | .pipe(csso()) // Minify CSS
20 | .pipe(gulp.dest('./spritesanddice/static/css'));
21 | });
22 |
23 | gulp.task('sass:userbar', function () {
24 | return gulp.src('./spritesanddice/static/scss/userbar.scss')
25 | .pipe(sass().on('error', sass.logError))
26 | .pipe(csso()) // Minify CSS
27 | .pipe(gulp.dest('./spritesanddice/static/wagtailadmin/css/'));
28 | });
29 |
30 |
31 | gulp.task('watch', function () {
32 | gulp.watch('./spritesanddice/static/scss/userbar.scss', gulp.series('sass:userbar'));
33 | gulp.watch('./spritesanddice/static/scss/**/*.scss', gulp.series('sass:main'));
34 | gulp.watch('./spritesanddice/static/scss/**/*.scss', gulp.series('sass:admin'));
35 | });
36 |
--------------------------------------------------------------------------------
/home/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/home/__init__.py
--------------------------------------------------------------------------------
/home/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-08-23 01:15
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ('wagtailcore', '0041_group_collection_permissions_verbose_name_plural'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='HomePage',
18 | fields=[
19 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
20 | ],
21 | options={
22 | 'abstract': False,
23 | },
24 | bases=('wagtailcore.page',),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/home/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/home/migrations/__init__.py
--------------------------------------------------------------------------------
/home/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from page.models import BlogPage
4 |
5 | from spritesanddice.models import SiteSettings
6 |
7 | from wagtail.core.models import Page
8 |
9 |
10 | class HomePage(Page):
11 |
12 | parent_page_types = []
13 |
14 | def header_image(self):
15 | first_post = BlogPage.objects.live().public().order_by('-go_live_at').first()
16 | if first_post.header_image:
17 | return first_post.header_image
18 | else:
19 | return SiteSettings.objects.first().default_social_thumb
20 |
21 | def get_context(self, request):
22 | context = super(HomePage, self).get_context(request)
23 | context['blog_posts'] = BlogPage.objects.live().public().order_by('-go_live_at')
24 | return context
25 |
--------------------------------------------------------------------------------
/home/templates/home/hero_page_listing.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags customuser_tags menu_tags %}
2 |
3 |
32 |
--------------------------------------------------------------------------------
/home/templates/home/home_page.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block body_class %}home-page{% endblock %}
5 |
6 | {% block page_feed %}
7 | {% blog_posts as pages %}
8 | {% for page in pages %}
9 | {% if forloop.first %}
10 | {% if pages.number == 1 %}
11 | {% include "home/hero_page_listing.html" %}
12 | {% else %}
13 | {% include "navigation/pagination.html" %}
14 |
15 | {% include "blog/medium_listing.html" %}
16 | {% endif %}
17 | {% else %}
18 | {% include "blog/medium_listing.html" %}
19 | {% endif %}
20 | {% endfor %}
21 |
22 | {% include "navigation/pagination.html" %}
23 |
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/image/migrations/0002_customimage_image_credit.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 17:08
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('image', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='customimage',
15 | name='image_credit',
16 | field=models.CharField(blank=True, max_length=250),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/image/migrations/0003_customimage_game.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 17:10
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('game', '0011_game_type'),
11 | ('image', '0002_customimage_image_credit'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='customimage',
17 | name='game',
18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='game.Game'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/image/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/image/migrations/__init__.py
--------------------------------------------------------------------------------
/install.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/install.py
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spritesanddice.settings.dev")
7 |
8 | from django.core.management import execute_from_command_line
9 |
10 | execute_from_command_line(sys.argv)
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sprites-and-dice",
3 | "license": "MIT",
4 | "version": "1.0.0",
5 | "description": "Dev dependencies for compiling CSS with Gulp.",
6 | "main": "index.js",
7 | "repository": "https://github.com/juan0tron/sprites-and-dice-django",
8 | "scripts": {
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "devDependencies": {
12 | "gulp": "^4.0.0",
13 | "gulp-autoprefixer": "~4.0.0",
14 | "gulp-clean": "^0.4.0",
15 | "gulp-cssnano": "^2.1.2",
16 | "gulp-csso": "^4.0.1",
17 | "gulp-rename": "^1.2.2",
18 | "gulp-sass": "^4.0.1",
19 | "gulp-size": "^2.1.0",
20 | "gulp-sourcemaps": "~2.6.1",
21 | "gulp-util": "~3.0.8"
22 | },
23 | "dependencies": {
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/page/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/page/__init__.py
--------------------------------------------------------------------------------
/page/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-08-23 01:15
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.contrib.taggit
6 | import modelcluster.fields
7 | import wagtail.core.blocks
8 | import wagtail.core.fields
9 | import wagtail.images.blocks
10 |
11 |
12 | class Migration(migrations.Migration):
13 |
14 | initial = True
15 |
16 | dependencies = [
17 | ('wagtailcore', '0041_group_collection_permissions_verbose_name_plural'),
18 | ('taggit', '0002_auto_20150616_2121'),
19 | ]
20 |
21 | operations = [
22 | migrations.CreateModel(
23 | name='BlogPage',
24 | fields=[
25 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
26 | ('subtitle', models.CharField(blank=True, max_length=250)),
27 | ('content', wagtail.core.fields.StreamField([('heading', wagtail.core.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.core.blocks.RichTextBlock()), ('image', wagtail.images.blocks.ImageChooserBlock())], blank=True)),
28 | ],
29 | options={
30 | 'abstract': False,
31 | },
32 | bases=('wagtailcore.page',),
33 | ),
34 | migrations.CreateModel(
35 | name='PageTag',
36 | fields=[
37 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
38 | ('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_tags', to='page.BlogPage')),
39 | ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='page_pagetag_items', to='taggit.Tag')),
40 | ],
41 | options={
42 | 'abstract': False,
43 | },
44 | ),
45 | migrations.AddField(
46 | model_name='blogpage',
47 | name='tags',
48 | field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='page.PageTag', to='taggit.Tag', verbose_name='Tags'),
49 | ),
50 | ]
51 |
--------------------------------------------------------------------------------
/page/migrations/0002_blogpage_header_image.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-10-20 20:30
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('image', '__first__'),
11 | ('page', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='blogpage',
17 | name='header_image',
18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='image.CustomImage'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/page/migrations/0003_auto_20191201_0756.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 07:56
2 |
3 | from django.db import migrations
4 | import wagtail.core.blocks
5 | import wagtail.core.fields
6 | import wagtail.images.blocks
7 | import wagtail.snippets.blocks
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('page', '0002_blogpage_header_image'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='blogpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([], icon='fa-pencil'))], blank=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/page/migrations/0004_auto_20191201_0829.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-12-01 08:29
2 |
3 | from django.db import migrations
4 | import wagtail.core.blocks
5 | import wagtail.core.fields
6 | import wagtail.images.blocks
7 | import wagtail.snippets.blocks
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('page', '0003_auto_20191201_0756'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='blogpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/page/migrations/0005_blogpage_author.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0 on 2020-04-16 22:08
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('page', '0004_auto_20191201_0829'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='blogpage',
18 | name='author',
19 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/page/migrations/0006_auto_20200416_2250.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0 on 2020-04-16 22:50
2 |
3 | from django.db import migrations
4 | import django.forms.widgets
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0005_blogpage_author'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='blogpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil')), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', wagtail.core.blocks.ChooserBlock(target_model='users.User', widget=django.forms.widgets.Select))], icon='fa-user'))], blank=True),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/page/migrations/0007_auto_20200416_2254.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0 on 2020-04-16 22:54
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0006_auto_20200416_2250'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='blogpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil')), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user'))], blank=True),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/page/migrations/0008_auto_20200416_2307.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0 on 2020-04-16 23:07
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import spritesanddice.stream_blocks
6 | import wagtail.core.blocks
7 | import wagtail.core.fields
8 | import wagtail.images.blocks
9 | import wagtail.snippets.blocks
10 |
11 |
12 | class Migration(migrations.Migration):
13 |
14 | dependencies = [
15 | ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
16 | ('page', '0007_auto_20200416_2254'),
17 | ]
18 |
19 | operations = [
20 | migrations.CreateModel(
21 | name='BasicPage',
22 | fields=[
23 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
24 | ('content', wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user'))], blank=True)),
25 | ],
26 | options={
27 | 'abstract': False,
28 | },
29 | bases=('wagtailcore.page',),
30 | ),
31 | migrations.CreateModel(
32 | name='BlogFolder',
33 | fields=[
34 | ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
35 | ],
36 | options={
37 | 'abstract': False,
38 | },
39 | bases=('wagtailcore.page',),
40 | ),
41 | migrations.AlterField(
42 | model_name='blogpage',
43 | name='content',
44 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
45 | ),
46 | ]
47 |
--------------------------------------------------------------------------------
/page/migrations/0009_blogfolder_folder_icon.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-24 04:47
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('image', '0001_initial'),
11 | ('page', '0008_auto_20200416_2307'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='blogfolder',
17 | name='folder_icon',
18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='image.CustomImage'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/page/migrations/0010_auto_20200424_0451.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-24 04:51
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0009_blogfolder_folder_icon'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='blogfolder',
15 | old_name='folder_icon',
16 | new_name='icon',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/page/migrations/0011_auto_20200424_2329.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-24 23:29
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('page', '0010_auto_20200424_0451'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='basicpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('image', wagtail.images.blocks.ImageChooserBlock()), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user')), ('User_Grid', wagtail.core.blocks.StructBlock([('users', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())])))], icon='fa-users'))], blank=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/page/migrations/0012_auto_20200501_1841.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-01 18:41
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0011_auto_20200424_2329'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='basicpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock())])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user')), ('User_Grid', wagtail.core.blocks.StructBlock([('users', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())])))], icon='fa-users'))], blank=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='blogpage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock())])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/page/migrations/0013_auto_20200501_1856.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-01 18:56
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0012_auto_20200501_1841'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='basicpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link']))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user')), ('User_Grid', wagtail.core.blocks.StructBlock([('users', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())])))], icon='fa-users'))], blank=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='blogpage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link']))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/page/migrations/0014_blogpage_legacy_url.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-01 19:22
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0013_auto_20200501_1856'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='blogpage',
15 | name='legacy_url',
16 | field=models.CharField(blank=True, max_length=255),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/page/migrations/0015_auto_20200501_2340.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-01 23:40
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 | import modelcluster.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('page', '0014_blogpage_legacy_url'),
12 | ]
13 |
14 | operations = [
15 | migrations.RemoveField(
16 | model_name='blogpage',
17 | name='legacy_url',
18 | ),
19 | migrations.CreateModel(
20 | name='LegacyUrl',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
24 | ('path', models.CharField(max_length=255)),
25 | ('blogpage', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='legacy_urls', to='page.BlogPage')),
26 | ],
27 | options={
28 | 'ordering': ['sort_order'],
29 | 'abstract': False,
30 | },
31 | ),
32 | ]
33 |
--------------------------------------------------------------------------------
/page/migrations/0016_auto_20200502_0226.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 02:26
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0015_auto_20200501_2340'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='basicpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user')), ('User_Grid', wagtail.core.blocks.StructBlock([('users', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())])))], icon='fa-users'))], blank=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='blogpage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Review_Info', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/page/migrations/0017_auto_20200502_0248.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 02:48
2 |
3 | from django.db import migrations
4 | import wagtail.core.blocks
5 | import wagtail.core.fields
6 | import wagtail.images.blocks
7 | import wagtail.snippets.blocks
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('page', '0016_auto_20200502_0226'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='blogpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Game', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/page/migrations/0018_blogpage_header_video.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-02 18:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0017_auto_20200502_0248'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='blogpage',
15 | name='header_video',
16 | field=models.URLField(blank=True, max_length=250),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/page/migrations/0019_auto_20200505_1848.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 18:48
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('page', '0018_blogpage_header_video'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='TagFolder',
16 | fields=[
17 | ('blogfolder_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='page.BlogFolder')),
18 | ],
19 | options={
20 | 'abstract': False,
21 | },
22 | bases=('page.blogfolder',),
23 | ),
24 | migrations.AddField(
25 | model_name='blogpage',
26 | name='legacy_id',
27 | field=models.IntegerField(blank=True, null=True),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/page/migrations/0020_auto_20200505_1928.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 19:28
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('taggit', '0003_taggeditem_add_unique_index'),
11 | ('page', '0019_auto_20200505_1848'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='blogfolder',
17 | name='show_in_sidebar',
18 | field=models.BooleanField(default=False),
19 | ),
20 | migrations.AddField(
21 | model_name='tagfolder',
22 | name='tag',
23 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='taggit.Tag'),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/page/migrations/0021_auto_20200505_1952.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 19:52
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0020_auto_20200505_1928'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='basicpage',
15 | name='show_in_sidebar',
16 | field=models.BooleanField(default=False),
17 | ),
18 | migrations.AddField(
19 | model_name='blogpage',
20 | name='show_in_sidebar',
21 | field=models.BooleanField(default=False),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/page/migrations/0022_blogpage_enable_comments.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 23:57
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0021_auto_20200505_1952'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='blogpage',
15 | name='enable_comments',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/page/migrations/0023_auto_20200506_0155.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-06 01:55
2 |
3 | from django.db import migrations
4 | import spritesanddice.stream_blocks
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.images.blocks
8 | import wagtail.snippets.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('page', '0022_blogpage_enable_comments'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='basicpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=False)), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Author_Bio', wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())], icon='fa-user')), ('User_Grid', wagtail.core.blocks.StructBlock([('users', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('user', spritesanddice.stream_blocks.UserChooserBlock())])))], icon='fa-users'))], blank=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='blogpage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('Image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=False)), ('caption', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'link'], required=False))])), ('Rich_Text', wagtail.core.blocks.RichTextBlock()), ('Podcast', wagtail.core.blocks.StructBlock([('podcast', wagtail.snippets.blocks.SnippetChooserBlock('podcast.Podcast'))], icon='fa-headphones')), ('Game', wagtail.core.blocks.StructBlock([('game', wagtail.snippets.blocks.SnippetChooserBlock('game.Game'))], icon='fa-pencil'))], blank=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/page/migrations/0024_auto_20200510_1956.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-10 19:56
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('page', '0023_auto_20200506_0155'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='blogpage',
15 | name='enable_comments',
16 | field=models.BooleanField(default=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/page/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/page/migrations/__init__.py
--------------------------------------------------------------------------------
/page/templates/blocks/image_block.html:
--------------------------------------------------------------------------------
1 | {% load wagtailimages_tags %}
2 |
3 | {% image value.image width-1080 %}
4 | {{value.caption}}
5 |
6 |
--------------------------------------------------------------------------------
/page/templates/blocks/review_block.html:
--------------------------------------------------------------------------------
1 | {% load wagtailimages_tags %}
2 |
3 |
4 |
5 | {% if game.box_art %}{% image game.box_art height-100 class="box-art" %}{% endif %}
6 | {{game.title}}
7 |
8 |
9 | {% if game.author %}Author: {{game.author}} {% endif %}
10 | {% if game.developer %}Developer: {{game.developer}} {% endif %}
11 | {% if game.designer %}Designer: {{game.designer}} {% endif %}
12 | {% if game.publisher %}Publisher: {{game.publisher}} {% endif %}
13 | {% if game.platforms %}Platforms: {{game.platforms}} {% endif %}
14 | {% if game.format %}Format: {{game.format}} {% endif %}
15 | {% if game.number_of_players %}Number of Players: {{game.number_of_players}} {% endif %}
16 | {% if game.play_time %}Play Time: {{game.play_time}} {% endif %}
17 | {% if game.price %}Price: {{game.price}} {% endif %}
18 | {% if game.release_date %}Release Date: {{game.release_date}} {% endif %}
19 |
20 | {% for x in game.other_info.all %}
21 | {% if x.label %}
22 | {{x.label}}: {{x.text}}
23 | {% else %}
24 | {{x.text}}
25 | {% endif%}
26 | {% endfor %}
27 |
28 |
--------------------------------------------------------------------------------
/page/templates/blog/large_listing.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags customuser_tags menu_tags %}
2 |
3 |
4 |
5 |
6 |
7 |
{{page.subtitle}}
8 | {% include "blog/meta_tags.html" %}
9 |
10 |
24 |
25 |
26 |
{{page.get_content_preview}}
27 |
28 | Read More
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/page/templates/blog/medium_listing.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags customuser_tags menu_tags %}
2 |
3 |
4 |
22 |
23 |
26 | {% if page.subtitle %}
27 |
30 | {% endif %}
31 | {% include "blog/meta_tags.html" %}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/page/templates/blog/meta_tags.html:
--------------------------------------------------------------------------------
1 | {% load customuser_tags menu_tags %}
2 |
3 |
4 | {% if page.author %}
5 | By {% author page.author %}
6 | {% endif %}
7 | {{page.post_date|date:"M d, Y"}}
8 |
9 |
--------------------------------------------------------------------------------
/page/templates/blog/sidebar_listing.html:
--------------------------------------------------------------------------------
1 | {% load wagtailimages_tags menu_tags customuser_tags %}
2 |
3 |
40 |
--------------------------------------------------------------------------------
/page/templates/blog/small_listing.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags customuser_tags menu_tags %}
2 |
3 |
22 |
--------------------------------------------------------------------------------
/page/templates/blog/tags.html:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/page/templates/page/base_page.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load static wagtailimages_tags menu_tags customuser_tags %}
3 |
4 | {% block body_class %}home-page{% endblock %}
5 |
6 | {% block og_description %}{{page.subtitle}}{% endblock %}
7 | {% block meta_description %}{{page.subtitle}}{% endblock %}
8 |
9 | {% block content %}
10 |
11 |
12 | {% block before_page_feed %}{% endblock %}
13 | {% block page_feed %}{% endblock %}
14 | {% block after_page_feed %}{% endblock %}
15 |
16 |
17 |
18 | {% include 'navigation/sidebar.html' %}
19 |
20 |
21 |
22 | {% endblock content %}
23 |
--------------------------------------------------------------------------------
/page/templates/page/basic_page.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load wagtailcore_tags wagtailimages_tags menu_tags customuser_tags %}
3 |
4 | {% block body_class %}blog-page{% endblock %}
5 |
6 | {% block content %}
7 |
8 |
9 |
10 |
{{page.title}}
11 |
12 |
13 | {% for block in page.content %}
14 | {% include_block block %}
15 | {% endfor %}
16 |
17 |
18 |
19 | {% include 'navigation/sidebar.html' %}
20 |
21 |
22 | {% endblock %}
23 |
24 | {% block extra_js %}{% endblock %}
25 |
--------------------------------------------------------------------------------
/page/templates/page/blog_folder.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block body_class %}blog-folder{% endblock %}
5 |
6 | {% block before_page_feed %}
7 |
8 | {% image page.icon original alt="page.title" class="folder-icon" %}
9 | {{page.title}}
10 |
11 |
12 | {% endblock %}
13 |
14 | {% block page_feed %}
15 | {% blog_posts blog_folder=self as pages %}
16 | {% for page in pages %}
17 | {% include "blog/medium_listing.html" %}
18 | {% endfor %}
19 |
20 | {% include "navigation/pagination.html" %}
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/page/templates/page/blog_page.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 | {% load wagtailembeds_tags wagtailcore_tags wagtailimages_tags menu_tags customuser_tags %}
3 |
4 | {% block body_class %}blog-page{% endblock %}
5 |
6 | {% block content %}
7 |
8 |
9 |
{{page.header_title}}
10 | {% if page.subtitle %}
11 |
{{page.subtitle}}
12 | {% endif %}
13 |
14 |
15 | {% if page.author %}
16 |
By {% author page.author %}
17 | {% endif %}
18 |
Comments
19 | {% if self.get_parent %}
20 |
{{self.get_parent.title}}
21 | {% endif %}
22 |
23 |
{{page.post_date|date:"M d, Y"}}
24 |
25 |
26 |
27 | {% if page.header_video %}
28 |
31 | {% elif page.header_image %}
32 |
35 | {% endif %}
36 |
37 |
38 | {% for block in page.content %}
39 | {% include_block block %}
40 | {% endfor %}
41 |
42 |
43 | {% include "blog/tags.html" %}
44 |
45 |
46 |
47 | {% author_bio page.author %}
48 |
49 |
50 |
51 | {% if page.enable_comments and page.live %}
52 | {% include "disqus.html" %}
53 | {% endif %}
54 |
55 |
56 |
57 | {% include 'navigation/sidebar.html' %}
58 |
59 |
60 | {% endblock %}
61 |
62 | {% block extra_js %}{% endblock %}
63 |
--------------------------------------------------------------------------------
/page/templates/page/tag_folder.html:
--------------------------------------------------------------------------------
1 | {% include "page/tag_page.html" %}
2 |
--------------------------------------------------------------------------------
/page/templates/page/tag_index.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block title %}{{title}}{% endblock %}
5 | {% block og_title %}{{title}}{% endblock %}
6 |
7 | {% block body_class %}tag-page{% endblock %}
8 |
9 | {% block before_page_feed %}
10 | {{title}}
11 |
12 | {% endblock %}
13 |
14 | {% block page_feed %}
15 |
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/page/templates/page/tag_page.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block title %}{{title}}{% endblock %}
5 | {% block og_title %}{{title}}{% endblock %}
6 |
7 | {% block body_class %}blog-folder tag-page{% endblock %}
8 |
9 | {% block before_page_feed %}
10 |
11 | {% image page.icon original alt="page.title" class="folder-icon" %}
12 | {{title}}
13 |
14 |
15 | {% endblock %}
16 |
17 | {% block page_feed %}
18 | {% blog_posts tag=tag_slug page_number=request.GET.page as pages %}
19 | {% for page in pages %}
20 | {% include "blog/medium_listing.html" %}
21 | {% endfor %}
22 |
23 | {% include "navigation/pagination.html" %}
24 | {% endblock %}
25 |
--------------------------------------------------------------------------------
/page/templates/page/user_page.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block title %}{{user.get_full_name}}{% endblock %}
5 | {% block og_title %}{{user.get_full_name}}{% endblock %}
6 |
7 | {% block body_class %}user-page{% endblock %}
8 |
9 | {% block page_feed %}
10 | {% author_bio user %}
11 |
12 | {% blog_posts user=user page_number=request.GET.page as pages %}
13 | {% if pages|length > 0 %}
14 | Posts by {{user.get_full_name}}
15 | {% for page in pages %}
16 | {% include "blog/medium_listing.html" %}
17 | {% endfor %}
18 |
19 | {% include "navigation/pagination.html" %}
20 | {% endif %}
21 | {% endblock %}
22 |
--------------------------------------------------------------------------------
/page/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import redirect, render
2 |
3 | from page.models import BlogPage, PageTag, TagFolder
4 |
5 | from taggit.models import Tag
6 |
7 | def tag_index(request):
8 | return render(request, 'page/tag_index.html', {
9 | 'title': 'Tags',
10 | 'tags': Tag.objects.all().distinct().order_by('name')
11 | })
12 |
13 |
14 | def tag_page(request, tag_slug=''):
15 | tag_name = tag_slug.replace('-', ' ') # Un-slugify
16 | title = 'Pages tagged "{}"'.format(tag_name)
17 | tag_folder = TagFolder.objects.filter(title__iexact=tag_name).first()
18 |
19 | if tag_folder:
20 | return render(request, 'page/tag_page.html', {
21 | 'title': tag_folder.title,
22 | 'tag_name': tag_name,
23 | 'tag_slug': tag_slug,
24 | 'page': tag_folder,
25 | })
26 | else:
27 | return render(request, 'page/tag_page.html', {
28 | 'title': title,
29 | 'tag_name': tag_name,
30 | 'tag_slug': tag_slug,
31 | })
32 |
33 | def get_rss_feed(request):
34 | return render(request, 'rss.xml', {
35 | 'pages': BlogPage.objects.live().public().order_by('-go_live_at')
36 | }, content_type='text/xml')
37 |
--------------------------------------------------------------------------------
/page/wagtail_hooks.py:
--------------------------------------------------------------------------------
1 | from page.models import BlogPage, LegacyUrl
2 |
3 | from wagtail.contrib.redirects.models import Redirect
4 | from wagtail.core import hooks
5 |
6 | # When a page moves, create a new LegacyUrl
7 | @hooks.register('before_move_page')
8 | def create_redirect_on_page_move(request, page, destination_page):
9 | if type(page) == BlogPage:
10 | old_url = Redirect.normalise_path(page.url)
11 | new_url = Redirect.normalise_path(destination_page.url + page.slug)
12 |
13 | # URLs are different, make a new LegacyUrl
14 | if old_url != new_url:
15 | legacy_url, created = LegacyUrl.objects.get_or_create(path=old_url, blogpage=page)
16 | legacy_url.save()
17 |
18 | # Check for existing LegacyUrls at the new path - if they exist, delete them
19 | existing_legacy_url = LegacyUrl.objects.filter(path=new_url).first()
20 | if existing_legacy_url:
21 | existing_legacy_url.delete()
22 |
--------------------------------------------------------------------------------
/podcast/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/podcast/__init__.py
--------------------------------------------------------------------------------
/podcast/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-07 21:12
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Podcast',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('title', models.CharField(blank=True, max_length=255, null=True)),
19 | ],
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/podcast/migrations/0002_podcastsettings.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-08 01:23
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('wagtailcore', '0041_group_collection_permissions_verbose_name_plural'),
11 | ('podcast', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='PodcastSettings',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('title', models.CharField(blank=True, max_length=255, null=True)),
20 | ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
21 | ],
22 | options={
23 | 'abstract': False,
24 | },
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/podcast/migrations/0003_podcastsettings_description.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-08 01:32
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('podcast', '0002_podcastsettings'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='podcastsettings',
15 | name='description',
16 | field=models.TextField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/podcast/migrations/0004_auto_20190908_0158.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-08 01:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('podcast', '0003_podcastsettings_description'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='podcast',
15 | name='description',
16 | field=models.TextField(blank=True, null=True),
17 | ),
18 | migrations.AddField(
19 | model_name='podcast',
20 | name='episode_number',
21 | field=models.IntegerField(blank=True, null=True),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/podcast/migrations/0005_podcast_file.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-08 02:42
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('wagtailmedia', '0003_copy_media_permissions_to_collections'),
11 | ('podcast', '0004_auto_20190908_0158'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='podcast',
17 | name='file',
18 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='podcast_file', to='wagtailmedia.Media'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/podcast/migrations/0006_podcast_publish_date.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-09-08 02:47
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('podcast', '0005_podcast_file'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='podcast',
15 | name='publish_date',
16 | field=models.DateTimeField(blank=True, null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/podcast/migrations/0007_podcast_related_page.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 17:16
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('page', '0018_blogpage_header_video'),
11 | ('podcast', '0006_podcast_publish_date'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='podcast',
17 | name='related_page',
18 | field=models.ForeignKey(blank=True, help_text='The associated announcement post for this episode.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='page.BlogPage'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/podcast/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/podcast/migrations/__init__.py
--------------------------------------------------------------------------------
/podcast/models.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from django.db import models
4 | from django.http import HttpResponseRedirect
5 |
6 | from modelcluster.models import ClusterableModel
7 |
8 | import time
9 |
10 | from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, PageChooserPanel
11 | from wagtail.contrib.settings.models import BaseSetting, register_setting
12 | from wagtail.documents.models import Document
13 | from wagtail.documents.edit_handlers import DocumentChooserPanel
14 | from wagtail.snippets.models import register_snippet
15 | from wagtail.search import index
16 |
17 | from wagtailmedia.edit_handlers import MediaChooserPanel
18 |
19 |
20 | @register_setting
21 | class PodcastSettings(BaseSetting):
22 | title = models.CharField(null=True, blank=True, max_length=255)
23 | description = models.TextField(null=True, blank=True)
24 |
25 | panels = [
26 | MultiFieldPanel([
27 | FieldPanel('title'),
28 | FieldPanel('description'),
29 | ], heading="Podcast Feed Settings")
30 | ]
31 |
32 | def save(self, *args, **kwargs):
33 | # TODO: IF CHANGES AFFECT PODCAST METADATA, UPDATE ALL EPISODES
34 | # episodes = Podcast.objects.all()
35 | # for e in episodes:
36 | # # update here
37 | # e.save()
38 | super(PodcastSettings, self).save(*args, **kwargs)
39 |
40 | @register_snippet
41 | class Podcast(index.Indexed, ClusterableModel):
42 |
43 | file = models.ForeignKey(
44 | 'wagtailmedia.Media',
45 | null=True,
46 | blank=True,
47 | on_delete=models.SET_NULL,
48 | related_name='podcast_file'
49 | )
50 |
51 | episode_number = models.IntegerField(null=True, blank=True)
52 | title = models.CharField(null=True, blank=True, max_length=255)
53 | description = models.TextField(null=True, blank=True)
54 | publish_date = models.DateTimeField(null=True, blank=True)
55 |
56 | related_page = models.ForeignKey(
57 | 'page.BlogPage',
58 | null=True,
59 | blank=True,
60 | on_delete=models.SET_NULL,
61 | related_name='+',
62 | help_text="The associated announcement post for this episode."
63 | )
64 |
65 | panels = [
66 | MediaChooserPanel('file'),
67 | FieldPanel('episode_number'),
68 | FieldPanel('title'),
69 | FieldPanel('description'),
70 | PageChooserPanel('related_page'),
71 | FieldPanel('publish_date'),
72 | ]
73 |
74 | search_fields = [
75 | index.SearchField('title', partial_match=True),
76 | index.SearchField('description', partial_match=True),
77 | ]
78 |
79 | def __str__(self):
80 | return self.title
81 |
82 | # Example XML date string: "Fri, 11 May 2018 10:12:46 -0400"
83 | def xml_pubdate(self):
84 | if self.publish_date:
85 | return datetime.strftime(self.publish_date, "%a, %d %b %Y %H:%M:%S %z")
86 |
87 | # Media file duration is in seconds. Convert to HH:MM
88 | def episode_length(self):
89 | seconds = 0
90 | if(self.file):
91 | seconds = self.file.duration
92 | return time.strftime('%H:%M:%S', time.gmtime(seconds))
93 |
94 | def save(self, *args, **kwargs):
95 | # EDIT MP3 METADATA/FILENAME HERE
96 | # https://eyed3.readthedocs.io/en/latest/
97 | super(Podcast, self).save(*args, **kwargs)
98 |
--------------------------------------------------------------------------------
/podcast/site_summary.py:
--------------------------------------------------------------------------------
1 | from podcast.models import Podcast
2 |
3 | from wagtail.admin.site_summary import SummaryItem
4 |
5 | PODCAST_ADMIN_LINK = '/admin/snippets/podcast/podcast/'
6 |
7 | class PodcastSummaryItem(SummaryItem):
8 | template = 'wagtailadmin/home/site_summary_podcast.html'
9 |
10 | def get_context(self):
11 | count = Podcast.objects.all().count()
12 | return {
13 | 'link': PODCAST_ADMIN_LINK,
14 | 'count': count
15 | }
16 |
--------------------------------------------------------------------------------
/podcast/templates/podcast/player.html:
--------------------------------------------------------------------------------
1 | {% load i18n wagtailadmin_tags %}
2 |
3 |
4 |
5 |
6 |
#{{podcast.episode_number}}: {{podcast.title}}
7 |
Download
8 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/podcast/templates/podcast/podcast.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% autoescape off %}
4 |
5 |
6 | {{settings.podcast.PodcastSettings.title}}
7 | {{settings.podcast.PodcastSettings.description}}
8 | https://www.spritesanddice.com/
9 |
10 |
11 |
12 | en
13 |
14 | Games & Hobbies
15 |
16 | contact@spritesanddice.com
17 |
18 | wyatt@spritesanddice.com (Wyatt Krause)
19 |
20 | jon@spritesanddice.com (Jon Glover)
21 |
22 | {{latest_episode.xml_pubdate}}
23 | {{latest_episode.xml_pubdate}}{# TODO: Look for the last modified date of all podcasts and use that instead #}
24 |
25 | {{settings.podcast.PodcastSettings.description}}
26 |
27 |
28 |
29 |
30 |
31 |
32 | video games, board games, independent, nerd, sprites and dice, tabletop, indie games
33 |
34 |
35 | Sprites and Dice
36 |
37 | no
38 | no
39 |
40 |
41 | wyatt@spritesanddice.com
42 | Wyatt Krause
43 |
44 |
45 | {% for episode in episodes %}
46 | -
47 |
{{episode.title}}
48 | {% if episode.related_page %} https://www.spritesanddice.com{{episode.related_page.url}}{% endif %}
49 | {{episode.description}}
50 |
51 | https://www.spritesanddice.com{{episode.file.file.url}}
52 | {{episode.xml_pubdate}}
53 | {{settings.podcast.PodcastSettings.title}}
54 | {{episode.description}}
55 | {{episode.episode_length}}
56 | {{episode.episode_number}}
57 |
58 | {% endfor %}
59 |
60 |
61 | {% endautoescape %}
62 |
63 |
--------------------------------------------------------------------------------
/podcast/templates/podcast/results.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailsnippets/snippets/results.html" %}
2 | {% load i18n wagtailadmin_tags %}
3 |
4 | {% block custom_content %}
5 |
6 | {% if is_searchable %}
7 |
15 | {% endif %}
16 |
17 |
18 |
19 |
20 |
21 |
30 |
31 |
32 |
40 | {% endblock %}
41 |
42 | {% block list_template %}
43 | {% include "podcast/list.html" %}
44 | {% endblock %}
45 |
--------------------------------------------------------------------------------
/podcast/templates/podcast/type_index.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailsnippets/snippets/type_index.html" %}
2 | {% load i18n wagtailadmin_tags %}
3 |
4 | {% block content %}
5 |
6 |
7 | {% block results_template %}
8 | {% include "podcast/results.html" %}
9 | {% endblock %}
10 |
11 |
12 | {% endblock %}
13 |
--------------------------------------------------------------------------------
/podcast/templates/wagtailadmin/home/site_summary_podcast.html:
--------------------------------------------------------------------------------
1 | {% load i18n wagtailadmin_tags %}
2 |
3 |
4 |
5 | {% blocktrans count counter=count with count|intcomma as total %}
6 | {{ total }} Podcast
7 | {% plural %}
8 | {{ total }} Podcasts
9 | {% endblocktrans %}
10 |
11 |
12 |
--------------------------------------------------------------------------------
/podcast/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from . import views
4 |
5 | app_name = 'podcast'
6 | urlpatterns = [
7 | url(r'^podcast/podcast/$', views.list, name='list'),
8 | url(r'^podcast/Podcast/$', views.list, name='list'),
9 | ]
10 |
--------------------------------------------------------------------------------
/podcast/wagtail_hooks.py:
--------------------------------------------------------------------------------
1 | from podcast.site_summary import PodcastSummaryItem, PODCAST_ADMIN_LINK
2 |
3 | from wagtail.admin.menu import MenuItem
4 | from wagtail.core import hooks
5 |
6 | @hooks.register('construct_homepage_summary_items')
7 | def add_podcast_summary_item(request, items):
8 | items.append(PodcastSummaryItem(request))
9 |
10 | @hooks.register('register_admin_menu_item')
11 | def register_color_menu_item():
12 | return MenuItem('Podcast', PODCAST_ADMIN_LINK, classnames='icon icon-fa-headphones', order=400)
13 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==3.0.3
2 | wagtail==2.9
3 | wagtailfontawesome==1.1.4
4 | wagtailmedia>=0.5
5 | wagalytics==1.2
6 | psycopg2-binary>=2.8.5
7 | gunicorn>=20.0.4
8 | wand>=0.5.9
9 |
--------------------------------------------------------------------------------
/search/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/search/__init__.py
--------------------------------------------------------------------------------
/search/templates/search/search.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailcore_tags %}
3 |
4 | {% block body_class %}search-results-page{% endblock %}
5 |
6 | {% block title %}Search Results{% if request.GET.query %} - "{{request.GET.query}}"{% endif %}{% endblock %}
7 | {% block og_title %}Search Results{% if request.GET.query %} - "{{request.GET.query}}"{% endif %}{% endblock %}
8 |
9 | {% block before_page_feed%}
10 |
18 | {% endblock %}
19 |
20 | {% block page_feed %}
21 | {% if pages %}
22 | {% include "navigation/pagination.html" %}
23 |
24 | {% for page in pages %}
25 | {% include "blog/medium_listing.html" %}
26 | {% endfor %}
27 |
28 | {% include "navigation/pagination.html" %}
29 | {% elif search_query %}
30 |
31 | No results found.
32 | {% endif %}
33 | {% endblock %}
34 |
--------------------------------------------------------------------------------
/search/templates/search/search_form.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/search/views.py:
--------------------------------------------------------------------------------
1 | from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
2 | from django.shortcuts import render
3 |
4 | from page.models import BlogPage
5 |
6 | from wagtail.core.models import Page
7 | from wagtail.search.models import Query
8 |
9 |
10 | def search(request):
11 | search_query = request.GET.get('query', None)
12 | page = request.GET.get('page', 1)
13 |
14 | # Search
15 | if search_query:
16 | search_results = BlogPage.objects.live().order_by('-go_live_at').search(search_query)
17 | query = Query.get(search_query)
18 |
19 | # Record hit
20 | query.add_hit()
21 |
22 | else:
23 | search_results = Page.objects.none()
24 |
25 | # Pagination
26 | paginator = Paginator(search_results, 25)
27 |
28 | try:
29 | search_results = paginator.page(page)
30 | except PageNotAnInteger:
31 | search_results = paginator.page(1)
32 | except EmptyPage:
33 | search_results = paginator.page(paginator.num_pages)
34 |
35 | return render(request, 'search/search.html', {
36 | 'search_query': search_query,
37 | 'pages': search_results,
38 | })
39 |
--------------------------------------------------------------------------------
/snippet/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/snippet/__init__.py
--------------------------------------------------------------------------------
/snippet/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.11 on 2019-08-23 01:23
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Game',
16 | fields=[
17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('title', models.CharField(blank=True, max_length=255, null=True)),
19 | ],
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/snippet/migrations/0002_delete_game.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-05 20:44
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('snippet', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.DeleteModel(
14 | name='Game',
15 | ),
16 | ]
17 |
--------------------------------------------------------------------------------
/snippet/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/snippet/migrations/__init__.py
--------------------------------------------------------------------------------
/snippet/templates/wagtailsnippets/snippets/list.html:
--------------------------------------------------------------------------------
1 | {% load i18n wagtailadmin_tags %}
2 |
3 | {% block customcontent %}{% endblock %}
4 |
5 | {% block table %}
6 |
52 | {% endblock %}
53 |
--------------------------------------------------------------------------------
/snippet/templates/wagtailsnippets/snippets/results.html:
--------------------------------------------------------------------------------
1 | {% load i18n %}
2 |
3 | {% block custom_content %}{% endblock %}
4 |
5 | {% if items %}
6 | {% if is_searching %}
7 |
8 | {% blocktrans count counter=items.paginator.count %}
9 | There is {{ counter }} match
10 | {% plural %}
11 | There are {{ counter }} matches
12 | {% endblocktrans %}
13 |
14 | {% endif %}
15 |
16 |
17 | {% block list_template %}
18 | {% include "wagtailsnippets/snippets/list.html" %}
19 | {% endblock %}
20 |
21 | {% url 'wagtailsnippets:list' model_opts.app_label model_opts.model_name as wagtailsnippets_list_url %}
22 | {% include "wagtailadmin/shared/pagination_nav.html" with items=items linkurl=wagtailsnippets_list_url %}
23 | {% else %}
24 | {% if is_searching %}
25 | {% blocktrans %}Sorry, no podcasts match "{{ query_string }} "{% endblocktrans %}
26 | {% else %}
27 | {% url 'wagtailsnippets:add' model_opts.app_label model_opts.model_name as wagtailsnippets_create_url %}
28 | {% blocktrans with snippet_type_name_plural=model_opts.verbose_name_plural %}No {{ snippet_type_name_plural }} have been created. Why not add one ?{% endblocktrans %}
29 | {% endif %}
30 | {% endif %}
31 |
--------------------------------------------------------------------------------
/snippet/templates/wagtailsnippets/snippets/type_index.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/base.html" %}
2 | {% load i18n static %}
3 | {% block titletag %}{% blocktrans with snippet_type_name_plural=model_opts.verbose_name_plural|capfirst %}Snippets {{ snippet_type_name_plural }}{% endblocktrans %}{% endblock %}
4 |
5 | {% block extra_js %}
6 | {{ block.super }}
7 |
14 | {% if can_delete_snippets %}
15 |
16 | {% endif %}
17 | {% endblock %}
18 |
19 | {% block content %}
20 |
47 |
48 |
49 |
50 | {% block results_template %}
51 | {% include "wagtailsnippets/snippets/results.html" %}
52 | {% endblock %}
53 |
54 |
55 | {% endblock %}
56 |
--------------------------------------------------------------------------------
/snippet/urls.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/snippet/urls.py
--------------------------------------------------------------------------------
/snippet/views.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/snippet/views.py
--------------------------------------------------------------------------------
/spritesanddice/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/__init__.py
--------------------------------------------------------------------------------
/spritesanddice/context_processors.py:
--------------------------------------------------------------------------------
1 | import os
2 | from django.conf import settings
3 |
4 | def global_vars(request):
5 | return {
6 | 'VERSION': settings.SPRITES_VERSION
7 | }
8 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-24 18:52
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | initial = True
10 |
11 | dependencies = [
12 | ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='HeaderSettings',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('update_schedule', models.CharField(blank=True, max_length=255, null=True)),
21 | ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
22 | ],
23 | options={
24 | 'abstract': False,
25 | },
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/0002_metadatasettings.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-25 05:26
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('image', '0001_initial'),
11 | ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
12 | ('spritesanddice', '0001_initial'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='MetaDataSettings',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='image.CustomImage')),
21 | ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
22 | ],
23 | options={
24 | 'abstract': False,
25 | },
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/0003_sidebarsettings.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-03 02:56
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
11 | ('spritesanddice', '0002_metadatasettings'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='SidebarSettings',
17 | fields=[
18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19 | ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
20 | ],
21 | options={
22 | 'abstract': False,
23 | },
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/0004_delete_sidebarsettings.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-03 23:21
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('spritesanddice', '0003_sidebarsettings'),
10 | ]
11 |
12 | operations = [
13 | migrations.DeleteModel(
14 | name='SidebarSettings',
15 | ),
16 | ]
17 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/0005_auto_20200509_2228.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-05-09 22:28
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('wagtailcore', '0045_assign_unlock_grouppagepermission'),
11 | ('image', '0003_customimage_game'),
12 | ('spritesanddice', '0004_delete_sidebarsettings'),
13 | ]
14 |
15 | operations = [
16 | migrations.CreateModel(
17 | name='SiteSettings',
18 | fields=[
19 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
20 | ('header_text', models.CharField(blank=True, help_text='Displayed below the site header in desktop mode.', max_length=255, null=True)),
21 | ('slogan', models.CharField(blank=True, max_length=255, null=True)),
22 | ('default_social_thumb', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='image.CustomImage')),
23 | ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
24 | ],
25 | options={
26 | 'abstract': False,
27 | },
28 | ),
29 | migrations.RemoveField(
30 | model_name='metadatasettings',
31 | name='image',
32 | ),
33 | migrations.RemoveField(
34 | model_name='metadatasettings',
35 | name='site',
36 | ),
37 | migrations.DeleteModel(
38 | name='HeaderSettings',
39 | ),
40 | migrations.DeleteModel(
41 | name='MetaDataSettings',
42 | ),
43 | ]
44 |
--------------------------------------------------------------------------------
/spritesanddice/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/migrations/__init__.py
--------------------------------------------------------------------------------
/spritesanddice/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, HelpPanel
4 | from wagtail.contrib.settings.models import BaseSetting, register_setting
5 | from wagtail.images.edit_handlers import ImageChooserPanel
6 |
7 |
8 | @register_setting
9 | class SiteSettings(BaseSetting):
10 | header_text = models.CharField(null=True, blank=True, max_length=255, help_text="Displayed below the site header in desktop mode.")
11 | slogan = models.CharField(null=True, blank=True, max_length=255)
12 |
13 | default_social_thumb = models.ForeignKey(
14 | 'image.CustomImage',
15 | null=True,
16 | blank=True,
17 | on_delete=models.SET_NULL,
18 | related_name='+'
19 | )
20 |
21 | panels = [
22 | FieldPanel('header_text'),
23 | FieldPanel('slogan'),
24 | MultiFieldPanel([
25 | HelpPanel(content="The default image that will show in social media if another isn't available on the page."),
26 | ImageChooserPanel('default_social_thumb'),
27 | ], heading="Social Media Metadata")
28 | ]
29 |
--------------------------------------------------------------------------------
/spritesanddice/settings/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/settings/__init__.py
--------------------------------------------------------------------------------
/spritesanddice/settings/dev.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 | from config import *
3 |
4 | INSTALLED_APPS += [
5 | 'wagtail.contrib.styleguide',
6 | ]
7 |
8 | # SECURITY WARNING: don't run with debug turned on in production!
9 | DEBUG = True
10 |
11 | # SECURITY WARNING: define the correct hosts in production!
12 | ALLOWED_HOSTS = ['*']
13 |
14 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
15 |
16 | try:
17 | from .local import *
18 | except ImportError:
19 | pass
20 |
--------------------------------------------------------------------------------
/spritesanddice/settings/production.py:
--------------------------------------------------------------------------------
1 | from .base import *
2 | from config import *
3 |
4 | INSTALLED_APPS += [
5 | ]
6 |
7 | DEBUG = False
8 |
9 | ALLOWED_HOSTS = ['localhost', '64.227.25.145', 'spritesanddice.com', 'www.spritesanddice.com']
10 |
11 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
12 |
13 | try:
14 | from .local import *
15 | except ImportError:
16 | pass
17 |
--------------------------------------------------------------------------------
/spritesanddice/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/favicon.ico
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Black.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Bold.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-ExtraBold.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-ExtraLight.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Light.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Medium.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Regular.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-SemiBold.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-Thin.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/RobotoSlab-VariableFont_wght.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/RobotoSlab-VariableFont_wght.ttf
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/jaapokki-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/jaapokki-regular.woff
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/opensans-regular-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/opensans-regular-webfont.woff
--------------------------------------------------------------------------------
/spritesanddice/static/fonts/wagtail.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/fonts/wagtail.woff
--------------------------------------------------------------------------------
/spritesanddice/static/img/logo-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/logo-black.png
--------------------------------------------------------------------------------
/spritesanddice/static/img/logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/logo-white.png
--------------------------------------------------------------------------------
/spritesanddice/static/img/podcast.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/podcast.jpg
--------------------------------------------------------------------------------
/spritesanddice/static/img/sd-logo-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/sd-logo-black.png
--------------------------------------------------------------------------------
/spritesanddice/static/img/sd-logo-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/sd-logo-white.png
--------------------------------------------------------------------------------
/spritesanddice/static/img/snd_end_mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/img/snd_end_mark.png
--------------------------------------------------------------------------------
/spritesanddice/static/js/admin.js:
--------------------------------------------------------------------------------
1 | function siteSummaryCountUp(){
2 | $('.stats a span:not(.visuallyhidden)').each(function(){
3 | let span = $(this);
4 | $({ Counter: 0 }).animate({ Counter: $(span).text().replace(',','') }, {
5 | duration: 1000,
6 | easing: 'swing',
7 | step: function (){
8 | $(span).text(Math.ceil(this.Counter));
9 | }
10 | });
11 | });
12 | }
13 |
14 | // Append "Add Child" button to folder pages in page drawer
15 | function pageExplorerDrawerAddChildButton(){
16 | $('.c-explorer__item').each(function(){
17 | let is_folder = $(this).find('.icon-folder-inverse').length > 0;
18 | let has_add_child_button = $(this).find('.add-folder-child').length > 0;
19 | if(is_folder && !has_add_child_button){
20 | let page_id = $(this).find('.c-explorer__item__link').attr('href').replace('/admin/pages/','').replace('/','');
21 | let page_title = $(this).find('.c-explorer__item__title').html();
22 | let last_button = $(this).find('.c-explorer__item__action').last();
23 | $(`
24 |
25 |
26 |
27 | Add a child page to '${page_title}'
28 |
29 |
30 | `).insertBefore(last_button);
31 | }
32 | });
33 | }
34 |
35 | $(document).bind('DOMSubtreeModified',function(){
36 | pageExplorerDrawerAddChildButton();
37 | })
38 |
39 | $(document).ready(function(){
40 | siteSummaryCountUp();
41 | pageExplorerDrawerAddChildButton();
42 | });
43 |
--------------------------------------------------------------------------------
/spritesanddice/static/js/navigation.js:
--------------------------------------------------------------------------------
1 | const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
2 |
3 | var scrollSpeed;
4 | var transparentNav = false;
5 |
6 | function showNav() {
7 | $('nav.navbar').removeClass('hidden');
8 | }
9 |
10 | function hideNav() {
11 | $('nav.navbar').addClass('hidden');
12 | }
13 |
14 | function initFixedNavbar() {
15 | $(document).bind('mousewheel DOMMouseScroll', function(e){
16 | scrollSpeed = e.originalEvent.wheelDelta || e.originalEvent.detail || 0;
17 |
18 | let down = scrollSpeed >= 1;
19 | let up = scrollSpeed <= -1;
20 | if(!isFirefox){
21 | up = scrollSpeed >= 1;
22 | down = scrollSpeed <= -1;
23 | }
24 |
25 | if (down) { hideNav(); }
26 | else if(up) { showNav(); }
27 | });
28 | }
29 |
30 | function initHamburgerButton(){
31 | $('.mobile-menu-trigger').click(function(e){
32 | e.preventDefault();
33 | $('.mobile-drawer').addClass('active');
34 | $('.mobile-overlay').addClass('active');
35 | });
36 | $('.mobile-overlay').click(function(e){
37 | e.preventDefault();
38 | $('.mobile-drawer').removeClass('active');
39 | $('.mobile-overlay').removeClass('active');
40 | })
41 | }
42 |
43 | function placeUserBarInNav(){
44 | if($('.wagtail-userbar').length){
45 | $('.wagtail-userbar').detach().appendTo('nav .userbar-container ul');
46 | }
47 | }
48 |
49 | $(document).ready(function(){
50 | initHamburgerButton();
51 | initFixedNavbar();
52 | });
53 |
54 | // Rip the .wagtail-userbar out of its default location and append it to the nav
55 | $(window).on('load', function(){
56 | placeUserBarInNav();
57 | })
58 |
--------------------------------------------------------------------------------
/spritesanddice/static/js/spritesanddice.js:
--------------------------------------------------------------------------------
1 | function addEndMarkIcon(){
2 | let last_paragraph = $('.blog-page .blog-post-content > .rich-text').last().find('p').last();
3 | let p_not_empty = $.trim(last_paragraph.html()).length;
4 | if (p_not_empty) {
5 | $(last_paragraph).append(" ");
6 | }
7 | }
8 |
9 | function makeExternalLinksTargetBlank(){
10 | // Make all outgoing links open in a new tab
11 | $('a').each(function() {
12 | if( location.hostname === this.hostname || !this.hostname.length ) {
13 | return;
14 | } else {
15 | $(this).attr('target', '_blank');
16 | }
17 | });
18 | }
19 |
20 | // Make it easier to target rich text youtube embeds with CSS (kinda hacky)
21 | function addClassToYouTubeContainer(){
22 | $('.blog-page .blog-post-content iframe').closest('div').addClass('embed')
23 | }
24 |
25 | function initFontAwesome(){
26 | // Prevents FA from converting tags to and breaking CSS
27 | window.FontAwesomeConfig = { autoReplaceSvg: false };
28 | }
29 |
30 | $(document).ready(function(){
31 | initFontAwesome();
32 | addClassToYouTubeContainer();
33 | addEndMarkIcon();
34 | makeExternalLinksTargetBlank();
35 | });
36 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/analytics.scss:
--------------------------------------------------------------------------------
1 | /* Wagalytics Style Fixes and Customizations */
2 |
3 | header .fields .field {
4 | overflow: hidden;
5 |
6 | .field-content {
7 | width: 100%;
8 | }
9 | }
10 |
11 | header form.left {
12 | min-width: 500px;
13 | max-width: 100%;
14 | }
15 |
16 | .wagalytics {
17 |
18 | td.title {
19 | word-break: keep-all;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/home.scss:
--------------------------------------------------------------------------------
1 | header {
2 | .welcome {
3 | text-align: center;
4 | margin: 0;
5 | padding-left: 3em;
6 |
7 | img {
8 | margin: 1em auto;
9 | max-height: 3em;
10 | display: block;
11 | max-width: 100%;
12 | }
13 | }
14 |
15 | .quick-actions {
16 | margin-right: 0;
17 | float: right;
18 |
19 | .row {
20 | display: flex;
21 | flex-direction: row;
22 |
23 | .col {
24 | margin: 1em 0 1em .5em;
25 | max-width: 33%;
26 | display: flex;
27 | flex-direction: column;
28 |
29 | a {
30 | line-height: 3em;
31 |
32 | &:before {
33 | line-height: 2.5em;
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/login.scss:
--------------------------------------------------------------------------------
1 | @import '../colors.scss';
2 |
3 | body.login{
4 | .wrapper{
5 | width: 450px;
6 | max-width: 100%;
7 | margin: 0 auto;
8 | padding: 0;
9 | }
10 |
11 | .content-wrapper {
12 | padding: 1em;
13 | }
14 |
15 | img.login-img{
16 | display: block;
17 | margin: 10px auto;
18 | max-width: 90%;
19 | }
20 |
21 | h1{
22 | font-size: 3em;
23 | text-align: center;
24 | }
25 |
26 | .full .iconfield .input:before{
27 | left: 1em;
28 | }
29 |
30 | .fields li.full{
31 | position: relative;
32 | width: 500px;
33 | margin: auto;
34 | padding: 0;
35 | max-width: 100%;
36 | overflow: hidden;
37 |
38 | @media (max-width: $width-xs) {
39 | input{
40 | padding: 1em;
41 | }
42 | }
43 | @media (min-width: $width-xs) {
44 | input{
45 | padding-left: 4em;
46 | }
47 | }
48 |
49 | &:nth-child(1){
50 | border-top-left-radius: $border-radius;
51 | border-top-right-radius: $border-radius;
52 | }
53 | &:nth-child(2){
54 | border-bottom-left-radius: $border-radius;
55 | border-bottom-right-radius: $border-radius;
56 | }
57 | }
58 |
59 | .submit button{
60 | width:100%;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/podcast.scss:
--------------------------------------------------------------------------------
1 | .podcast-results-title{
2 | position: absolute;
3 | top: 30px;
4 | right: 50px;
5 | }
6 |
7 | .podcast-actions{
8 | float: right;
9 | margin-top: -70px;
10 | }
11 |
12 | #podcast-search.search-form {
13 | right: 50px;
14 | top: 30px;
15 | position: absolute;
16 | border: 1px solid #e6e6e6;
17 | border-radius: 500px;
18 | overflow: hidden;
19 | z-index: 1;
20 | label{
21 | display: none;
22 | }
23 | li{
24 | padding: 0;
25 | }
26 | .field-content{
27 | width: 100%;
28 | }
29 | input{
30 | border: transparent;
31 | }
32 | .error-message{
33 | display:none;
34 | }
35 | .error input{
36 | background:#fafafa;
37 | }
38 | }
39 |
40 | #podcast{
41 | display: flex;
42 | flex-direction: row;
43 | justify-content: flex-start;
44 | align-items: flex-start;
45 | padding: 30px 0;
46 | position: relative;
47 |
48 | #album-art{
49 | max-width: 200px;
50 | img{
51 | border-radius: 3px;
52 | }
53 | }
54 |
55 | #metadata{
56 | max-width: 60%;
57 | padding: 15px 30px;
58 |
59 | h1{
60 | text-transform: capitalize;
61 | }
62 |
63 | .episode-count{
64 | opacity: .8;
65 | }
66 |
67 | > *{
68 | margin-top: 0;
69 | }
70 |
71 | p > a {
72 | white-space: nowrap;
73 | }
74 | }
75 | }
76 |
77 | table.listing#podcast-table{
78 | table-layout: fixed;
79 | width: 100%;
80 |
81 | .title-wrapper{ font-weight: 500; }
82 |
83 | a:not(:hover){ background: transparent }
84 |
85 | a{
86 | transition: 0.3s ease-in-out all;
87 | text-overflow: ellipsis;
88 | overflow: hidden;
89 | white-space: nowrap;
90 | display: block;
91 | }
92 | th, td{
93 | padding: .6em .8em;
94 | text-overflow: ellipsis;
95 | overflow: hidden;
96 | white-space: nowrap;
97 | &.select{ width: 20px; }
98 | &.episode{ width: 15px; }
99 | &.title{ width: 130px; }
100 | &.description{ width: 100px; }
101 | &.length{ width: 60px; }
102 | &.date{ width: 60px; }
103 | &.actions-cell{
104 | width: 100px;
105 | ul.actions{
106 | display:table-cell;
107 | > li{ float: right; margin:0; }
108 | }
109 | }
110 | }
111 | tbody{
112 | border: none;
113 | tr{
114 | border: none;
115 | transition: 0.1s ease-in-out all;
116 | &:nth-child(odd):not(.selected):not(:hover){
117 | background-color: #f8f8f8;
118 | }
119 | td{
120 | &.actions-cell .actions{
121 | visibility: visible;
122 | }
123 | }
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/summary.scss:
--------------------------------------------------------------------------------
1 | section.summary {
2 | background-color: #444;
3 | color: #FFF;
4 |
5 | margin: 0;
6 | padding-top: 0;
7 | padding-bottom: 1em;
8 |
9 | ul.stats {
10 | display: flex;
11 | justify-content: flex-start;
12 | padding: 0;
13 | flex-flow: row nowrap;
14 | overflow-x: scroll;
15 |
16 | li {
17 | float: none;
18 | display: flex;
19 | padding: 1.5em;
20 | margin: 0;
21 | justify-content: flex-start;
22 | white-space: nowrap;
23 | flex-grow: 1;
24 | min-width: 160px;
25 | max-width: 25%;
26 |
27 | a {
28 | display: flex;
29 | flex-direction: column;
30 | width: auto;
31 | flex-grow: 1;
32 | color: #ccc;
33 |
34 | span {
35 | display: inline;
36 | font-size: 2em;
37 | }
38 | }
39 |
40 | &::before {
41 | opacity: 0.66;
42 | font-size: 3em;
43 | }
44 | }
45 |
46 | /* Overrides "display:table" */
47 | &::before {
48 | content: unset;
49 | display: none;
50 | }
51 |
52 | /* Fill empty space in last row */
53 | &::after {
54 | content: " ";
55 | flex: auto;
56 | display: flex;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/admin/typography.scss:
--------------------------------------------------------------------------------
1 | body, p{
2 | font-family: 'Roboto','Open Sans',Helvetica,sans-serif;
3 | }
4 |
5 | h1, h2, h3, h4, h5, h6{
6 | font-family: 'Jaapokki','Roboto Slab',Helvetica,sans-serif;
7 | }
8 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/colors.scss:
--------------------------------------------------------------------------------
1 | // Sprites and Dice Brand Palette
2 | $light-blue: #00ACDB;
3 | $mid-blue: #0098B7;
4 | $darker-blue: #008DA9;
5 | $darkest-blue: #00667F;
6 | $sprites-grey: #232323;
7 |
8 | $light-grey: #CCCCCC;
9 | $lighter-grey: #EEEEEE;
10 |
11 | $text-color: #2E2E2E;
12 | $dark-text: #272727;
13 |
14 | $primary-link-color: #008da8;
15 | $secondary-link-color: #619d5f;
16 |
17 | $white: #F9F9F9;
18 |
19 | // Brand Colors
20 | $facebook: #3A5795;
21 | $twitter: #1DA1F2;
22 | $patreon: #FE5900;
23 | $discord: #7289DA;
24 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/fonts.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Declare @font-face tags here.
3 | */
4 |
5 |
6 | /* ========== Roboto ========== */
7 |
8 | @font-face {
9 | font-family: 'Roboto';
10 | src: local('Roboto Regular'),
11 | local('Roboto-Regular'),
12 | url('../fonts/Roboto-Regular.ttf');
13 | font-weight: normal;
14 | font-style: normal;
15 | font-display: swap;
16 | }
17 |
18 | /* ========== Open Sans ========== */
19 |
20 | @font-face {
21 | font-family: 'Open Sans';
22 | src: local('Open Sans Regular'),
23 | local('Open-Sans-Regular'),
24 | url('../fonts/opensans-regular-webfont.woff') format('woff');
25 | font-weight: normal;
26 | font-style: normal;
27 | font-display: fallback; // Fallback for Roboto
28 | }
29 |
30 |
31 | /* ========== Jaapokki ========== */
32 |
33 | @font-face {
34 | font-family: 'Jaapokki';
35 | src: local('Jaapokki Regular'),
36 | local('Jaapokki-Regular'),
37 | url('../fonts/jaapokki-regular.woff') format('woff');
38 | font-weight: normal;
39 | font-style: normal;
40 | font-display: swap;
41 | }
42 |
43 |
44 | /* ========== Roboto Slab ========== */
45 |
46 | @font-face {
47 | font-family: 'Roboto Slab';
48 | src: local('Roboto Slab Regular'),
49 | local('Roboto-Slab-Regular'),
50 | url('../fonts/RobotoSlab-Regular.ttf');
51 | font-weight: normal;
52 | font-style: normal;
53 | font-display: fallback; // Fallback for Jaapokki
54 | }
55 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/icons.scss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/spritesanddice/static/scss/icons.scss
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/author.scss:
--------------------------------------------------------------------------------
1 | .author-bio {
2 | font-size: 16px;
3 | .user-title {
4 | font-size: 0.8em;
5 | }
6 | }
7 |
8 | .user-bio-small {
9 | .title {
10 | margin-bottom: 0;
11 | }
12 | .subtitle {
13 | margin: 0;
14 | }
15 | }
16 |
17 | div.grid.user-grid {
18 | max-width: 650px;
19 | margin: auto;
20 | }
21 |
22 | .avatar-column {
23 | display: flex;
24 | justify-content: center;
25 | align-content: center;
26 | flex-flow: column nowrap;
27 | padding-right: 0!important;
28 |
29 | img { width: 100%; }
30 | }
31 |
32 | .social-links {
33 | display: flex;
34 | flex-flow: row nowrap;
35 | font-size: 0.8em;
36 |
37 | a {
38 | padding: .5em;
39 | margin: 5px;
40 | background: #aaa;
41 | border-radius: $border-radius;
42 | display: block;
43 | line-height: 1;
44 |
45 | &:nth-child(1){
46 | margin-left: 0;
47 | }
48 | }
49 |
50 | i {
51 | color: $white;
52 | }
53 |
54 | li.twitter a { background: $twitter; }
55 | li.email a { background: #dd4b39; }
56 | li.website a { background: #464646; }
57 | }
58 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/blog-page.scss:
--------------------------------------------------------------------------------
1 | @import '../colors.scss';
2 |
3 | .blog-folder {
4 | .folder-icon {
5 | display: inline-block;
6 | max-height: 2em;
7 | width: auto;
8 | margin-right: .25em;
9 | }
10 | }
11 |
12 | .blog-post-meta {
13 | display: flex;
14 | flex-flow: row wrap;
15 | align-items: center;
16 |
17 | span {
18 | margin-right: 1em;
19 | white-space: nowrap;
20 | line-height: 2;
21 |
22 | .fa {
23 | margin-right: .25em;
24 | color: $light-blue;
25 | }
26 | }
27 | }
28 |
29 | twitter-widget {
30 | margin: auto;
31 | }
32 |
33 | .blog-page {
34 | .blog-post-content {}
35 |
36 | .blog-post-header {
37 | margin: 0;
38 |
39 | img {
40 | width: 100%;
41 | }
42 |
43 | // When the sidebar is gone, let images bleed to the edges
44 | @media (max-width: $width-sm){
45 | margin: 0 -15px;
46 | }
47 | }
48 |
49 | .review-block {
50 | line-height: 1.8;
51 | padding: 10px 20px;
52 | margin: 1.5em 0;
53 | border-left: 5px solid $lighter-grey;
54 | overflow: hidden;
55 |
56 | .box-art {
57 | display: inline;
58 | margin-right: 1em;
59 | }
60 | }
61 |
62 | // Youtube Embeds
63 | div.embed {
64 | position: relative;
65 | padding-bottom: 56.25%;
66 | padding-top: 30px;
67 | height: 0;
68 | overflow: hidden;
69 | margin: 0;
70 | max-width: unset;
71 |
72 | iframe,
73 | object,
74 | embed {
75 | position: absolute;
76 | top: 0;
77 | left: 0;
78 | width: 100%;
79 | height: 100%;
80 | }
81 | }
82 |
83 | .rich-text {
84 | div.embed {
85 | margin: 15px 0;
86 | }
87 | }
88 |
89 |
90 | .social-media{
91 | margin: 0;
92 | margin-bottom: 15px;
93 | display: block;
94 | overflow: hidden;
95 |
96 | a {
97 | color: #fff;
98 | }
99 |
100 | .fa {
101 | margin: 0 5px;
102 | background: transparent;
103 | }
104 |
105 | .fb-like,
106 | .twitter-follow,
107 | .patreon-back {
108 | float: left;
109 | display: inline-block;
110 | width: 33.33%;
111 | margin: 0 auto;
112 | text-align: center;
113 | font-size: 1.2rem;
114 | line-height:2.5;
115 | }
116 |
117 | .fb-like{ background: $facebook; }
118 |
119 | .twitter-follow{ background: $twitter; }
120 |
121 | .patreon-back{ background: $patreon; }
122 | }
123 | }
124 |
125 | .image-block {
126 | text-align: center;
127 |
128 |
129 | p {
130 | padding-left: 1rem;
131 | padding-right: 1rem;
132 | }
133 |
134 | img {
135 | max-height: 75vh; /* dont make em too tall */
136 | width: auto;
137 | margin: auto;
138 | // When the sidebar is visible, give it room to breathe
139 | @media (min-width: $width-sm) {
140 | max-width: 95%;
141 | }
142 | }
143 | // When the sidebar is gone, let images bleed to the edges
144 | @media (max-width: $width-sm){
145 | margin: 0 -15px;
146 | }
147 |
148 | .rich-text{
149 | margin: 1em auto;
150 | max-width: 500px;
151 | font-size: 0.8em;
152 | }
153 | }
154 |
155 | .author-bio {
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/calendar.scss:
--------------------------------------------------------------------------------
1 | body.calendar {
2 | .event {
3 | border-radius: $border-radius;
4 | padding: 1em;
5 | margin-bottom: 1em;
6 |
7 | &.stream {
8 | background-color: purple;
9 | }
10 | &.online {
11 | background-color: blue;
12 | }
13 | &.meatspace {
14 | background-color: yellow;
15 | }
16 | &.convention {
17 | background-color: red;
18 | }
19 | &.other {
20 | background-color: orange;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/home-page.scss:
--------------------------------------------------------------------------------
1 | @import '../colors.scss';
2 |
3 | .home-page{}
4 |
5 | div.blog-post-listing.grid {
6 | margin: 0 auto;
7 | font-size: 16px;
8 |
9 | .blog-post-meta {
10 | max-width: 650px;
11 | display: flex;
12 | flex-direction: column;
13 | margin-top: .15em;
14 | margin-left: auto;
15 | margin-right: auto;
16 | justify-content: flex-start;
17 | align-items: flex-start;
18 | width: 100%;
19 |
20 | a {
21 | color: $secondary-link-color;
22 | }
23 | }
24 |
25 | .title, .subtitle {
26 | line-height: 1.15;
27 | margin: 0 auto;
28 | width: 100%;
29 | max-width: 650px;
30 | }
31 |
32 | > div {
33 | display: flex;
34 | flex-flow: column nowrap;
35 | justify-content: center;
36 | align-content: center;
37 | }
38 |
39 | &.hero {
40 |
41 | > .col-12 {
42 | padding: 0;
43 | }
44 |
45 | .btn{
46 | display: block;
47 | max-width: max-content;
48 | font-size: .8em;
49 | text-align: center;
50 | margin-right: auto;
51 | margin-left: 0;
52 | }
53 |
54 | .hero-image {
55 | margin: -15px;
56 | background: $sprites-grey;
57 | position: relative;
58 | height: 0;
59 | padding-bottom: 44%;
60 | margin-bottom: 1.65em;
61 |
62 | img {
63 | box-shadow: inset 0px -10px 25px -5px rgba(39,39,39,1);
64 | width: 100%;
65 | }
66 | }
67 |
68 | .hero{
69 | .title,.subtitle{
70 | line-height: 1.15;
71 | }
72 | }
73 |
74 | h1, h2, h3, p {
75 | width: 100%;
76 | max-width: 650px;
77 | margin-left: auto;
78 | margin-right: auto;
79 | }
80 | }
81 |
82 | &.small{
83 | max-width: 650px;
84 | padding: 1.25em 0;
85 | }
86 |
87 | &.medium {
88 | padding: 1.65em 0;
89 |
90 | .title {
91 | margin-bottom: .15em;
92 | }
93 | }
94 |
95 | &.large{
96 | padding: 2.5em 0;
97 | max-width: 750px;
98 |
99 | div.title-col {
100 | justify-content: flex-end;
101 | align-items: flex-end;
102 | }
103 | }
104 |
105 | &.hero {
106 | .title, .subtitle {
107 | line-height: 1.65;
108 | }
109 | }
110 |
111 | .listing-image {
112 | height: auto;
113 |
114 | a {
115 | display: block;
116 | position: relative;
117 | }
118 |
119 | img {
120 | width: 100%;
121 | margin: auto;
122 | }
123 |
124 | .placeholder {
125 | background: $sprites-grey;
126 | height: 100%;
127 | width: 100%;
128 | min-height: 200px;
129 | }
130 | }
131 |
132 | // Show an ellipsis after 3 lines of text
133 | .listing-description{
134 | max-height: calc(1.7em * 3);
135 | overflow: hidden;
136 | text-overflow: ellipsis;
137 | display: block;
138 | max-width: 100%;
139 | display: -webkit-box;
140 | -webkit-line-clamp: 3;
141 | -webkit-box-orient: vertical;
142 | max-width: 85%;
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/navigation/footer.scss:
--------------------------------------------------------------------------------
1 | footer{
2 | color: $white;
3 | background: $dark-text;
4 | a {
5 | color: $primary-link-color;
6 | }
7 | }
8 |
9 | .footer-social-icons {
10 | display: flex;
11 | justify-content: space-between;
12 | }
13 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/navigation/header.scss:
--------------------------------------------------------------------------------
1 | header {
2 | width: 100%;
3 | background: $dark-text;
4 | display: block;
5 | color: $white;
6 | padding: 15px 0;
7 | border-bottom: 2px solid #CCC;
8 |
9 | .header-logo{
10 | .update-schedule{
11 | margin-top: 1em;
12 | text-align: center;
13 | text-transform: uppercase;
14 | }
15 | }
16 |
17 | @media (min-width: $width-sm) {
18 | .header-search {
19 | margin-top: 1.6%; // Line up with clear space in site logo in desktop mode
20 | }
21 | }
22 |
23 | .header-search {
24 | .search-input-container {
25 | input {
26 | font-size: .75em;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/navigation/sidebar.scss:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | justify-content: space-between;
3 | display: flex!important;
4 | flex-direction: column;
5 | height: 100%;
6 |
7 | .text {
8 | padding-left: 15px;
9 | padding-right: 15px;
10 | }
11 | }
12 |
13 |
14 | img.sidebar-icon {
15 | display: inline-block;
16 | max-height: 2.25rem;
17 | width: auto;
18 | }
19 |
20 | @media (max-width: $width-lg) {
21 | .cta-prefix,
22 | img.sidebar-icon {
23 | display: none;
24 | }
25 | }
26 |
27 | .sidebar-posts {
28 | display: flex;
29 | flex-direction: column;
30 | margin: 1.65em auto;
31 |
32 | h2, h3, h4, h5 {
33 | a {
34 | color: $sprites-grey;
35 | span { color: $sprites-grey; }
36 | }
37 | }
38 |
39 | // Category title
40 | h4 {
41 | margin-bottom: 0;
42 |
43 | span {
44 | white-space: nowrap;
45 | }
46 | }
47 |
48 | h6 {
49 | margin: 0;
50 | line-height: 1.65;
51 | }
52 |
53 | .sidebar-blog-post {
54 | background: $lighter-grey;
55 | padding-bottom: 1.5em;
56 | margin-bottom: 3em;
57 | flex-direction: column;
58 |
59 | small {
60 | display: inline-block;
61 | }
62 | }
63 |
64 | .byline {
65 | margin: 0;
66 | }
67 |
68 | .post-date {
69 | float: right;
70 | }
71 |
72 | .thumb {
73 | height: 0;
74 | padding-bottom: 66%;
75 | background: $sprites-grey;
76 | position: relative;
77 | margin-bottom: 1.65em;
78 |
79 | img {
80 | width: 100%;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/password-required.scss:
--------------------------------------------------------------------------------
1 | .password-protected-page {
2 | #password-content {
3 | input {
4 | font-size: 1.5em;
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/podcast.scss:
--------------------------------------------------------------------------------
1 | .podcast-player {
2 | display: flex;
3 | flex-direction: row;
4 | align-items: center;
5 | align-content: stretch;
6 | background: #191919; // Match the default background of Firefox's HTML5 audio tag
7 | margin: 15px 0;
8 |
9 | img,
10 | .audio-content {
11 | display: flex;
12 | flex-direction: column;
13 | }
14 |
15 | img {
16 | height: 125px;
17 | width: auto;
18 | }
19 |
20 | .audio-content {
21 | flex-grow: 1;
22 | padding: 0 1em;
23 |
24 | .audio-title {
25 | margin: 0.25em 1em;
26 |
27 | p {
28 | color: #FFF;
29 | margin: 0;
30 | float: left;
31 | }
32 |
33 | a {
34 | display: inline;
35 | float: right;
36 |
37 | svg {
38 | height: 1em;
39 | vertical-align: middle;
40 |
41 | .icon {
42 | stroke: #FFF;
43 | }
44 | }
45 | }
46 | }
47 |
48 | audio {
49 | width: 100%;
50 | display: block;
51 | padding: 0.25em 0;
52 |
53 | &::-webkit-media-controls-panel {
54 | background: #191919;
55 | }
56 |
57 | &::-webkit-media-controls-enclosure {
58 | border-radius: 0;
59 | box-shadow: none;
60 | }
61 |
62 | &::-webkit-media-controls-mute-button,
63 | &::-webkit-media-controls-play-button,
64 | &::-webkit-media-controls-seek-back-button,
65 | &::-webkit-media-controls-seek-forward-button,
66 | &::-webkit-media-controls-fullscreen-button,
67 | &::-webkit-media-controls-rewind-button,
68 | &::-webkit-media-controls-return-to-realtime-button,
69 | &::-webkit-media-controls-toggle-closed-captions-button {
70 | -webkit-filter: invert(100%);
71 | }
72 |
73 | &::-webkit-media-controls-volume-slider {
74 | -webkit-filter: invert(100%);
75 | box-shadow: none;
76 | }
77 |
78 | &::-internal-media-controls-overflow-button {
79 | -webkit-filter: invert(10);
80 | }
81 |
82 | &::-webkit-media-controls-timeline {
83 | -webkit-filter: invert(100%);
84 | }
85 |
86 | &::-webkit-media-controls-time-remaining-display,
87 | &::-webkit-media-controls-current-time-display {
88 | color: #FFF;
89 | text-shadow: none;
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/search.scss:
--------------------------------------------------------------------------------
1 | .search-input-container{
2 | display: flex;
3 | flex-flow: row nowrap;
4 | align-items: stretch;
5 | width: 100%;
6 | overflow: hidden;
7 | font-size: 1rem;
8 |
9 | input, button {
10 | display: flex;
11 | flex-direction: column;
12 | }
13 |
14 | input {
15 | background: $lighter-grey;
16 | color: $sprites-grey;
17 | border: none;
18 | padding: .5em .75em;
19 |
20 | border-top-left-radius: $border-radius;
21 | border-bottom-left-radius: $border-radius;
22 | border-top-right-radius: 0px;
23 | border-bottom-right-radius: 0px;
24 |
25 | width: calc(100% - 3.5em);
26 | }
27 |
28 | button {
29 | border-top-left-radius: 0px;
30 | border-bottom-left-radius: 0px;
31 | border-top-right-radius: $border-radius;
32 | border-bottom-right-radius: $border-radius;
33 |
34 | padding: 0;
35 | margin: 0;
36 | border: none;
37 |
38 | align-items: center; // Vertical Align Icon
39 | font-size: 1em;
40 | padding: .5em;
41 | color: #FFF;
42 | }
43 | }
44 |
45 | // Search Results Page Only
46 | .search-results-page {
47 |
48 | .search-page-header {
49 | align-items: center;
50 |
51 | .search-form {
52 | .search-input-container {
53 | justify-content: flex-end;
54 |
55 | input {
56 | font-size: 1em;
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/main/typography.scss:
--------------------------------------------------------------------------------
1 | body{
2 | color: $text-color;
3 | font-family: 'Roboto',Helvetica,sans-serif;
4 | font-size: 18px;
5 | font-weight: 300;
6 | line-height: 1.65;
7 |
8 | @media (max-width: $width-sm) { font-size: 16px; }
9 | }
10 |
11 | /* keep content from being too wide to read comfortably */
12 | .blog-post-content > .rich-text,
13 | .tags {
14 | margin: auto;
15 | max-width: 650px;
16 | }
17 |
18 | header{
19 | .header-logo .update-schedule{
20 | font-family: 'Jaapokki';
21 | }
22 | }
23 |
24 | nav{
25 | font-family: 'Jaapokki','Roboto',Helvetica,sans-serif;
26 | font-size: 1.15em;
27 | }
28 |
29 | h1, h2, h3, h4, h5, h6 {
30 | font-family: "Jaapokki", "Roboto Slab";
31 | color: $sprites-grey;
32 | margin: 1.15rem 0;
33 | line-height: 1.15;
34 | a {
35 | color: $sprites-grey;
36 | }
37 | }
38 |
39 | .btn {
40 | font-size: 1.15em;
41 | font-family: 'Jaapokki';
42 | }
43 |
44 | h1 { font-size: 3.052em; }
45 | h2 { font-size: 2.441em; }
46 | h3 { font-size: 1.953em; }
47 | h4 { font-size: 1.563em; }
48 | h5 { font-size: 1.25em; }
49 | h6 { font-size: 1em; }
50 |
51 | .subtitle{
52 | font-family: 'Roboto';
53 | color: $sprites-grey;
54 | opacity: 0.6;
55 |
56 | a {
57 | color: $sprites-grey;
58 | }
59 | }
60 |
61 | p {
62 | margin: 1.65rem auto;
63 | line-height: 1.65;
64 | max-width: 650px;
65 |
66 | &:empty {
67 | margin: 0;
68 | }
69 | }
70 |
71 | b {
72 | font-weight: bold;
73 | }
74 |
75 | .rich-text ul {
76 | list-style-type: initial;
77 | padding: initial;
78 | margin: 1.7rem auto;
79 | line-height: 2;
80 | padding: 0 1em;
81 | }
82 |
83 | blockquote {
84 | font-size: 1.25em;
85 | margin: 1.5em auto;
86 | padding: 1em 1.5em;
87 | border-left: 1px solid #aaa;
88 | opacity: .75;
89 | }
90 |
91 | small {
92 | font-size: 0.8em;
93 | opacity: 0.8;
94 |
95 | a {
96 | color: $secondary-link-color;
97 | }
98 | }
99 |
100 | .richtext-image {
101 | height: auto;
102 |
103 | &.full-width {
104 | width: 100%;
105 | }
106 |
107 | &.left {
108 | max-width: 50%;
109 | float: left;
110 | padding-right: 1.5em;
111 | }
112 |
113 | &.right{
114 | max-width: 50%;
115 | float: right;
116 | padding-left: 1.5em;
117 | }
118 | }
119 |
120 | .wagtail-userbar-items {
121 | font-family: 'Roboto', sans-serif;
122 | }
123 |
124 | i.endmark {
125 | content: "";
126 | margin-left: 5px;
127 | margin-bottom: -1px;
128 | height: 15px;
129 | width: 16px;
130 | background-size: 100%;
131 | background-image: url('/static/img/snd_end_mark.png');
132 | display: inline;
133 | }
134 |
135 | .tag {
136 | font-size: .8em;
137 | }
138 |
--------------------------------------------------------------------------------
/spritesanddice/static/scss/variables.scss:
--------------------------------------------------------------------------------
1 | // Keep a consistent border radius on all buttons / tags
2 | $border-radius: 2.5px;
3 |
4 | // Reflex Grid Breakpoints
5 | $width-xs: 576px;
6 | $width-sm: 768px;
7 | $width-md: 992px;
8 | $width-lg: 1200px;
9 | $width-xlg: 1600px;
10 |
--------------------------------------------------------------------------------
/spritesanddice/static/svg/logo-big.svg:
--------------------------------------------------------------------------------
1 | logo
--------------------------------------------------------------------------------
/spritesanddice/stream_blocks.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 |
3 | from users.models import User
4 |
5 | from wagtail.core import blocks
6 | from wagtail.images.blocks import ImageChooserBlock
7 | from wagtail.documents.blocks import DocumentChooserBlock
8 | from wagtail.snippets.blocks import SnippetChooserBlock
9 |
10 | # Generic User Chooser
11 | class UserChooserBlock(blocks.ChooserBlock):
12 | target_model = User
13 | widget = forms.Select
14 |
15 | # Return the key value for the select field
16 | def value_for_form(self, value):
17 | if isinstance(value, self.target_model):
18 | return value.pk
19 | else:
20 | return value
21 |
22 | class Meta:
23 | icon = "user"
24 |
25 |
26 | class ImageBlock(blocks.StructBlock):
27 | image = ImageChooserBlock(required=False)
28 | caption = blocks.RichTextBlock(required=False, features=['bold', 'italic', 'link'])
29 |
30 | class Meta:
31 | icon = "image"
32 | template = 'blocks/image_block.html'
33 |
34 |
35 | class PodcastBlock(blocks.StructBlock):
36 | podcast = SnippetChooserBlock('podcast.Podcast')
37 |
38 | def get_context(self, value, parent_context=None):
39 | context = super().get_context(value, parent_context=parent_context)
40 | context['podcast'] = value['podcast']
41 | return context
42 |
43 | class Meta:
44 | template = 'podcast/player.html'
45 |
46 |
47 | class GameBlock(blocks.StructBlock):
48 | game = SnippetChooserBlock('game.Game')
49 |
50 | def get_context(self, value, parent_context=None):
51 | context = super().get_context(value, parent_context=parent_context)
52 | context['game'] = value['game']
53 | return context
54 |
55 | class Meta:
56 | template = 'blocks/review_block.html'
57 |
58 |
59 | class AuthorBlock(blocks.StructBlock):
60 | user = UserChooserBlock()
61 |
62 | def get_context(self, value, parent_context=None):
63 | context = super().get_context(value, parent_context=parent_context)
64 | context['user'] = value['user']
65 | return context
66 |
67 | class Meta:
68 | template = 'users/author_bio.html'
69 |
70 |
71 | class UserGrid(blocks.StructBlock):
72 | users = blocks.ListBlock(blocks.StructBlock([
73 | ('user', UserChooserBlock()),
74 | ]))
75 |
76 | def get_context(self, value, parent_context=None):
77 | context = super().get_context(value, parent_context=parent_context)
78 | context['users'] = [x['user'] for x in value['users']]
79 | return context
80 |
81 | class Meta:
82 | template = 'users/user_grid.html'
83 |
84 |
85 | # Stream Blocks for all content types
86 | stream_blocks = [
87 | # Default
88 | ('Image', ImageBlock()),
89 | ('Rich_Text', blocks.RichTextBlock()),
90 | ]
91 |
92 | # Basic Pages Only
93 | basic_blocks = stream_blocks + [
94 | ('Author_Bio', AuthorBlock(icon='fa-user')),
95 | ('User_Grid', UserGrid(icon='fa-users')),
96 | ]
97 |
98 | # Blog Page Only
99 | blog_blocks = stream_blocks + [
100 | ('Podcast', PodcastBlock(icon='fa-headphones')),
101 | ('Game', GameBlock(icon='fa-pencil')),
102 | ]
103 |
--------------------------------------------------------------------------------
/spritesanddice/templates/404.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block body_class %}blog-page template-404{% endblock %}
4 |
5 | {% block content %}
6 |
7 |
8 |
9 |
Page not found
10 |
11 |
14 |
15 |
16 | {% include 'navigation/sidebar.html' %}
17 |
18 |
19 | {% endblock %}
20 |
--------------------------------------------------------------------------------
/spritesanddice/templates/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Internal server error
6 |
7 |
8 |
9 | Internal server error
10 |
11 | Sorry, there seems to be an error. Please try again soon.
12 |
13 |
14 |
--------------------------------------------------------------------------------
/spritesanddice/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailuserbar wagtailfontawesome wagtailimages_tags menu_tags %}
2 |
3 |
4 | {% with request.site.site_name as site_name %}
5 |
6 |
7 |
8 |
9 |
10 | {% block title %}
11 | {% if self.seo_title %}{{ self.seo_title }}{% else %}{{ self.title }}{% endif %}
12 | {% endblock %}
13 | {% if self.title != "Sprites and Dice" %} | Sprites and Dice{% else %}| {{settings.spritesanddice.SiteSettings.slogan}}{% endif %}
14 |
15 |
16 | {% if page %}
17 | {% include "page_meta.html"%}
18 | {% else %}
19 | {% include "meta_tags.html" %}
20 | {% endif %}
21 |
22 | {% include "google_analytics.html" %}
23 |
24 |
25 |
26 | {# Preload the most important fonts #}
27 |
28 |
29 |
30 | {# Global stylesheets #}
31 |
32 |
33 |
34 |
35 | {% block extra_css %}
36 | {# Override this in templates to add extra stylesheets #}
37 | {% endblock %}
38 |
39 | {% endwith %}
40 |
41 |
42 | {% wagtailuserbar %}
43 |
44 | {% block header %}
45 | {% include 'navigation/header.html' %}
46 | {% endblock %}
47 |
48 | {% include 'navigation/navbar.html' %}
49 |
50 |
51 | {% block content %}
52 | {% endblock %}
53 |
54 |
55 | {% include 'navigation/footer.html' %}
56 |
57 | {# Global javascript #}
58 |
59 |
60 |
61 |
62 | {% block extra_js %}
63 | {# Override this in templates to add extra javascript #}
64 | {% endblock %}
65 |
66 |
67 |
--------------------------------------------------------------------------------
/spritesanddice/templates/disqus.html:
--------------------------------------------------------------------------------
1 | {% if request.site.hostname == 'www.spritesanddice.com' %}
2 |
3 |
20 |
21 | Please enable Javascript to view the comments .
22 |
23 |
24 | {% endif %}
25 |
--------------------------------------------------------------------------------
/spritesanddice/templates/google_analytics.html:
--------------------------------------------------------------------------------
1 | {% if request.site.hostname == 'www.spritesanddice.com' %}
2 |
3 |
4 |
11 | {% endif %}
12 |
--------------------------------------------------------------------------------
/spritesanddice/templates/meta_tags.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailuserbar wagtailfontawesome wagtailimages_tags menu_tags %}
2 |
3 | {% with settings.spritesanddice.SiteSettings as meta %}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {% image meta.default_social_thumb original as meta_image %}
17 |
18 |
19 |
20 | {% block additional_meta %}{% endblock %}
21 | {% endwith %}
22 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/footer.html:
--------------------------------------------------------------------------------
1 | {% load menu_tags %}
2 |
22 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/header.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
18 |
19 | {% include "navigation/social-link-icons.html" %}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/mobile_menu.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags menu_tags %}
2 |
3 |
4 |
5 |
6 | {% include "search/search_form.html" %}
7 |
8 |
9 |
10 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/mobile_social_links.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Facebook
4 |
5 |
6 |
7 |
8 | Patreon
9 |
10 |
11 |
12 |
13 | Discord
14 |
15 |
16 |
17 |
18 | Twitter
19 |
20 |
21 |
22 |
23 | Email
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/navbar.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailimages_tags menu_tags %}
2 |
3 |
4 |
26 |
27 |
41 |
42 |
43 |
44 | {# Userbar goes here #}
45 |
46 |
47 |
48 |
49 |
50 | {% include "navigation/mobile_menu.html" %}
51 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/pagination.html:
--------------------------------------------------------------------------------
1 | {% load menu_tags %}
2 | {% if pages.paginator.num_pages > 1%}
3 |
38 | {% endif %}
39 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/sidebar.html:
--------------------------------------------------------------------------------
1 | {% load menu_tags %}
2 |
5 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/sidebar_posts.html:
--------------------------------------------------------------------------------
1 | {% load wagtailimages_tags menu_tags %}
2 |
3 | {% for category in categories %}
4 | {# Don't show empty categories #}
5 | {% if category.children|length > 0 %}
6 |
21 | {% endif %}
22 | {% endfor %}
23 |
--------------------------------------------------------------------------------
/spritesanddice/templates/navigation/social-link-icons.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/spritesanddice/templates/page_meta.html:
--------------------------------------------------------------------------------
1 | {% extends "meta_tags.html" %}
2 |
3 | {% load wagtailimages_tags menu_tags %}
4 |
5 | {% block og_title %}{% if page.seo_title %}{{page.seo_title}}{% else %}{{page.title}}{% endif %}{% endblock %}
6 |
7 | {% block og_description %}{{page.subtitle}}{% endblock %}
8 | {% block meta_description %}{{page.subtitle}}{% endblock %}
9 |
10 | {% block og_image %}{% image page.header_image width-1080 as header_image %}https://www.spritesanddice.com{{header_image.url}}{% endblock %}
11 |
12 | {% block og_url %}{{page.full_url}}{% endblock %}
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/spritesanddice/templates/password_required.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load i18n wagtailcore_tags wagtailimages_tags menu_tags %}
3 |
4 | {% block body_class %}password-protected-page{% endblock %}
5 |
6 | {% block title %}Password Required{% endblock %}
7 |
8 | {% block page_feed %}
9 |
10 |
11 |
12 |
{% trans "Password required" %}
13 | {% block password_required_message %}
14 |
{% trans "This page is not intended for the public just yet. If you have a password, enter it below." %}
15 | {% endblock %}
16 |
17 |
26 |
27 |
28 | {% endblock %}
29 |
--------------------------------------------------------------------------------
/spritesanddice/templates/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/spritesanddice/templates/rss.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {% autoescape off %}
4 |
5 |
6 | Sprites and Dice - Life is short, so have fun gaming.{# TODO: Site slogan should be an admin setting #}
7 | Sprites and Dice Posts Feed
8 | https://www.spritesanddice.com/rss.xml
9 |
10 |
11 |
12 | en
13 |
14 | Games & Hobbies
15 |
16 | contact@spritesanddice.com
17 |
18 | wyatt@spritesanddice.com (Wyatt Krause)
19 |
20 | jon@spritesanddice.com (Jon Glover)
21 |
22 | {% for page in pages %}
23 | -
24 |
{{page.title}}
25 | https://www.spritesanddice.com{{page.url}}
26 | {{page.search_description}}
27 | {{page.xml_pubdate}}
28 | {{page.xml_lastbuilddate}}
29 | {{page.author.email}}
30 | https://www.spritesanddice.com{{page.url}}#disqus_thread
31 |
32 | {% endfor %}
33 |
34 | {% endautoescape %}
35 |
36 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/admin_base.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/admin_base.html" %}
2 | {% load static %}
3 |
4 | {% block branding_favicon %}
5 |
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/base.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/base.html" %}
2 | {% load static %}
3 |
4 | {% block branding_logo %}
5 |
6 | {% endblock %}
7 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/home.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/base.html" %}
2 |
3 | {% load wagtailadmin_tags static i18n admin_tags %}
4 |
5 | {% block titletag %}{% trans "Dashboard" %}{% endblock %}
6 |
7 | {% block bodyclass %}homepage{% endblock %}
8 |
9 | {% block extra_css %}
10 | {{ block.super }}
11 |
12 | {% endblock %}
13 |
14 | {% block content %}
15 |
16 |
51 |
52 | {% if panels %}
53 |
54 | {% for panel in panels %}
55 | {{ panel.render }}
56 | {% endfor %}
57 |
58 |
59 |
60 | {% review_copies %}
61 |
62 |
63 |
64 | {% endif %}
65 |
66 | {% endblock %}
67 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/login.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/login.html" %}
2 | {% load static %}
3 |
4 | {% block above_login %}
5 |
6 | {% endblock %}
7 |
8 | {% block branding_login %}{% endblock %}
9 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/review_copies.html:
--------------------------------------------------------------------------------
1 | {% load menu_tags wagtailimages_tags %}
2 |
3 |
4 |
Available Review Copies
5 |
35 |
36 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/skeleton.html:
--------------------------------------------------------------------------------
1 |
2 | {% load static i18n %}
3 | {% get_current_language as LANGUAGE_CODE %}
4 |
5 |
6 |
7 |
8 | {# Dev note - This file literally only exists to customize the title tag #}
9 |
S+D Admin | {% block titletag %}{% endblock %}
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | {% block css %}{% endblock %}
20 |
21 | {% block branding_favicon %}{% endblock %}
22 |
23 |
24 |
27 |
28 | {% blocktrans %}
29 | Javascript is required to use Wagtail, but it is currently disabled.
30 | Here are the instructions how to enable JavaScript in your web browser .
31 | {% endblocktrans %}
32 |
33 |
34 | {% block js %}{% endblock %}
35 |
36 |
37 | {% block furniture %}{% endblock %}
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/spritesanddice/templates/wagtailadmin/userbar/base.html:
--------------------------------------------------------------------------------
1 | {% load static i18n %}
2 |
3 |
4 |
5 |
6 |
7 | {% if request.user.get_short_name|length > 0 %}
8 | {{ request.user.get_short_name }}
9 | {% else %}
10 | {{ request.user.get_username }}
11 | {% endif %}
12 |
13 | {% if request.user.wagtail_userprofile.avatar %}
14 |
19 | {% else %}
20 |
24 | {% endif %}
25 |
26 | {% for item in items %}
27 | {{ item|safe }}
28 | {% endfor %}
29 |
34 |
39 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/spritesanddice/templatetags/admin_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | from game.models import Game
4 |
5 | from page.models import BlogFolder
6 |
7 | register = template.Library()
8 |
9 | @register.inclusion_tag('wagtailadmin/review_copies.html')
10 | def review_copies():
11 | games = filter(lambda game: game.available_copies() > 0, Game.objects.all())
12 | return { 'games': games }
13 |
14 | @register.simple_tag()
15 | def folders():
16 | return BlogFolder.objects.all().exact_type(BlogFolder)
17 |
--------------------------------------------------------------------------------
/spritesanddice/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.conf.urls import include, url
3 | from django.urls import path, re_path
4 | from django.contrib import admin
5 | from django.views.generic import TemplateView
6 |
7 | from wagtail.admin import urls as wagtailadmin_urls
8 | from wagtail.core import urls as wagtail_urls
9 | from wagtail.documents import urls as wagtaildocs_urls
10 |
11 | from page import views as page_views
12 |
13 | from podcast import urls as podcast_urls
14 | from podcast import views as podcast_views
15 |
16 | from users import urls as user_urls
17 | from users import views as user_views
18 |
19 | from search import views as search_views
20 |
21 | urlpatterns = [
22 | url(r'^django-admin/', admin.site.urls),
23 |
24 | url(r'^admin/snippets/', include(podcast_urls)),
25 |
26 | path('calendar/', include('event.urls', namespace='event')),
27 |
28 | url(r'^admin/', include(wagtailadmin_urls)),
29 | url(r'^documents/', include(wagtaildocs_urls)),
30 | url(r'^users/', include(user_urls), name='users'),
31 |
32 | url(r'^tags/(?P
[\w-]+)/', page_views.tag_page, name='pages_by_tag'),
33 | url(r'^tags/?$', page_views.tag_index, name='tags'),
34 |
35 | url(r'^search/$', search_views.search, name='search'),
36 |
37 | url(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', content_type='text/plain')),
38 |
39 | url(r'^podcast\.xml$', podcast_views.get_podcast_feed, name='xml'),
40 |
41 | url(r'^rss\.xml$', page_views.get_rss_feed, name='rss'),
42 |
43 | # For anything not caught by a more specific rule above, hand over to
44 | # Wagtail's page serving mechanism. This should be the last pattern in
45 | # the list:
46 | url(r'', include(wagtail_urls)),
47 | ]
48 |
49 |
50 | if settings.DEBUG:
51 | from django.conf.urls.static import static
52 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns
53 |
54 | # Serve static and media files from development server
55 | urlpatterns += staticfiles_urlpatterns()
56 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
57 |
--------------------------------------------------------------------------------
/spritesanddice/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for spritesanddice project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spritesanddice.settings.production")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/spritesanddice/wsgi_dev.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for spritesanddice project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "spritesanddice.settings.dev")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/users/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/users/__init__.py
--------------------------------------------------------------------------------
/users/forms.py:
--------------------------------------------------------------------------------
1 | from django import forms
2 | from django.utils.translation import ugettext_lazy as _
3 |
4 | from users.models import User
5 |
6 | from wagtail.admin.edit_handlers import RichTextFieldPanel
7 | from wagtail.admin.forms import WagtailAdminModelForm
8 | from wagtail.admin.rich_text import get_rich_text_editor_widget
9 | from wagtail.core.fields import RichTextField
10 | from wagtail.users.forms import UserEditForm, UserCreationForm, AvatarPreferencesForm
11 | from wagtail.users.models import UserProfile
12 |
13 | from pprint import pprint
14 |
15 | # For users to edit their own Bio in Account Settings
16 | class AuthorBioForm(WagtailAdminModelForm):
17 | title = forms.CharField()
18 | bio = RichTextField()
19 |
20 | show_email = forms.BooleanField(required=False, label="Show email publicly?")
21 | twitter = forms.CharField(required=False, label="Twitter Username")
22 | website = forms.URLField(required=False, label="Personal Website")
23 |
24 | class Meta:
25 | model = User
26 | fields = (
27 | "title",
28 | "bio",
29 |
30 | "show_email",
31 | "twitter",
32 | "website",
33 | )
34 |
35 |
36 | # For admins to edit users' bios in the User Edit form
37 | class CustomUserEditForm(UserEditForm):
38 | title = forms.CharField()
39 | bio = RichTextField()
40 |
41 | show_email = forms.BooleanField(required=False, label="Show email publicly?")
42 | twitter = forms.CharField(required=False, label="Twitter Username")
43 | website = forms.URLField(required=False, label="Personal Website")
44 |
45 | avatar = forms.ImageField(required=False, label=_("Upload a profile picture"))
46 |
47 | # Use a custom save hook to save the submitted avatar to the User's UserProfile
48 | # since it's not accessible via the UserEditForm
49 | def save(self):
50 | user = self.instance
51 | avatar = self.cleaned_data.get('avatar')
52 |
53 | if avatar:
54 | try: # Create New Profile
55 | profile = UserProfile.objects.get(user=user)
56 | profile.avatar = avatar
57 | profile.save()
58 | except: # Save new Profile
59 | profile = UserProfile(user=user, avatar=avatar)
60 | profile.save()
61 |
62 | user.save()
63 | super(CustomUserEditForm, self).save(commit=True)
64 | return user
65 |
--------------------------------------------------------------------------------
/users/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-17 00:08
2 |
3 | import django.contrib.auth.models
4 | import django.contrib.auth.validators
5 | from django.db import migrations, models
6 | import django.utils.timezone
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ('auth', '0011_update_proxy_permissions'),
15 | ]
16 |
17 | operations = [
18 | migrations.CreateModel(
19 | name='User',
20 | fields=[
21 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('password', models.CharField(max_length=128, verbose_name='password')),
23 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
24 | ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
25 | ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
26 | ('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
27 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
28 | ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
29 | ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
30 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
31 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
32 | ('title', models.CharField(blank=True, max_length=250)),
33 | ('bio', models.TextField(blank=True, max_length=600)),
34 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
35 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
36 | ],
37 | options={
38 | 'verbose_name': 'user',
39 | 'verbose_name_plural': 'users',
40 | 'abstract': False,
41 | },
42 | managers=[
43 | ('objects', django.contrib.auth.models.UserManager()),
44 | ],
45 | ),
46 | ]
47 |
--------------------------------------------------------------------------------
/users/migrations/0002_auto_20200422_0253.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-22 02:53
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='user',
15 | name='facebook',
16 | field=models.URLField(blank=True, max_length=250),
17 | ),
18 | migrations.AddField(
19 | model_name='user',
20 | name='patreon',
21 | field=models.URLField(blank=True, max_length=250),
22 | ),
23 | migrations.AddField(
24 | model_name='user',
25 | name='show_email',
26 | field=models.BooleanField(default=False, verbose_name='Show email publicly?'),
27 | ),
28 | migrations.AddField(
29 | model_name='user',
30 | name='steam',
31 | field=models.URLField(blank=True, max_length=250),
32 | ),
33 | migrations.AddField(
34 | model_name='user',
35 | name='twitter',
36 | field=models.CharField(blank=True, max_length=250),
37 | ),
38 | migrations.AddField(
39 | model_name='user',
40 | name='website',
41 | field=models.URLField(blank=True, max_length=250),
42 | ),
43 | ]
44 |
--------------------------------------------------------------------------------
/users/migrations/0003_auto_20200422_0311.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-22 03:11
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0002_auto_20200422_0253'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='user',
15 | name='show_email',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/users/migrations/0004_auto_20200422_0311.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.0.3 on 2020-04-22 03:11
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('users', '0003_auto_20200422_0311'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='user',
15 | name='facebook',
16 | ),
17 | migrations.RemoveField(
18 | model_name='user',
19 | name='patreon',
20 | ),
21 | migrations.RemoveField(
22 | model_name='user',
23 | name='steam',
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/users/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Sprites-and-Dice/sprites-and-dice/d314d24a6ae7c8bc891ebeab7c63dd27651d771f/users/migrations/__init__.py
--------------------------------------------------------------------------------
/users/models.py:
--------------------------------------------------------------------------------
1 | from django.contrib.auth.models import AbstractUser
2 | from django.db import models
3 |
4 | from wagtail.core.fields import RichTextField
5 |
6 | class User(AbstractUser):
7 | title = models.CharField(max_length=250, blank=True)
8 | bio = models.TextField(max_length=600, blank=True)
9 | # bio = RichTextField(blank=True) # Does not currently work - check in on https://github.com/wagtail/wagtail/issues/5961 for updates
10 |
11 | # Social Media Info
12 | show_email = models.BooleanField(default=False)
13 | twitter = models.CharField(max_length=250, blank=True)
14 | website = models.URLField(max_length=250, blank=True)
15 |
16 | # Make it easier to read usernames in chooser forms
17 | def __str__(self):
18 | return self.get_full_name()
19 |
20 | # IE "gif", "png"
21 | def avatar_file_type(self):
22 | if self.wagtail_userprofile.avatar:
23 | return self.wagtail_userprofile.avatar.file.name[-3:]
24 | return None
25 |
--------------------------------------------------------------------------------
/users/templates/users/author.html:
--------------------------------------------------------------------------------
1 | {% if user %}
2 | {{user.get_full_name}}
3 | {% endif %}
4 |
--------------------------------------------------------------------------------
/users/templates/users/author_bio.html:
--------------------------------------------------------------------------------
1 | {% load menu_tags %}
2 |
3 | {% if user %}
4 |
5 |
6 |
7 |
8 |
12 |
13 | {% include "users/author_social_links.html" %}
14 |
15 |
16 |
17 |
18 | {% if user.title %}
19 |
{{user.title}}
20 | {% endif %}
21 |
{{user.bio}}
22 |
23 |
24 |
27 |
28 |
29 | {% endif %}
30 |
--------------------------------------------------------------------------------
/users/templates/users/author_social_links.html:
--------------------------------------------------------------------------------
1 |
2 | {% if user.email and user.show_email %}
3 |
4 |
5 |
6 |
7 |
8 | {% endif %}
9 | {% if user.website %}
10 |
11 |
12 |
13 |
14 |
15 | {% endif %}
16 | {% if user.twitter %}
17 |
22 | {% endif %}
23 |
24 |
--------------------------------------------------------------------------------
/users/templates/users/user_grid.html:
--------------------------------------------------------------------------------
1 |
2 | {% for user in users %}
3 |
13 | {% endfor %}
14 |
15 |
--------------------------------------------------------------------------------
/users/templates/users/user_index.html:
--------------------------------------------------------------------------------
1 | {% extends "page/base_page.html" %}
2 | {% load static wagtailimages_tags customuser_tags menu_tags %}
3 |
4 | {% block title %}Users{% endblock %}
5 | {% block og_title %}Users{% endblock %}
6 |
7 | {% block body_class %}user-index{% endblock %}
8 |
9 | {% block page_feed %}
10 | {% include "users/user_grid.html" %}
11 | {% endblock %}
12 |
--------------------------------------------------------------------------------
/users/templates/wagtailadmin/account/change_bio.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailadmin/base.html" %}
2 | {% load i18n %}
3 |
4 | {% block titletag %}{% trans "Change bio" %}{% endblock %}
5 | {% block content %}
6 | {% trans "Change bio" as change_str %}
7 | {% include "wagtailadmin/shared/header.html" with title=change_str %}
8 |
9 |
20 | {% endblock %}
21 |
--------------------------------------------------------------------------------
/users/templates/wagtailusers/users/edit.html:
--------------------------------------------------------------------------------
1 | {% extends "wagtailusers/users/edit.html" %}
2 |
3 | {% load wagtailadmin_tags i18n menu_tags %}
4 |
5 | {% block extra_fields %}
6 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.avatar %}
7 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.title %}
8 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.bio %}
9 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.show_email %}
10 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.twitter %}
11 | {% include "wagtailadmin/shared/field_as_li.html" with field=form.website %}
12 | {% endblock extra_fields %}
13 |
--------------------------------------------------------------------------------
/users/templatetags/customuser_tags.py:
--------------------------------------------------------------------------------
1 | from django import template
2 |
3 | register = template.Library()
4 |
5 | @register.inclusion_tag('users/author.html')
6 | def author(user=None):
7 | return {
8 | 'user': user
9 | }
10 |
11 | @register.inclusion_tag('users/author_bio.html')
12 | def author_bio(user=None):
13 | return {
14 | 'user': user
15 | }
16 |
--------------------------------------------------------------------------------
/users/urls.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import url
2 |
3 | from . import views
4 |
5 | app_name = 'users'
6 | urlpatterns = [
7 | url(r'^(?P[\w-]+)/?$', views.user_page, name='page'),
8 | url(r'', views.user_index, name='all_users'),
9 | ]
10 |
--------------------------------------------------------------------------------
/users/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import redirect, render
2 | from django.utils.translation import ugettext_lazy as _
3 | from django.shortcuts import get_object_or_404
4 | from django.http import Http404, HttpResponse, HttpResponseRedirect
5 | from django.db.models import Q
6 |
7 | from wagtail.admin import messages
8 |
9 | from users.forms import AuthorBioForm
10 |
11 | from users.models import User
12 |
13 | from page.models import BlogPage
14 |
15 | def change_bio(request):
16 | if request.method == 'POST':
17 | form = AuthorBioForm(request.POST, instance=request.user)
18 |
19 | if form.is_valid():
20 | form.save()
21 | messages.success(request, _("Your bio has been changed successfully!"))
22 | return redirect('wagtailadmin_account')
23 | else:
24 | form = AuthorBioForm(instance=request.user)
25 |
26 | return render(request, 'wagtailadmin/account/change_bio.html', {
27 | 'form': form,
28 | })
29 |
30 | def user_index(request):
31 | users = User.objects.all()
32 | return render(request, 'users/user_index.html', { 'users': users })
33 |
34 | def user_page(request, username=None):
35 | user = User.objects.filter(username=username).first()
36 | if user:
37 | return render(request, 'page/user_page.html', { 'user': user })
38 |
39 | # No username found, check if the URL matches a user's full name
40 | else:
41 | # Drupal-era user URLs were `/users/full-name-slug/`
42 | # attempt to un-slugify the username and search by full name
43 | first_name = username.split('-')[0]
44 | last_name = ' '.join(username.split('-')[1:])
45 |
46 | # Some old last names have hyphens, so search for last names with spaces OR hyphens
47 | users = User.objects
48 | users = users.filter(first_name__iexact=first_name)
49 | users = users.filter(Q(last_name__iexact=last_name.replace(' ','-')) | Q(last_name__iexact=last_name))
50 |
51 | user = users.first()
52 |
53 | # 301 Redirect to the new /users/ URL
54 | if user:
55 | username = user.username
56 | return HttpResponseRedirect('/users/{}/'.format(username), status=301)
57 | # 302 Redirect to /users/ index
58 | else:
59 | return HttpResponseRedirect('/users/', status=302)
60 |
--------------------------------------------------------------------------------
/users/wagtail_hooks.py:
--------------------------------------------------------------------------------
1 | from django.conf.urls import include, url
2 | from django.utils.translation import ugettext_lazy as _
3 | from django.urls import reverse
4 |
5 | from users.views import change_bio
6 |
7 | from wagtail.core import hooks
8 |
9 | @hooks.register('register_admin_urls')
10 | def register_admin_urls():
11 | return [
12 | url(r'^account/bio/$', change_bio, name='wagtailadmin_custom_account_change_bio'),
13 | ]
14 |
15 | @hooks.register('register_account_menu_item')
16 | def register_account_change_bio(request):
17 | return {
18 | 'url': reverse('wagtailadmin_custom_account_change_bio'),
19 | 'label': _('Change bio'),
20 | 'help_text': _('Update your author bio.'),
21 | }
22 |
--------------------------------------------------------------------------------