├── django
├── .gitattributes
├── website
│ ├── wagtail_vue
│ │ ├── apps
│ │ │ ├── __init__.py
│ │ │ └── pages
│ │ │ │ ├── __init__.py
│ │ │ │ ├── migrations
│ │ │ │ ├── __init__.py
│ │ │ │ ├── 0004_homepage_content.py
│ │ │ │ ├── 0001_initial.py
│ │ │ │ ├── 0003_flexpage.py
│ │ │ │ ├── 0002_auto_20181106_0418.py
│ │ │ │ ├── 0005_auto_20181111_0459.py
│ │ │ │ ├── 0006_auto_20181111_0506.py
│ │ │ │ ├── 0007_flexpage_content.py
│ │ │ │ ├── 0008_auto_20181113_1657.py
│ │ │ │ ├── 0009_auto_20181113_1721.py
│ │ │ │ └── 0010_auto_20181114_0847.py
│ │ │ │ ├── tests.py
│ │ │ │ ├── admin.py
│ │ │ │ ├── views.py
│ │ │ │ ├── apps.py
│ │ │ │ ├── models.py
│ │ │ │ └── streamfields.py
│ │ ├── static
│ │ │ ├── css
│ │ │ │ └── .gitkeep
│ │ │ ├── js
│ │ │ │ └── .gitkeep
│ │ │ └── images
│ │ │ │ └── .gitkeep
│ │ ├── wagtail_vue
│ │ │ ├── __init__.py
│ │ │ ├── settings
│ │ │ │ ├── __init__.py
│ │ │ │ ├── production.py
│ │ │ │ ├── database.sqlite3
│ │ │ │ ├── dev.py
│ │ │ │ └── settings_base.py
│ │ │ ├── api.py
│ │ │ ├── urls.py
│ │ │ └── wsgi.py
│ │ ├── media
│ │ │ ├── images
│ │ │ │ ├── skateboard.original.jpg
│ │ │ │ ├── skateboard.max-165x165.jpg
│ │ │ │ ├── skateboard.max-800x600.jpg
│ │ │ │ ├── hidethepainharold.original.jpg
│ │ │ │ ├── hidethepainharold.max-165x165.jpg
│ │ │ │ ├── hidethepainharold.max-800x600.jpg
│ │ │ │ ├── stefan-kunze-26932.max-165x165.jpg
│ │ │ │ ├── earth-608366-unsplash.max-165x165.jpg
│ │ │ │ ├── adam-krowitz-389725-unsplash.max-165x165.jpg
│ │ │ │ └── hidethepainharold.2e16d0ba.fill-100x100.jpg
│ │ │ ├── original_images
│ │ │ │ ├── skateboard.jpeg
│ │ │ │ ├── hidethepainharold.jpg
│ │ │ │ ├── stefan-kunze-26932.jpg
│ │ │ │ ├── earth-608366-unsplash.jpg
│ │ │ │ └── adam-krowitz-389725-unsplash.jpg
│ │ │ └── documents
│ │ │ │ └── Wagtail__Vue.js_Slides_Vue.odp
│ │ ├── manage.py
│ │ └── templates
│ │ │ └── base.html
│ ├── requirements
│ │ ├── production.txt
│ │ ├── req_base.txt
│ │ └── dev.txt
│ └── requirements.txt
├── .flake8
├── .isort.cfg
├── README.md
├── bash_aliases
├── .editorconfig
└── Dockerfile
├── vue
├── .browserslistrc
├── vue.config.js
├── babel.config.js
├── postcss.config.js
├── public
│ ├── favicon.ico
│ ├── loading.gif
│ └── index.html
├── src
│ ├── assets
│ │ └── logo.png
│ ├── views
│ │ ├── NotFound.vue
│ │ ├── FlexPage.vue
│ │ ├── HomePage.vue
│ │ └── WagtailPageHandler.vue
│ ├── router.js
│ ├── components
│ │ ├── streamfields
│ │ │ ├── RichTextBlock.vue
│ │ │ ├── ContentBlock.vue
│ │ │ ├── ImageBlock.vue
│ │ │ ├── ImageGalleryBlock.vue
│ │ │ ├── CallToActionBlock.vue
│ │ │ └── ButtonBlock.vue
│ │ ├── WagtailPage.vue
│ │ ├── WagtailDocument.vue
│ │ ├── WagtailImage.vue
│ │ ├── Streamfield.vue
│ │ └── SiteHeader.vue
│ ├── main.js
│ ├── App.vue
│ └── api.js
├── .gitignore
├── README.md
├── .eslintrc.js
├── Dockerfile
├── NOTES.md
├── package.json
├── TODO.md
└── SLIDES.md
├── .gitignore
├── docker-compose.yml
├── README.md
├── Makefile
└── LICENSE
/django/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/__init__.py:
--------------------------------------------------------------------------------
1 | # yup
--------------------------------------------------------------------------------
/django/website/wagtail_vue/static/css/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/static/js/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/static/images/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/settings/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vue/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/vue/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | lintOnSave: false
3 | }
4 |
--------------------------------------------------------------------------------
/vue/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/vue/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/vue/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/vue/public/favicon.ico
--------------------------------------------------------------------------------
/vue/public/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/vue/public/loading.gif
--------------------------------------------------------------------------------
/vue/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/vue/src/assets/logo.png
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/tests.py:
--------------------------------------------------------------------------------
1 | from django.test import TestCase
2 |
3 | # Create your tests here.
4 |
--------------------------------------------------------------------------------
/django/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude =
3 | migrations,
4 | __init__.py,
5 | settings,
6 | max-line-length = 119
7 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/views.py:
--------------------------------------------------------------------------------
1 | from django.shortcuts import render
2 |
3 | # Create your views here.
4 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/settings/production.py:
--------------------------------------------------------------------------------
1 | """Overwrite settings with production settings when going live."""
2 |
--------------------------------------------------------------------------------
/django/website/requirements/production.txt:
--------------------------------------------------------------------------------
1 | # production that isn't in development. ie. monitoring services.
2 | -r req_base.txt
3 |
4 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class PagesConfig(AppConfig):
5 | name = 'pages'
6 |
--------------------------------------------------------------------------------
/django/website/requirements/req_base.txt:
--------------------------------------------------------------------------------
1 | bpython
2 | Django==2.1
3 | django-adminactions==1.*
4 | django-extensions==1.*
5 | Pillow==4.*
6 | wagtail==2.3
7 | django-cors-headers==2.4.*
8 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/skateboard.original.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/skateboard.original.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/original_images/skateboard.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/original_images/skateboard.jpeg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/settings/database.sqlite3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/wagtail_vue/settings/database.sqlite3
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/skateboard.max-165x165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/skateboard.max-165x165.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/skateboard.max-800x600.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/skateboard.max-800x600.jpg
--------------------------------------------------------------------------------
/django/website/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file is here because many Platforms as a Service look for
2 | # requirements.txt in the root directory of a project.
3 | -r requirements/production.txt
4 |
--------------------------------------------------------------------------------
/django/website/requirements/dev.txt:
--------------------------------------------------------------------------------
1 | # Local development dependencies go here
2 | -r req_base.txt
3 | django-debug-toolbar==1.9.1
4 | django-debug-toolbar-template-profiler
5 | Werkzeug==0.10.*
6 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/hidethepainharold.original.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/hidethepainharold.original.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/original_images/hidethepainharold.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/original_images/hidethepainharold.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/original_images/stefan-kunze-26932.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/original_images/stefan-kunze-26932.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/documents/Wagtail__Vue.js_Slides_Vue.odp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/documents/Wagtail__Vue.js_Slides_Vue.odp
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/hidethepainharold.max-165x165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/hidethepainharold.max-165x165.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/hidethepainharold.max-800x600.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/hidethepainharold.max-800x600.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/stefan-kunze-26932.max-165x165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/stefan-kunze-26932.max-165x165.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/original_images/earth-608366-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/original_images/earth-608366-unsplash.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/earth-608366-unsplash.max-165x165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/earth-608366-unsplash.max-165x165.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/original_images/adam-krowitz-389725-unsplash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/original_images/adam-krowitz-389725-unsplash.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/adam-krowitz-389725-unsplash.max-165x165.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/adam-krowitz-389725-unsplash.max-165x165.jpg
--------------------------------------------------------------------------------
/django/website/wagtail_vue/media/images/hidethepainharold.2e16d0ba.fill-100x100.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyshka/wagtail-vue-talk/HEAD/django/website/wagtail_vue/media/images/hidethepainharold.2e16d0ba.fill-100x100.jpg
--------------------------------------------------------------------------------
/django/.isort.cfg:
--------------------------------------------------------------------------------
1 | [settings]
2 | line_length=119
3 | indent=' '
4 | multi_line_output=5
5 | known_django=django
6 | sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
7 | skip=migrations
8 | include_trailing_comma=True
9 |
--------------------------------------------------------------------------------
/django/README.md:
--------------------------------------------------------------------------------
1 | # Wagtail Vue Talk: backend setup
2 | > Django + Wagtail
3 |
4 | ## Requires Docker
5 |
6 | ## Installation
7 | 1. `make build`
8 | 2. `make up`
9 | 3. `make enter`
10 | 4. `django-admin.py runserver 0.0.0.0:8000`
11 | 5. Go to `http://localhost:8000/admin/`
12 | * login is `demo` / `demo123`
13 |
--------------------------------------------------------------------------------
/vue/src/views/NotFound.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
404 - Page Not Found
6 |
7 |
8 |
9 |
10 |
15 |
--------------------------------------------------------------------------------
/vue/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw*
22 |
--------------------------------------------------------------------------------
/vue/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue"
2 | import Router from "vue-router"
3 |
4 | import WagtailPageHandler from "./views/WagtailPageHandler.vue"
5 |
6 | Vue.use(Router)
7 |
8 | export default new Router({
9 | mode: "history",
10 | base: process.env.BASE_URL,
11 | routes: [
12 | {
13 | path: "*",
14 | component: WagtailPageHandler,
15 | },
16 | ],
17 | })
18 |
--------------------------------------------------------------------------------
/vue/README.md:
--------------------------------------------------------------------------------
1 | # Development
2 | ```
3 | # install dependencies
4 | npm install
5 |
6 | # start local dev server
7 | npm run serve
8 | ```
9 |
10 | ## Docker
11 | ```
12 | # build image and start container
13 | make up
14 |
15 | # enter container
16 | make enter
17 |
18 | # start local dev server (inside container)
19 | npm run serve
20 |
21 | # stop and remove container
22 | make clean
23 | ```
24 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/RichTextBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 |
12 |
21 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django’s command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 | if __name__ == '__main__':
7 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wagtail_vue.settings.production') # noqa: E501
8 |
9 | from django.core.management import execute_from_command_line
10 |
11 | execute_from_command_line(sys.argv)
12 |
--------------------------------------------------------------------------------
/django/bash_aliases:
--------------------------------------------------------------------------------
1 | # Django Aliases
2 | alias dj="django-admin.py"
3 | alias djr="django-admin.py runserver 0.0.0.0:8000"
4 |
5 | # Print Message
6 | # Pass format specifies to printf which tells printf to take each argument it
7 | # gets and print it, followed by a newline.
8 | printf "%s\n" \
9 | "Here are some common aliases for our stack:" \
10 | 'dj="django-admin.py"' \
11 | 'djr="django-admin.py runserver 0.0.0.0:8000"'
12 |
--------------------------------------------------------------------------------
/vue/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | '@vue/prettier'
9 | ],
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
13 | },
14 | parserOptions: {
15 | parser: 'babel-eslint'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/ContentBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
18 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/ImageBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python
2 | *.py[cod]
3 | *.egg*
4 | local_dev.py
5 | .mypy_cache
6 |
7 | # Temporary files
8 | *.swp
9 | *.swo
10 | *.tmp
11 | *~
12 |
13 | # Tags file
14 | /tags
15 |
16 | # Packages
17 | *.7z
18 | *.dmg
19 | *.gz
20 | *.iso
21 | *.jar
22 | *.rar
23 | *.tar
24 | *.zip
25 |
26 | # Logs and databases
27 | *.log
28 | *.db
29 |
30 | # OS generated files
31 | .DS_Store*
32 | thumbs.db
33 | Thumbs.db
34 | Desktop.ini
35 |
36 | # Front-end files
37 | node_modules
38 |
39 | local_production.py
40 | local_dev.py
41 |
42 | .vimcache
43 | .vscode
44 |
--------------------------------------------------------------------------------
/vue/src/components/WagtailPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
24 |
--------------------------------------------------------------------------------
/vue/src/views/FlexPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
{{ this.page.title }}
6 |
7 |
8 |
9 |
10 |
15 |
16 |
17 |
18 |
19 |
25 |
--------------------------------------------------------------------------------
/vue/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue"
2 | import VueMeta from "vue-meta"
3 | import VueLazyload from 'vue-lazyload'
4 |
5 | import App from "./App.vue"
6 | import router from "./router"
7 | import Streamfield from "@/components/Streamfield.vue"
8 |
9 | Vue.config.productionTip = false
10 |
11 | // Vue addons
12 | Vue.use(VueMeta)
13 | Vue.use(VueLazyload, {
14 | loading: '/loading.gif',
15 | })
16 |
17 | // Register streamfield as global component
18 | Vue.component("streamfield", Streamfield)
19 |
20 | new Vue({
21 | router,
22 | render: h => h(App),
23 | }).$mount("#app")
24 |
--------------------------------------------------------------------------------
/django/.editorconfig:
--------------------------------------------------------------------------------
1 | # Defines the coding style for different editors and IDEs.
2 | # http://editorconfig.org
3 |
4 | # top-most EditorConfig file
5 | root = true
6 |
7 | # Rules for source code.
8 | [*]
9 | charset = utf-8
10 | end_of_line = lf
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | # Rules for Python code.
17 | [*.py]
18 | indent_size = 4
19 |
20 | # Rules for tool configuration.
21 | [{package.json,*.yml, *.yaml}]
22 | indent_size = 2
23 |
24 | # Rules for markdown documents.
25 | [*.md]
26 | trim_trailing_whitespace = false
27 |
--------------------------------------------------------------------------------
/vue/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
18 |
19 |
29 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0004_homepage_content.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-11 04:38
2 |
3 | from django.db import migrations
4 | import pages.streamfields
5 | import wagtail.core.fields
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('pages', '0003_flexpage'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='homepage',
17 | name='content',
18 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock())], null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/vue/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Wagtail + Vue.js
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/vue/src/components/WagtailDocument.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
26 |
--------------------------------------------------------------------------------
/vue/src/components/WagtailImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
27 |
--------------------------------------------------------------------------------
/vue/src/api.js:
--------------------------------------------------------------------------------
1 | import axios from "axios"
2 |
3 | // TODO: set hostname + port dynamically
4 |
5 | export function getWagtailPage(id) {
6 | return axios.get(`//localhost:8000/api/v2/pages/${id}/`)
7 | }
8 |
9 | export function getWagtailPageByPath(path) {
10 | return axios.get(`//localhost:8000/api/v2/pages/find/?html_path=${path}`)
11 | }
12 |
13 | export function getWagtailPagesInMenu() {
14 | return axios.get(
15 | "//localhost:8000/api/v2/pages/?show_in_menus=true&fields=_,html_url,title"
16 | )
17 | }
18 |
19 | export function getWagtailImage(id) {
20 | return axios.get(`//localhost:8000/api/v2/images/${id}/`)
21 | }
22 |
23 | export function getWagtailDocument(id) {
24 | return axios.get(`//localhost:8000/api/v2/documents/${id}/`)
25 | }
26 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | backend:
4 | network_mode: "bridge"
5 | build: ./django
6 | container_name: wagtail_vue_backend
7 | image: wagtail_vue:backend
8 | ports:
9 | - "8000:8000"
10 | volumes:
11 | - ${WINDIR}./django:/app
12 | stdin_open: true
13 | command: /bin/sh -c "while true; do echo hi; sleep 1; done;"
14 | frontend:
15 | network_mode: "bridge"
16 | links:
17 | - backend
18 | build: ./vue
19 | container_name: wagtail_vue_frontend
20 | image: wagtail_vue:frontend
21 | ports:
22 | - "8080:8080"
23 | volumes:
24 | - ./vue:/app
25 | - /app/node_modules
26 | stdin_open: true
27 | command: /bin/sh -c "while true; do echo hi; sleep 1; done;"
28 |
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # wagtail-vue-talk
2 | How to vue wagtail in a better light
3 |
4 |
5 | ## Development with Docker
6 |
7 | The Django server will run on port `8000`, and the Node.js server compiling the Vue.js app will run on port `8080`.
8 |
9 | To access the Wagtail admin go to http://localhost:8000/admin/, login is `demo` / `demo123`.
10 |
11 | The Vue.js app is expecting the Wagtail API to be avaialable at `localhost:8000/api/v2/`.
12 |
13 | ```
14 | # build image and start containers
15 | make up
16 |
17 | # enter backend (python) container
18 | make enter
19 |
20 | # start django server (inside container)
21 | django-admin.py runserver 0.0.0.0:8000
22 |
23 | # enter frontend (node.js) container
24 | make enter_fe
25 |
26 | # start node server (inside container)
27 | npm run serve
28 |
29 | # stop and remove containers
30 | make clean
31 | ```
32 |
--------------------------------------------------------------------------------
/vue/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:10-alpine
2 | MAINTAINER Bryan Hyshka
3 |
4 | # Set a term for terminal inside the container, can't clear without it
5 | ENV TERM xterm-256color
6 |
7 | # Prefix path with global node_modules folder
8 | # This allows npm package binaries to be available everywhere
9 | ENV PATH /app/node_modules/.bin:$PATH
10 |
11 | # Update, install, and cleanup
12 | # RUN apk update \
13 | # && apk add git \
14 | # && rm -rf /var/cache/apk/*
15 |
16 | # Add the project requirements
17 | # This will add the package.json and package-lock.json if it exists
18 | ADD package*.json /app/
19 |
20 | # Install the requirements
21 | RUN /bin/sh -c 'cd /app && npm install && npm cache clean --force'
22 |
23 | # The code should be symlinked to this directory
24 | WORKDIR /app
25 |
26 | # Expose the 8080 port
27 | EXPOSE 8080
28 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/ImageGalleryBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 |
26 |
27 |
37 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/CallToActionBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
{{ this.block.value.title }}
5 |
6 |
7 |
14 |
15 |
16 |
17 |
18 |
19 |
31 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/api.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Urls for app."""
3 |
4 | from wagtail.api.v2.endpoints import PagesAPIEndpoint
5 | from wagtail.api.v2.router import WagtailAPIRouter
6 | from wagtail.images.api.v2.endpoints import ImagesAPIEndpoint
7 | from wagtail.documents.api.v2.endpoints import DocumentsAPIEndpoint
8 |
9 | # Create the router. "wagtailapi" is the URL namespace
10 | api_router = WagtailAPIRouter('wagtailapi')
11 |
12 | # Add the three endpoints using the "register_endpoint" method.
13 | # The first parameter is the name of the endpoint (eg. pages, images). This
14 | # is used in the URL of the endpoint
15 | # The second parameter is the endpoint class that handles the requests
16 | api_router.register_endpoint('pages', PagesAPIEndpoint)
17 | api_router.register_endpoint('images', ImagesAPIEndpoint)
18 | api_router.register_endpoint('documents', DocumentsAPIEndpoint)
19 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-06 04:04
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', '0040_page_draft_title'),
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 | 'verbose_name_plural': 'Home Pages',
23 | 'verbose_name': 'Home Page',
24 | },
25 | bases=('wagtailcore.page',),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0003_flexpage.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-10 16:51
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', '0040_page_draft_title'),
11 | ('pages', '0002_auto_20181106_0418'),
12 | ]
13 |
14 | operations = [
15 | migrations.CreateModel(
16 | name='FlexPage',
17 | fields=[
18 | ('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')),
19 | ],
20 | options={
21 | 'verbose_name_plural': 'Flex Pages',
22 | 'verbose_name': 'Flex Page',
23 | },
24 | bases=('wagtailcore.page',),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0002_auto_20181106_0418.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-06 04:18
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 | ('wagtailimages', '0021_image_file_hash'),
11 | ('pages', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='homepage',
17 | name='banner_image',
18 | field=models.ForeignKey(help_text='An optional banner image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image'),
19 | ),
20 | migrations.AddField(
21 | model_name='homepage',
22 | name='banner_subtitle',
23 | field=models.CharField(blank=True, help_text='An optional banner subtitle', max_length=50, null=True),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/vue/NOTES.md:
--------------------------------------------------------------------------------
1 | # Wagtail + Vue.js Notes
2 |
3 | ## Intro
4 | - Goals
5 | - Lay some foundation for using Wagtail as a headless CMS
6 | - Learn more about the inner workings of Wagtail and Vue.js
7 | - Vue is a javascript framework commonly used for building user interfaces and single page applications.
8 |
9 | ## Flavor
10 | - I'm not stopping here
11 | - How can we improve this
12 | - Library agnostic, should be able to reproduce results in any framework (React, etc.)
13 |
14 | ## Scaffolding with `vue-cli`
15 | ```
16 | # Install vue-cli
17 | npm install -g @vue/cli
18 |
19 | # Create new project in folder "vue"
20 | vue create vue
21 |
22 | # Options:
23 | # manually select features: Babel, Router, Linter / Formatter
24 | # user history mode for router (yes)
25 | # eslint + prettier
26 | # disable lint on save, enable lint + fix on commit
27 | # in dedicated config files
28 | # save as a preset (no)
29 |
30 | # Move into project folder
31 | cd vue
32 |
33 | # Start development server
34 | npm run serve
35 | ```
36 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build
2 |
3 | SHELL := /bin/bash
4 | CONTAINERNAME_BACKEND=wagtail_vue_backend
5 | IMAGENAME_BACKEND=wagtail_vue:backend
6 | CONTAINERNAME_FRONTEND=wagtail_vue_frontend
7 | IMAGENAME_FRONTEND=wagtail_vue:frontend
8 |
9 | build: ## Build the Docker images
10 | docker-compose -p wagtail_vue build
11 |
12 | up: build ## Bring the Docker containers up
13 | docker-compose -p wagtail_vue up -d || echo 'Already up!'
14 |
15 | upwin: ## Bring the Docker container up for bash on ubuntu folk
16 | export WINDIR="$(subst /mnt/c,//c,$(CURDIR))/" && make up
17 |
18 | lint: build ## Lint the python code.
19 | docker run -v $(CURDIR)/django:/app $(IMAGENAME_BACKEND) /bin/bash -c 'flake8 website'
20 |
21 | down: ## Stop the backend Docker container
22 | docker-compose -p wagtail_vue stop
23 |
24 | enter: ## Enter backend container
25 | docker exec -it $(CONTAINERNAME_BACKEND) /bin/bash
26 |
27 | enter_fe: ## Enter frontend container
28 | docker exec -it $(CONTAINERNAME_FRONTEND) /bin/sh
29 |
30 | clean: ## Stop and remove all Docker containers
31 | docker-compose down
32 |
--------------------------------------------------------------------------------
/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "axios": "^0.18.0",
12 | "lodash": "^4.17.11",
13 | "tachyons": "^4.11.1",
14 | "vue": "^2.5.17",
15 | "vue-meta": "^1.5.5",
16 | "vue-router": "^3.0.1"
17 | },
18 | "devDependencies": {
19 | "@vue/cli-plugin-babel": "^3.0.5",
20 | "@vue/cli-plugin-eslint": "^3.0.5",
21 | "@vue/cli-service": "^3.0.5",
22 | "@vue/eslint-config-prettier": "^4.0.0",
23 | "eslint": "^5.8.0",
24 | "eslint-plugin-vue": "^5.0.0-0",
25 | "lint-staged": "^7.2.2",
26 | "vue-lazyload": "^1.2.6",
27 | "vue-template-compiler": "^2.5.17"
28 | },
29 | "gitHooks": {
30 | "pre-commit": "lint-staged"
31 | },
32 | "lint-staged": {
33 | "*.js": [
34 | "vue-cli-service lint",
35 | "git add"
36 | ],
37 | "*.vue": [
38 | "vue-cli-service lint",
39 | "git add"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/django/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM ubuntu:16.04
2 | MAINTAINER Kalob Taulien
3 |
4 | # Needed for better experience in container terminal
5 | ENV TERM=xterm-256color
6 |
7 | # Update and install
8 | RUN apt-get update && apt-get install -y \
9 | git \
10 | wget \
11 | # Python, remove 3 for wagtail sites
12 | python3-dev \
13 | python3-pip
14 |
15 | # Set the encoding to avoid issues with internationalization packages.
16 | ENV LANG C.UTF-8
17 | ENV LC_ALL C.UTF-8
18 |
19 | RUN pip3 install --upgrade pip
20 |
21 | # Add the project requirements
22 | ADD website/requirements /opt/requirements
23 |
24 | # Install the requirements, remove 3 for wagtail
25 | RUN /bin/bash -c 'cd /opt \
26 | && pip3 install -r requirements/dev.txt'
27 |
28 | # Set the needed variables
29 | ENV PYTHONPATH=/app/website/wagtail_vue:/app/website/wagtail_vue/apps
30 | ENV DJANGO_SETTINGS_MODULE=wagtail_vue.settings.dev
31 |
32 | # change to /app for the working directory, you should mount the local dir volume here
33 | WORKDIR /app
34 |
35 | EXPOSE 8000
36 |
37 | # Add bash aliases
38 | ADD bash_aliases /root/.bash_aliases
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Bryan Hyshka and Kalob Taulien
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0005_auto_20181111_0459.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-11 04:59
2 |
3 | from django.db import migrations
4 | import pages.streamfields
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 | ('pages', '0004_homepage_content'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='homepage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock()), ('image', pages.streamfields.ImageBlock()), ('nested_streams', wagtail.core.blocks.StructBlock([('card', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('content', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul'], required=False)), ('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=True))])))]))], null=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/templates/base.html:
--------------------------------------------------------------------------------
1 | {% load static wagtailuserbar %}
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {% block page_title %}{% endblock page_title %}
11 |
12 |
13 | {% block extra_meta %}{% endblock extra_meta %}
14 |
15 |
16 |
17 | {% block extra_head %}{% endblock %}
18 |
19 |
20 | {% include "includes/accessibility.html" with header=header_class %}
21 |
22 |
23 |
24 | {% block header %}{% endblock header %}
25 |
26 | {% block content %}
27 |
28 | {% block footer %}{% endblock footer %}
29 |
30 |
31 | {% wagtailuserbar %}
32 |
33 |
34 | {% block extra_js %}{% endblock extra_js %}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/vue/src/views/HomePage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
{{ this.page.title }}
8 |
{{ this.page.banner_subtitle }}
9 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
40 |
41 |
46 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/urls.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Urls for app."""
3 | from django.conf import settings
4 | from django.conf.urls import include, static, url
5 | from django.contrib import admin
6 | from django.contrib.staticfiles.urls import staticfiles_urlpatterns
7 |
8 | from wagtail.admin import urls as wagtailadmin_urls
9 | from wagtail.core import urls as wagtail_urls
10 | from wagtail.documents import urls as wagtaildocs_urls
11 |
12 | from .api import api_router
13 |
14 |
15 | urlpatterns = [
16 | url(r'^django-admin/', admin.site.urls),
17 |
18 | url(r'^api/v2/', api_router.urls),
19 | url(r'^admin/', include(wagtailadmin_urls)),
20 | url(r'^documents/', include(wagtaildocs_urls)),
21 |
22 | # For anything not caught by a more specific rule above, hand over to
23 | # Wagtail's page serving mechanism. This should be the last pattern in
24 | # the list:
25 | url(r'', include(wagtail_urls)),
26 | ]
27 |
28 | if settings.DEBUG:
29 | import debug_toolbar
30 | urlpatterns += staticfiles_urlpatterns()
31 | urlpatterns += static.static(
32 | settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
33 | urlpatterns += (
34 | url(r'^__debug__/', include(debug_toolbar.urls)),
35 | )
36 |
--------------------------------------------------------------------------------
/vue/src/components/Streamfield.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
35 |
36 |
41 |
--------------------------------------------------------------------------------
/vue/src/components/SiteHeader.vue:
--------------------------------------------------------------------------------
1 |
2 |
24 |
25 |
26 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for wagtail_vue project.
3 |
4 | This module contains the WSGI application used by Django's development server
5 | and any production WSGI deployments. It should expose a module-level variable
6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
7 | this application via the ``WSGI_APPLICATION`` setting.
8 |
9 | Usually you will have the standard Django WSGI application here, but it also
10 | might make sense to replace the whole Django WSGI application with a custom one
11 | that later delegates to the Django one. For example, you could introduce WSGI
12 | middleware here, or combine a Django application with an application of another
13 | framework.
14 |
15 | """
16 | from os.path import abspath, dirname
17 | from sys import path
18 |
19 | SITE_ROOT = dirname(dirname(abspath(__file__)))
20 | path.append(SITE_ROOT)
21 |
22 | # This application object is used by any WSGI server configured to use this
23 | # file. This includes Django's development server, if the WSGI_APPLICATION
24 | # setting points here.
25 | from django.core.wsgi import get_wsgi_application # noqa # isort:skip noqa
26 | application = get_wsgi_application()
27 |
28 | # Apply WSGI middleware here.
29 | # from helloworld.wsgi import HelloWorldApplication
30 | # application = HelloWorldApplication(application)
31 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0006_auto_20181111_0506.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-11 05:06
2 |
3 | from django.db import migrations
4 | import pages.streamfields
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 | ('pages', '0005_auto_20181111_0459'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='homepage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock()), ('image', pages.streamfields.ImageBlock()), ('nested_streams', wagtail.core.blocks.StructBlock([('card', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('content', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul'], required=False)), ('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=True))])))])), ('carousel', wagtail.core.blocks.StreamBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('quotation', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('author', wagtail.core.blocks.CharBlock())]))]))], null=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0007_flexpage_content.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-11 16:11
2 |
3 | from django.db import migrations
4 | import pages.streamfields
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 | ('pages', '0006_auto_20181111_0506'),
14 | ]
15 |
16 | operations = [
17 | migrations.AddField(
18 | model_name='flexpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock()), ('image', pages.streamfields.ImageBlock()), ('nested_streams', wagtail.core.blocks.StructBlock([('card', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('content', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul'], required=False)), ('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=True))])))])), ('carousel', wagtail.core.blocks.StreamBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('quotation', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('author', wagtail.core.blocks.CharBlock())]))]))], null=True),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/vue/TODO.md:
--------------------------------------------------------------------------------
1 | # NOTES
2 |
3 | ## SSR/Prerendering
4 | TODO
5 | - https://ssr.vuejs.org/
6 | - https://nuxtjs.org/
7 | - https://github.com/chrisvfritz/prerender-spa-plugin
8 |
9 | ## Noteworthy packages
10 | - vue.js https://vuejs.org
11 | - vue-cli https://cli.vuejs.org/
12 | - vue-router https://router.vuejs.org/
13 | - vue-meta https://github.com/declandewet/vue-meta
14 | - axios https://github.com/axios/axios
15 |
16 | ## Meta data
17 | TODO: document
18 | - all code is in WagtailPageHandler
19 |
20 | ## Async components for page views
21 | TODO: document
22 | - all code is in WagtailPageHandler
23 | - loads vue components for page template as-needed
24 | - integreated into webpack so they get split into separate bundles
25 |
26 | ## Page transition
27 | TODO: document
28 | - all code is in WagtailPageHandler
29 | - using vue.js transition
30 | - page views need to be keyed (to route path) and positioned absolute
31 |
32 | ## Lazy load images
33 | TODO: document
34 | - code in main.js and WagtailImage.vue
35 | - using vue-lazyload (https://github.com/hilongjw/vue-lazyload)
36 |
37 | ## Misc
38 | - setup static host for staging
39 | - admin on subdomain
40 | - page preview
41 | - try yarn
42 |
43 | ## Gotchas
44 | - Had to register streamfield component globally
45 | - Can't assign a component dynamically from within vue-router
46 | - Had to manually remove domain + port from URLs from the API to make then a relative path we could use for the router (Improve: move this into the router logic)
47 |
--------------------------------------------------------------------------------
/vue/src/components/streamfields/ButtonBlock.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
58 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/settings/dev.py:
--------------------------------------------------------------------------------
1 | """Development settings and globals."""
2 | from .settings_base import * # noqa
3 |
4 | # ======== DEBUG CONFIGURATION
5 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
6 | DEBUG = True
7 | TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa
8 | # ======== END DEBUG CONFIGURATION
9 |
10 |
11 | # ======== EMAIL CONFIGURATION
12 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
13 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
14 | # ======== END EMAIL CONFIGURATION
15 |
16 |
17 | # ======== DATABASE CONFIGURATION
18 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
19 | DATABASES = {
20 | 'default': {
21 | 'ENGINE': 'django.db.backends.sqlite3',
22 | 'NAME': '/app/website/wagtail_vue/wagtail_vue/settings/database.sqlite3',
23 | }
24 | }
25 | # ======== END DATABASE CONFIGURATION
26 |
27 |
28 | # ======== CACHE CONFIGURATION
29 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#caches
30 | CACHES = {
31 | 'default': {
32 | 'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
33 | }
34 | }
35 | # ======== END CACHE CONFIGURATION
36 |
37 |
38 | # ======== TOOLBAR CONFIGURATION
39 | # See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation # noqa
40 | INSTALLED_APPS += ( # noqa
41 | 'debug_toolbar',
42 | )
43 |
44 | MIDDLEWARE += ( # noqa
45 | 'debug_toolbar.middleware.DebugToolbarMiddleware',
46 | )
47 |
48 |
49 | # See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation # noqa
50 | INTERNAL_IPS = ('127.0.0.1', '172.17.0.1',)
51 |
52 | # ======== END TOOLBAR CONFIGURATION
53 |
54 | ALLOWED_HOSTS += [ # noqa
55 | 'localhost',
56 | 'backend',
57 | ]
58 |
59 | CORS_ORIGIN_ALLOW_ALL = True
60 |
61 | try:
62 | from .local_dev import * # noqa
63 | except ImportError:
64 | pass
65 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0008_auto_20181113_1657.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-13 23:57
2 |
3 | from django.db import migrations
4 | import pages.streamfields
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 | ('pages', '0007_flexpage_content'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='flexpage',
19 | name='content',
20 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock()), ('image', pages.streamfields.ImageBlock()), ('nested_streams', wagtail.core.blocks.StructBlock([('card', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('content', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul'], required=False)), ('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=True))])))])), ('carousel', wagtail.core.blocks.StreamBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('quotation', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('author', wagtail.core.blocks.CharBlock())]))]))], blank=True, null=True),
21 | ),
22 | migrations.AlterField(
23 | model_name='homepage',
24 | name='content',
25 | field=wagtail.core.fields.StreamField([('richtext', pages.streamfields.RichTextBlock()), ('image', pages.streamfields.ImageBlock()), ('nested_streams', wagtail.core.blocks.StructBlock([('card', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=True)), ('content', wagtail.core.blocks.RichTextBlock(features=['bold', 'italic', 'ol', 'ul'], required=False)), ('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=True))])))])), ('carousel', wagtail.core.blocks.StreamBlock([('image', wagtail.images.blocks.ImageChooserBlock()), ('quotation', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('author', wagtail.core.blocks.CharBlock())]))]))], blank=True, null=True),
26 | ),
27 | ]
28 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/models.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """Page models."""
3 | from django.db import models
4 | from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
5 | from wagtail.images.edit_handlers import ImageChooserPanel
6 | from wagtail.core.models import Page
7 | from wagtail.api import APIField
8 | from wagtail.images.api.fields import ImageRenditionField
9 | from wagtail.core.fields import StreamField
10 |
11 | from .streamfields import ContentBlock, ImageGalleryBlock, CallToActionBlock
12 |
13 |
14 | class HomePage(Page):
15 | """A home page class."""
16 |
17 | template = "cms/pages/home_page.html"
18 | subpage_types = ['pages.FlexPage']
19 |
20 | banner_subtitle = models.CharField(
21 | max_length=50, blank=True, null=True, help_text="An optional banner subtitle"
22 | )
23 | banner_image = models.ForeignKey(
24 | "wagtailimages.Image",
25 | null=True,
26 | blank=False,
27 | on_delete=models.SET_NULL,
28 | related_name="+",
29 | help_text="An optional banner image",
30 | )
31 |
32 | content = StreamField([
33 | ('ContentBlock', ContentBlock()),
34 | ('ImageGalleryBlock', ImageGalleryBlock()),
35 | ('CallToActionBlock', CallToActionBlock()),
36 | ], null=True, blank=True)
37 |
38 | content_panels = [
39 | FieldPanel("title", classname="full title"),
40 | ImageChooserPanel("banner_image"),
41 | FieldPanel("banner_subtitle"),
42 | StreamFieldPanel('content'),
43 | ]
44 |
45 | api_fields = [
46 | APIField("title"),
47 | APIField("banner_subtitle"),
48 | APIField("banner_image"),
49 | APIField("banner_image_thumbnail", serializer=ImageRenditionField("fill-100x100", source="banner_image")),
50 | APIField("content"),
51 | ]
52 |
53 | class Meta:
54 | """Meta information."""
55 |
56 | verbose_name = "Home Page"
57 | verbose_name_plural = "Home Pages"
58 |
59 |
60 | class FlexPage(Page):
61 | """A Flexible page class. Used for generic pages that don't have a true purpose."""
62 |
63 | template = "cms/pages/flex_page.html"
64 | subpage_types = []
65 |
66 | content = StreamField([
67 | ('ContentBlock', ContentBlock()),
68 | ('ImageGalleryBlock', ImageGalleryBlock()),
69 | ('CallToActionBlock', CallToActionBlock()),
70 | ], null=True, blank=True)
71 |
72 | content_panels = [
73 | FieldPanel("title", classname="full title"),
74 | StreamFieldPanel('content'),
75 | ]
76 |
77 | api_fields = [
78 | APIField("title"),
79 | APIField("content"),
80 | ]
81 |
82 | class Meta:
83 | """Meta information."""
84 |
85 | verbose_name = "Flex Page"
86 | verbose_name_plural = "Flex Pages"
87 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/streamfields.py:
--------------------------------------------------------------------------------
1 | """Streamfields for Wagtail Pages."""
2 | from wagtail.core import blocks
3 | from wagtail.images.blocks import ImageChooserBlock
4 | from wagtail.documents.blocks import DocumentChooserBlock
5 |
6 |
7 | class RichTextBlock(blocks.RichTextBlock):
8 | """Rich text content."""
9 |
10 | class Meta:
11 | """Provide additional meta information."""
12 |
13 | icon = "edit"
14 | label = "Richtext"
15 |
16 |
17 | class ButtonBlock(blocks.StructBlock):
18 | """Button block."""
19 |
20 | text = blocks.CharBlock(required=True)
21 | page = blocks.PageChooserBlock(required=False)
22 | document = DocumentChooserBlock(required=False)
23 | external_link = blocks.CharBlock(required=False)
24 |
25 | class Meta:
26 | """Provide attional meta information."""
27 |
28 | icon = "link"
29 | label = "Button"
30 |
31 |
32 | class ImageBlock(blocks.StructBlock):
33 | """Image Content."""
34 | POSITIONS_LRF_OPTIONS = (
35 | ('left', 'Left'),
36 | ('right', 'Right'),
37 | ('full', 'Full'),
38 | )
39 |
40 | image = ImageChooserBlock(required=True)
41 | caption = blocks.CharBlock(required=False)
42 | align = blocks.ChoiceBlock(choices=POSITIONS_LRF_OPTIONS)
43 |
44 | class Meta:
45 | """Provide attional meta information."""
46 |
47 | icon = "image"
48 | label= "Image"
49 |
50 |
51 | class ContentBlock(blocks.StructBlock):
52 | """
53 | Content - stream block example
54 | - Richtext
55 | - Image (center, left, right)
56 | - Button
57 | """
58 |
59 | content = blocks.StreamBlock([
60 | ('RichTextBlock', RichTextBlock()),
61 | ('ButtonBlock', ButtonBlock()),
62 | ('ImageBlock', ImageBlock()),
63 | ])
64 |
65 | class Meta:
66 | """Provide additional meta information."""
67 |
68 | icon = "doc-full"
69 | label = "Content"
70 |
71 |
72 | class ImageGalleryBlock(blocks.StructBlock):
73 | """
74 | Image Gallery - list block example
75 | - Image
76 | """
77 |
78 | images = blocks.ListBlock(blocks.StructBlock([
79 | ('ImageBlock', ImageChooserBlock(required=True)),
80 | ]))
81 |
82 | class Meta:
83 | """Provide additional meta information."""
84 |
85 | icon = "image"
86 | label = "Image Gallery"
87 |
88 |
89 | class CallToActionBlock(blocks.StructBlock):
90 | """
91 | Call to Action - struct block example
92 | """
93 |
94 | title = blocks.CharBlock(required=False)
95 | text = blocks.RichTextBlock(
96 | features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'link'], required=False)
97 | buttons = blocks.ListBlock(ButtonBlock(required=False), default=[])
98 |
99 | class Meta:
100 | """Provide additional meta information."""
101 |
102 | icon = "pick"
103 | label = "Call to Action"
104 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0009_auto_20181113_1721.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-14 00:21
2 |
3 | from django.db import migrations
4 | import pages.streamfields
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.documents.blocks
8 | import wagtail.images.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('pages', '0008_auto_20181113_1657'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='flexpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('content', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.StreamBlock([('text', pages.streamfields.RichTextBlock()), ('button', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))])), ('image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.core.blocks.CharBlock(required=False)), ('align', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right'), ('full', 'Full')]))]))]))])), ('image_gallery', wagtail.core.blocks.StructBlock([('images', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True))])))])), ('call_to_action', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=False)), ('text', wagtail.core.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'link'], required=False)), ('buttons', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))], required=False), default=[]))]))], blank=True, null=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='homepage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('content', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.StreamBlock([('text', pages.streamfields.RichTextBlock()), ('button', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))])), ('image', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.core.blocks.CharBlock(required=False)), ('align', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right'), ('full', 'Full')]))]))]))])), ('image_gallery', wagtail.core.blocks.StructBlock([('images', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True))])))])), ('call_to_action', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=False)), ('text', wagtail.core.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'link'], required=False)), ('buttons', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))], required=False), default=[]))]))], blank=True, null=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/vue/src/views/WagtailPageHandler.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
108 |
109 |
117 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/apps/pages/migrations/0010_auto_20181114_0847.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1 on 2018-11-14 15:47
2 |
3 | from django.db import migrations
4 | import pages.streamfields
5 | import wagtail.core.blocks
6 | import wagtail.core.fields
7 | import wagtail.documents.blocks
8 | import wagtail.images.blocks
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | dependencies = [
14 | ('pages', '0009_auto_20181113_1721'),
15 | ]
16 |
17 | operations = [
18 | migrations.AlterField(
19 | model_name='flexpage',
20 | name='content',
21 | field=wagtail.core.fields.StreamField([('ContentBlock', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.StreamBlock([('RichTextBlock', pages.streamfields.RichTextBlock()), ('ButtonBlock', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))])), ('ImageBlock', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.core.blocks.CharBlock(required=False)), ('align', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right'), ('full', 'Full')]))]))]))])), ('ImageGalleryBlock', wagtail.core.blocks.StructBlock([('images', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('ImageBlock', wagtail.images.blocks.ImageChooserBlock(required=True))])))])), ('CallToActionBlock', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=False)), ('text', wagtail.core.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'link'], required=False)), ('buttons', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))], required=False), default=[]))]))], blank=True, null=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='homepage',
25 | name='content',
26 | field=wagtail.core.fields.StreamField([('ContentBlock', wagtail.core.blocks.StructBlock([('content', wagtail.core.blocks.StreamBlock([('RichTextBlock', pages.streamfields.RichTextBlock()), ('ButtonBlock', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))])), ('ImageBlock', wagtail.core.blocks.StructBlock([('image', wagtail.images.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.core.blocks.CharBlock(required=False)), ('align', wagtail.core.blocks.ChoiceBlock(choices=[('left', 'Left'), ('right', 'Right'), ('full', 'Full')]))]))]))])), ('ImageGalleryBlock', wagtail.core.blocks.StructBlock([('images', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('ImageBlock', wagtail.images.blocks.ImageChooserBlock(required=True))])))])), ('CallToActionBlock', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.CharBlock(required=False)), ('text', wagtail.core.blocks.RichTextBlock(features=['h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'link'], required=False)), ('buttons', wagtail.core.blocks.ListBlock(wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.CharBlock(required=True)), ('page', wagtail.core.blocks.PageChooserBlock(required=False)), ('document', wagtail.documents.blocks.DocumentChooserBlock(required=False)), ('external_link', wagtail.core.blocks.CharBlock(required=False))], required=False), default=[]))]))], blank=True, null=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/django/website/wagtail_vue/wagtail_vue/settings/settings_base.py:
--------------------------------------------------------------------------------
1 | """Common settings and globals."""
2 |
3 | import os
4 | from os.path import abspath, basename, dirname, join, normpath
5 | from sys import path
6 |
7 | from django.core.exceptions import ImproperlyConfigured
8 |
9 | # ======== PATH CONFIGURATION
10 | # Absolute filesystem path to the Django project directory:
11 | DJANGO_ROOT = dirname(dirname(abspath(__file__)))
12 |
13 | # Absolute filesystem path to the top-level project folder:
14 | SITE_ROOT = dirname(DJANGO_ROOT)
15 |
16 | # Site name:
17 | SITE_NAME = basename(DJANGO_ROOT)
18 |
19 | # Add our project to our pythonpath, this way we don't need to type our project
20 | # name in our dotted import paths:
21 | path.append(DJANGO_ROOT)
22 | # ======== END PATH CONFIGURATION
23 |
24 | ALLOWED_HOSTS = [
25 | '.wagtail-vue-definately-a-real-wesbite-we-promise.com',
26 | ]
27 |
28 | # ======== DEBUG CONFIGURATION
29 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug
30 | DEBUG = False
31 |
32 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug
33 | TEMPLATE_DEBUG = DEBUG
34 | # ======== END DEBUG CONFIGURATION
35 |
36 |
37 | # ======== MANAGER CONFIGURATION
38 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#admins
39 | ADMINS = (
40 | ('backend', 'admin@yourwebsite.com'),
41 | )
42 |
43 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#managers
44 | MANAGERS = ADMINS
45 | # ======== END MANAGER CONFIGURATION
46 |
47 |
48 | # ======== GENERAL CONFIGURATION
49 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#time-zone
50 | TIME_ZONE = 'Canada/Mountain'
51 |
52 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#language-code
53 | LANGUAGE_CODE = 'en-us'
54 |
55 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
56 | SITE_ID = 1
57 |
58 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n
59 | USE_I18N = True
60 |
61 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n
62 | USE_L10N = True
63 |
64 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#use-tz
65 | USE_TZ = True
66 | # ======== END GENERAL CONFIGURATION
67 |
68 |
69 | # ======== MEDIA CONFIGURATION
70 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root
71 | MEDIA_ROOT = normpath(join(SITE_ROOT, 'media'))
72 |
73 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url
74 | MEDIA_URL = '/media/'
75 | # ======== END MEDIA CONFIGURATION
76 |
77 |
78 | # ======== STATIC FILE CONFIGURATION
79 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root
80 | STATIC_ROOT = normpath(join(SITE_ROOT, 'static_collected'))
81 |
82 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url
83 | STATIC_URL = '/static/'
84 |
85 | # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS # noqa: E501
86 | STATICFILES_DIRS = (
87 | normpath(join(SITE_ROOT, 'static')),
88 | )
89 |
90 | STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage'
91 |
92 | # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders # noqa: E501
93 | STATICFILES_FINDERS = (
94 | 'django.contrib.staticfiles.finders.FileSystemFinder',
95 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder',
96 | )
97 | # ======== END STATIC FILE CONFIGURATION
98 |
99 |
100 | # ======== SECRET CONFIGURATION
101 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
102 | # Note: This key only used for development and testing.
103 | SECRET_KEY = r'lbo=9bzcg-hkmubso1n!6%at5@8u2+=&g%2um8wk7jjpnkczqg'
104 | # ======== END SECRET CONFIGURATION
105 |
106 |
107 | # ======== FIXTURE CONFIGURATION
108 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS # noqa: E501
109 | FIXTURE_DIRS = (
110 | normpath(join(SITE_ROOT, 'fixtures')),
111 | )
112 | # ======== END FIXTURE CONFIGURATION
113 |
114 |
115 | # ======== TEMPLATE CONFIGURATION
116 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors # noqa: E501
117 | TEMPLATES = [
118 | {
119 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
120 | 'APP_DIRS': True,
121 | 'DIRS': [
122 | normpath(join(SITE_ROOT, 'templates')),
123 | ],
124 | 'OPTIONS': {
125 | 'context_processors': [
126 | 'django.contrib.auth.context_processors.auth',
127 | 'django.template.context_processors.debug',
128 | 'django.template.context_processors.i18n',
129 | 'django.template.context_processors.media',
130 | 'django.template.context_processors.static',
131 | 'django.template.context_processors.tz',
132 | 'django.contrib.messages.context_processors.messages',
133 | 'django.template.context_processors.request',
134 | ],
135 | 'debug': DEBUG,
136 | },
137 | },
138 | ]
139 | # ======== END TEMPLATE CONFIGURATION
140 |
141 |
142 | # ======== MIDDLEWARE CONFIGURATION
143 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#middleware-classes
144 | MIDDLEWARE = (
145 | 'corsheaders.middleware.CorsMiddleware',
146 | # Default Django middleware.
147 | 'django.middleware.common.CommonMiddleware',
148 | 'django.contrib.sessions.middleware.SessionMiddleware',
149 | 'django.middleware.csrf.CsrfViewMiddleware',
150 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
151 | 'django.contrib.messages.middleware.MessageMiddleware',
152 | 'wagtail.core.middleware.SiteMiddleware',
153 | 'wagtail.contrib.redirects.middleware.RedirectMiddleware',
154 | )
155 | # ======== END MIDDLEWARE CONFIGURATION
156 |
157 |
158 | # ======== URL CONFIGURATION
159 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
160 | ROOT_URLCONF = '%s.urls' % SITE_NAME
161 | # ======== END URL CONFIGURATION
162 |
163 |
164 | # ======== APP CONFIGURATION
165 | INSTALLED_APPS = (
166 | 'django.contrib.auth',
167 | 'django.contrib.contenttypes',
168 | 'django.contrib.sessions',
169 | 'django.contrib.sites',
170 | 'django.contrib.messages',
171 | 'django.contrib.staticfiles',
172 | 'django.contrib.humanize',
173 | 'django.contrib.admin',
174 |
175 | 'adminactions',
176 | 'django_extensions',
177 |
178 | 'wagtail.contrib.forms',
179 | 'wagtail.contrib.redirects',
180 | 'wagtail.embeds',
181 | 'wagtail.sites',
182 | 'wagtail.users',
183 | 'wagtail.snippets',
184 | 'wagtail.documents',
185 | 'wagtail.images',
186 | 'wagtail.search',
187 | 'wagtail.admin',
188 | 'wagtail.core',
189 | 'wagtail.api.v2',
190 |
191 | 'rest_framework',
192 | 'modelcluster',
193 | 'taggit',
194 | 'corsheaders',
195 |
196 | 'pages',
197 | )
198 | # ======== END APP CONFIGURATION
199 |
200 |
201 | # ======== LOGGING CONFIGURATION
202 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
203 | # A sample logging configuration. The only tangible logging
204 | # performed by this configuration is to send an email to
205 | # the site admins on every HTTP 500 error when DEBUG=False.
206 | # See http://docs.djangoproject.com/en/dev/topics/logging for
207 | # more details on how to customize your logging configuration.
208 | LOGGING = {
209 | 'version': 1,
210 | 'disable_existing_loggers': False,
211 | 'filters': {
212 | 'require_debug_false': {
213 | '()': 'django.utils.log.RequireDebugFalse'
214 | }
215 | },
216 | 'handlers': {
217 | 'mail_admins': {
218 | 'level': 'ERROR',
219 | 'filters': ['require_debug_false'],
220 | 'class': 'django.utils.log.AdminEmailHandler'
221 | },
222 | },
223 | }
224 | # ======== END LOGGING CONFIGURATION
225 |
226 |
227 | # ======== WSGI CONFIGURATION
228 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application
229 | WSGI_APPLICATION = 'wsgi.application'
230 | # ======== END WSGI CONFIGURATION
231 |
232 |
233 | # ======== WAGTAIL SETTINGS CONFIGURATION
234 | WAGTAIL_SITE_NAME = "Wagtail/Vue Website"
235 | # ======== END WAGTAIL SETTINGS CONFIGURATION
236 |
237 |
238 | # ======== ERROR MESSAGE FOR MISSING ENVIRONMENT VARIABLES
239 | def get_env_variable(var_name):
240 | """Get the environment variable or return exception."""
241 | try:
242 | return os.environ[var_name]
243 | except KeyError:
244 | error_msg = 'Set the %s environment variable' % var_name
245 | raise ImproperlyConfigured(error_msg)
246 | # ======== END ERROR MESSAGE FOR MISSING ENVIRONMENT VARIABLES
247 |
--------------------------------------------------------------------------------
/vue/SLIDES.md:
--------------------------------------------------------------------------------
1 | # Wagtail + Vue.js
2 |
3 | __ __ _ _ _ __ __ _
4 | \ \ / /_ _ __ _| |_ __ _(_) | _ \ \ / / _ ___ (_)___
5 | \ \ /\ / / _` |/ _` | __/ _` | | | _| |_ \ \ / / | | |/ _ \ | / __|
6 | \ V V / (_| | (_| | || (_| | | | |_ _| \ V /| |_| | __/_ | \__ \
7 | \_/\_/ \__,_|\__, |\__\__,_|_|_| |_| \_/ \__,_|\___(_)/ |___/
8 | |___/ |__/
9 |
10 | Using Wagtail as a headless CMS.
11 |
12 | - Routing
13 | - Rendering page content
14 | - Simple page navigation
15 |
16 | # Routing for Wagtail
17 |
18 | _____ _ _ __
19 | | _ \ ___ _ _| |_(_)_ __ __ _ / _| ___ _ __
20 | | |_) / _ \| | | | __| | '_ \ / _` | | |_ / _ \| '__|
21 | | _ < (_) | |_| | |_| | | | | (_| | | _| (_) | |
22 | |_| \_\___/ \__,_|\__|_|_| |_|\__, | |_| \___/|_|
23 | |___/
24 | __ __ _ _ _
25 | \ \ / /_ _ __ _| |_ __ _(_) |
26 | \ \ /\ / / _` |/ _` | __/ _` | | |
27 | \ V V / (_| | (_| | || (_| | | |
28 | \_/\_/ \__,_|\__, |\__\__,_|_|_|
29 | |___/
30 |
31 | ##
32 |
33 | In Wagtail each type of page will have it's own model since they may have different types of content. This makes it easy for us to render a different component for each page type.
34 |
35 | ```
36 | class HomePage(Page):
37 | """A home page class."""
38 | ...fields for page content...
39 | ```
40 |
41 | ##
42 |
43 | When a user navigates to a page we make a request to the Wagtail API's "find" method and pass in the URL the user requested.
44 |
45 | If Wagtail can resolve this path to an existing page than it will return a `302` redirect response to the page's detail view which in turn returns the JSON data.
46 |
47 | ```
48 | User requests "http://localhost:8080/"
49 |
50 | GET /api/v2/pages/find/?html_path=/ => 302
51 | GET /api/v2/pages/3/ => 200 JSON data
52 | ```
53 |
54 | TODO: Show example of page response: http://localhost:8000/api/v2/pages/3/?format=json
55 |
56 | Right in the returned JSON data we'll find the `meta.type` key which we use to determine which component to render.
57 |
58 | ##
59 |
60 | If Wagtail cannot resolve the URL it will return a `404` not found response and we can inform the user.
61 |
62 | ```
63 | GET /api/v2/pages/find/?html_path=/wp-login.php => 404 Not Found message
64 | ```
65 |
66 | # Rendering Wagtail page content
67 |
68 | ____ _ _
69 | | _ \ ___ _ __ __| | ___ _ __(_)_ __ __ _
70 | | |_) / _ \ '_ \ / _` |/ _ \ '__| | '_ \ / _` |
71 | | _ < __/ | | | (_| | __/ | | | | | | (_| |
72 | |_| \_\___|_| |_|\__,_|\___|_| |_|_| |_|\__, |
73 | |___/
74 | __ __ _ _ _
75 | \ \ / /_ _ __ _| |_ __ _(_) | _ __ __ _ __ _ ___
76 | \ \ /\ / / _` |/ _` | __/ _` | | | | '_ \ / _` |/ _` |/ _ \
77 | \ V V / (_| | (_| | || (_| | | | | |_) | (_| | (_| | __/
78 | \_/\_/ \__,_|\__, |\__\__,_|_|_| | .__/ \__,_|\__, |\___|
79 | |___/ |_| |___/
80 | _ _
81 | ___ ___ _ __ | |_ ___ _ __ | |_
82 | / __/ _ \| '_ \| __/ _ \ '_ \| __|
83 | | (_| (_) | | | | || __/ | | | |_
84 | \___\___/|_| |_|\__\___|_| |_|\__|
85 |
86 | ##
87 |
88 | Along with the typical methods of managing page content, Wagtail has a unique concept called "StreamField" which allows the user to intersperse different types of content, usually referred to as "blocks", which can be repeated and arranged in any order.
89 |
90 | TODO: Show example of Wagtail StreamField content: http://localhost:8000/api/v2/pages/3/?format=json
91 |
92 | ##
93 |
94 | These StreamField blocks can at times be tricky to work with through the API. Since they are so flexible they are commonly used in many different ways and this can cause the same block to end up outputting data differently. At times we need to add extra logic to our components to work around this.
95 |
96 | ```
97 | # ButtonBlock used inside of a "StreamBlock"
98 | {
99 | "id": "23960469-af52-41dd-a9fe-3f9bbb9593e5",
100 | "value": {
101 | "document": null,
102 | "text": "External link",
103 | "external_link": "https://www.duckduckgo.com/",
104 | "page": null
105 | },
106 | "type": "ButtonBlock"
107 | }
108 |
109 | # The same ButtonBlock used inside of a "ListBlock"
110 | {
111 | "document": null,
112 | "text": "Page link",
113 | "external_link": "",
114 | "page": 4
115 | }
116 | ```
117 |
118 | ##
119 |
120 | Inside of StreamField blocks Wagtail usually only stores the IDs for images, documents, or other pages. It's best to make a dedicated component for each of these to abstract the API logic of fetching the full details.
121 |
122 | ```
123 | # Wagtail Endpoints
124 | Pages: /api/v2/pages//
125 | Images: /api/v2/images//
126 | Documents: /api/v2/documents//
127 |
128 | # WagtailPage component
129 | - Fetch a Wagtail page with the ID passed into our component.
130 | - Get the page's path from the "meta.html_url" property and anything else we want from the response.
131 | - Render a link with the path.
132 | ```
133 |
134 | ##
135 |
136 | Which StreamField blocks are available can differ between page types and they can be nested inside of one another so we need a reliable way to pass data down the component tree.
137 |
138 | Again, we chose to abstract this out into a separate component which accepts any type of StreamField block and renders the specific component we've made for that block type. This allows us to loop through the StreamField content on any page or nested StreamField content inside of a StreamField block.
139 |
140 | ```
141 | # HomePage component
142 | - Loop through blocks in content field.
143 | - For each block pass block data to StreamField component.
144 |
145 | # StreamField component
146 | - Register all available StreamField block components.
147 | - Render StreamField block component matching the current block's "type" property.
148 |
149 | # StreamField block component
150 | - This is where all your code for a specific StreamField block will go. Templating, styles, extra scripting, etc.
151 | - If you're using nested StreamFields you can re-use that component here and even re-use other StreamField block components entirely.
152 | ```
153 |
154 | # Simple page navigation
155 |
156 | ____ _ _
157 | / ___|(_)_ __ ___ _ __ | | ___ _ __ __ _ __ _ ___
158 | \___ \| | '_ ` _ \| '_ \| |/ _ \ | '_ \ / _` |/ _` |/ _ \
159 | ___) | | | | | | | |_) | | __/ | |_) | (_| | (_| | __/
160 | |____/|_|_| |_| |_| .__/|_|\___| | .__/ \__,_|\__, |\___|
161 | |_| |_| |___/
162 | _ _ _
163 | _ __ __ ___ _(_) __ _ __ _| |_(_) ___ _ __
164 | | '_ \ / _` \ \ / / |/ _` |/ _` | __| |/ _ \| '_ \
165 | | | | | (_| |\ V /| | (_| | (_| | |_| | (_) | | | |
166 | |_| |_|\__,_| \_/ |_|\__, |\__,_|\__|_|\___/|_| |_|
167 | |___/
168 |
169 | ...or: How I Learned to Stop Worrying and Love the API
170 |
171 | ## Specifying fields
172 |
173 | The first thing I'll show you is how to restrict which fields the Wagtail API returns when making a request. If we're just building a menu we don't need the page content bloating our requests.
174 |
175 | ```
176 | GET /api/v2/pages/?fields=_,html_url,title
177 | ```
178 |
179 | This request will return a list of all pages on the site with only the `html_url` and `title` for each. The `_` here is a special character and tells the Wagtail API to hide all fields by default, this gives us a clean slate to start with.
180 |
181 | ## Limiting
182 |
183 | If we got a thousand pages on the site we don't want all of those showing up in the menu. You can quickly limit the number of returned pages by tacking on the `limit` query parameter.
184 |
185 | ```
186 | GET /api/v2/pages/?fields=_,html_url,title&limit=5
187 | ```
188 |
189 | ## Filtering
190 |
191 | We're getting close now, but what if we want more control over what pages are in the menu? By default Wagtail pages have a boolean field called `show_in_menus`. We might as well use that and the Wagtail API makes this easy for us, simple add the field as a query parameter and assign the value we need.
192 |
193 | ```
194 | GET /api/v2/pages/?fields=_,html_url,title&limit=5?show_in_menus=true
195 | ```
196 |
197 | ## I want more...
198 |
199 | This is all we need for our purposes right now but with the Wagtail API you can do a lot more out of the box which can also be useful, just to name a few:
200 |
201 | - Pagination
202 | - Ordering
203 | - Filtering (field, page type, tree position, etc.)
204 | - Search
205 |
206 | For details on these, and more, check out the docs: https://docs.wagtail.io/en/latest/advanced_topics/api/v2/usage.html
207 |
--------------------------------------------------------------------------------