Merge pull request #4 from RealOrangeOne/pelican-refresh
Pelican refresh
This commit is contained in:
commit
b4c50c26e7
48 changed files with 506 additions and 407 deletions
2
.buildpacks
Normal file
2
.buildpacks
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
https://github.com/heroku/heroku-buildpack-nodejs
|
||||||
|
https://github.com/heroku/heroku-buildpack-python
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "./node_modules/eslint-config/.eslintrc",
|
"extends": "eslint-config-dabapps/base/.eslintrc",
|
||||||
"globals": {
|
"globals": {
|
||||||
$: true
|
"$": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -60,7 +60,5 @@ target/
|
||||||
# Pelican stuff
|
# Pelican stuff
|
||||||
output/
|
output/
|
||||||
theme/static/build/
|
theme/static/build/
|
||||||
pelican_plugins/
|
|
||||||
|
|
||||||
theme/static/src/scss/pygment.css
|
|
||||||
deploy/
|
deploy/
|
||||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "pelican_plugins"]
|
||||||
|
path = pelican_plugins
|
||||||
|
url = https://github.com/getpelican/pelican-plugins
|
2
.nvmrc
2
.nvmrc
|
@ -1 +1 @@
|
||||||
5.10.1
|
6.9.4
|
||||||
|
|
|
@ -56,6 +56,7 @@ rules:
|
||||||
force-pseudo-nesting: 0
|
force-pseudo-nesting: 0
|
||||||
force-element-nesting: 0
|
force-element-nesting: 0
|
||||||
placeholder-in-extend: 0
|
placeholder-in-extend: 0
|
||||||
|
no-url-domains: 0
|
||||||
nesting-depth:
|
nesting-depth:
|
||||||
- 2
|
- 2
|
||||||
-
|
-
|
||||||
|
|
80
Makefile
80
Makefile
|
@ -1,80 +0,0 @@
|
||||||
BASEDIR=$(PWD)
|
|
||||||
ENV=$(BASEDIR)/env/bin
|
|
||||||
NODE_BIN=node_modules/.bin
|
|
||||||
PELICAN=$(ENV)/pelican
|
|
||||||
|
|
||||||
OUTPUTDIR=$(BASEDIR)/output
|
|
||||||
PLUGINS_DIR=$(BASEDIR)/pelican_plugins
|
|
||||||
DEPLOY_DIR=$(BASEDIR)/deploy
|
|
||||||
CONFIG_FILE=$(BASEDIR)/config/pelicanconf.py
|
|
||||||
|
|
||||||
FLAKE8_IGNORE=--ignore=E128,E501,E401,E402
|
|
||||||
|
|
||||||
build: install
|
|
||||||
rm -rf $(OUTPUTDIR)/*
|
|
||||||
@echo ">> Building static data..."
|
|
||||||
mkdir -p theme/static/build/js/lib theme/static/build/fonts theme/static/build/css theme/static/build/img
|
|
||||||
cp -R node_modules/font-awesome/fonts theme/static/build/
|
|
||||||
npm run build-js
|
|
||||||
npm run build-scss
|
|
||||||
@echo ">> Building pelican..."
|
|
||||||
$(PELICAN) -o $(OUTPUTDIR) -vs $(CONFIG_FILE)
|
|
||||||
mv $(OUTPUTDIR)/assets/robots.txt $(OUTPUTDIR)
|
|
||||||
cp -R $(OUTPUTDIR)/assets/* $(OUTPUTDIR)/static
|
|
||||||
rm -rf $(OUTPUTDIR)/assets
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OUTPUTDIR)/*
|
|
||||||
rm -rf $(BASEDIR)/env/
|
|
||||||
rm -rf $(BASEDIR)/node_modules/
|
|
||||||
rm -rf $(PLUGINS_DIR)/*
|
|
||||||
|
|
||||||
|
|
||||||
install: env node_modules pelican_plugins
|
|
||||||
|
|
||||||
pelican_plugins: env
|
|
||||||
rm -rf $(PLUGINS_DIR) || "No existing extensions"
|
|
||||||
git clone --recursive https://github.com/getpelican/pelican-plugins $(PLUGINS_DIR) || "Git Fail"
|
|
||||||
@echo ">> Hotfixing..."
|
|
||||||
rm -rf $(PLUGINS_DIR)/pelican-jinja2content
|
|
||||||
git clone https://github.com/RealOrangeOne/pelican-jinja2content -b patch-1 --depth=1 $(PLUGINS_DIR)/pelican-jinja2content
|
|
||||||
|
|
||||||
env:
|
|
||||||
pyvenv env
|
|
||||||
$(ENV)/pip install -r requirements.txt --upgrade
|
|
||||||
|
|
||||||
node_modules:
|
|
||||||
npm install
|
|
||||||
|
|
||||||
|
|
||||||
test: unittest lint spellcheck securitycheck
|
|
||||||
|
|
||||||
unittest:
|
|
||||||
$(ENV)/nose2 --verbose
|
|
||||||
|
|
||||||
lint:
|
|
||||||
$(NODE_BIN)/eslint 'theme/static/src/js/'
|
|
||||||
$(NODE_BIN)/sass-lint -vqc .sass-lint.yml
|
|
||||||
$(ENV)/flake8 $(BASEDIR)/plugins/ $(FLAKE8_IGNORE)
|
|
||||||
$(ENV)/flake8 $(BASEDIR)/scripts/ $(FLAKE8_IGNORE)
|
|
||||||
$(ENV)/flake8 $(BASEDIR)/config/ $(FLAKE8_IGNORE)
|
|
||||||
$(ENV)/flake8 $(BASEDIR)/tests/ $(FLAKE8_IGNORE)
|
|
||||||
$(ENV)/yamllint config/config.yml
|
|
||||||
|
|
||||||
spellcheck:
|
|
||||||
$(NODE_BIN)/mdspell --en-gb -ranx theme/templates/**/*.* theme/templates/*.*
|
|
||||||
$(NODE_BIN)/mdspell --en-gb -ranx content/**/*.md content/*.md content/**/*.html content/*.html
|
|
||||||
|
|
||||||
securitycheck:
|
|
||||||
$(NODE_BIN)/nsp check
|
|
||||||
$(ENV)/bandit -r plugins/ config/ tests/
|
|
||||||
|
|
||||||
|
|
||||||
upload:
|
|
||||||
git clone https://github.com/RealOrangeOne/host-container.git $(DEPLOY_DIR)
|
|
||||||
cp -rf $(OUTPUTDIR)/. $(DEPLOY_DIR)/site/
|
|
||||||
@cd $(DEPLOY_DIR) && git remote add dokku $(DEPLOY_URL) && git add . && git commit -m "add files" && git push -f dokku master --quiet
|
|
||||||
rm -rf $(DEPLOY_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: build clean test lint install upload
|
|
1
Procfile
Normal file
1
Procfile
Normal file
|
@ -0,0 +1 @@
|
||||||
|
web: bash scripts/run-build-server.sh
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
[![Circle CI](https://circleci.com/gh/RealOrangeOne/theorangeone.net.svg?style=svg)](https://circleci.com/gh/RealOrangeOne/theorangeone.net)
|
[![Circle CI](https://circleci.com/gh/RealOrangeOne/theorangeone.net.svg?style=svg)](https://circleci.com/gh/RealOrangeOne/theorangeone.net)
|
||||||
|
|
||||||
|
Find it here: [https://theorangeone.net](http://theorangeone.net)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
```bash
|
```bash
|
||||||
make
|
./scripts/build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run Tests
|
## Run Tests
|
||||||
```bash
|
```bash
|
||||||
make test
|
./scripts/test
|
||||||
```
|
```
|
||||||
|
|
22
circle.yml
22
circle.yml
|
@ -2,24 +2,22 @@ machine:
|
||||||
python:
|
python:
|
||||||
version: 3.5.1
|
version: 3.5.1
|
||||||
node:
|
node:
|
||||||
version: 5.11.1
|
version: 6.9.4
|
||||||
environment:
|
environment:
|
||||||
BUILD_PRODUCTION: true
|
BUILD_PRODUCTION: true
|
||||||
NODE_ENV: production
|
NODE_ENV: production
|
||||||
NPM_CONFIG_PRODUCTION: false
|
NPM_CONFIG_PRODUCTION: false
|
||||||
|
|
||||||
|
checkout:
|
||||||
|
post:
|
||||||
|
- git submodule sync
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
override:
|
||||||
- make -B
|
- scripts/build
|
||||||
|
- pelican -v
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
- make test
|
- scripts/test
|
||||||
|
|
||||||
deployment:
|
|
||||||
production:
|
|
||||||
branch: master
|
|
||||||
commands:
|
|
||||||
- git config --global user.email "git@theorangeone.net"
|
|
||||||
- git config --global user.name "TheOrangeOne"
|
|
||||||
- make upload
|
|
||||||
|
|
|
@ -2,29 +2,12 @@ import yaml
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
class DotDictionary(dict):
|
settings_dir = os.path.dirname(__file__)
|
||||||
def __getattr__(self, attr):
|
|
||||||
value = self[attr]
|
|
||||||
if type(value) == dict:
|
|
||||||
value = DotDictionary(value)
|
|
||||||
return value
|
|
||||||
__setattr__ = dict.__setitem__
|
|
||||||
__delattr__ = dict.__delitem__
|
|
||||||
|
|
||||||
|
|
||||||
class WrappedSettings:
|
def get_config(filename):
|
||||||
def __init__(self):
|
with open(os.path.join(settings_dir, '{}.yml'.format(filename))) as f:
|
||||||
self.settings_dir = os.path.join(os.path.dirname(__file__), 'config.yml')
|
return yaml.safe_load(f)
|
||||||
settings = open(self.settings_dir)
|
|
||||||
self.settings = yaml.safe_load(settings)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
value = self.settings[name]
|
|
||||||
if type(value) == dict:
|
|
||||||
value = DotDictionary(value)
|
|
||||||
return value
|
|
||||||
|
|
||||||
def __str__(self):
|
social = get_config('social')
|
||||||
return str(self.settings)
|
|
||||||
|
|
||||||
settings = WrappedSettings()
|
|
||||||
|
|
|
@ -1,22 +1,4 @@
|
||||||
---
|
---
|
||||||
|
|
||||||
author: Jake Howard
|
|
||||||
site_name: TheOrangeOne
|
|
||||||
url: https://theorangeone.net
|
|
||||||
timezone: Europe/London
|
|
||||||
language: en
|
|
||||||
|
|
||||||
pelican_plugins:
|
|
||||||
- sitemap
|
|
||||||
- filetime_from_git
|
|
||||||
- pelican-jinja2content
|
|
||||||
- metatags
|
|
||||||
- autopages
|
|
||||||
- screenfetch
|
|
||||||
|
|
||||||
sitemap_format: xml
|
|
||||||
|
|
||||||
|
|
||||||
accounts:
|
accounts:
|
||||||
github:
|
github:
|
||||||
- GitHub
|
- GitHub
|
||||||
|
@ -103,7 +85,3 @@ footer_accounts:
|
||||||
- instagram
|
- instagram
|
||||||
- youtube
|
- youtube
|
||||||
- flickr
|
- flickr
|
||||||
|
|
||||||
piwik:
|
|
||||||
url: piwik.theorangeone.net
|
|
||||||
site_id: 1
|
|
|
@ -1 +0,0 @@
|
||||||
web: bash scripts/server.sh
|
|
35
package.json
35
package.json
|
@ -3,8 +3,9 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"description": " Source code for TheOrangeOne.net",
|
"description": " Source code for TheOrangeOne.net",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-js": "./scripts/build-js.sh",
|
"postinstall": "npm run create-build-dirs",
|
||||||
"build-scss": "./scripts/build-scss.sh"
|
"create-build-dirs": "mkdir -p theme/static/build/js theme/static/build/fonts theme/static/build/css theme/static/build/img",
|
||||||
|
"start": "tstatic output/"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -14,27 +15,23 @@
|
||||||
"animate.css": "=3.5.2",
|
"animate.css": "=3.5.2",
|
||||||
"ansi_up": "=1.3.0",
|
"ansi_up": "=1.3.0",
|
||||||
"bootstrap-sass": "=3.3.7",
|
"bootstrap-sass": "=3.3.7",
|
||||||
"font-awesome": "=4.6.3",
|
"font-awesome": "=4.7.0",
|
||||||
"jquery": "=2.2.3",
|
"jquery": "=3.1.1",
|
||||||
"jquery.easing": "=1.3.2",
|
"pygments-css": "=1.0.0",
|
||||||
|
"tstatic": "RealOrangeOne/tstatic",
|
||||||
"underscore": "=1.8.3",
|
"underscore": "=1.8.3",
|
||||||
"wow.js": "=1.2.0"
|
"wow.js": "=1.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "=6.5.0",
|
"autoprefixer": "=6.6.1",
|
||||||
"babel-preset-es2015": "=6.13.2",
|
"browserify": "=13.3.0",
|
||||||
"babel-preset-react": "=6.11.1",
|
"clean-css": "=3.4.23",
|
||||||
"babelify": "=7.3.0",
|
"eslint-config": "dabapps/eslint-config.git#2.0.4",
|
||||||
"browserify": "=13.1.0",
|
|
||||||
"clean-css": "=3.4.20",
|
|
||||||
"eslint": "=1.5.0",
|
|
||||||
"eslint-config": "git://github.com/dabapps/eslint-config.git",
|
|
||||||
"eslint-plugin-react": "=3.4.2",
|
|
||||||
"markdown-spellcheck": "=0.11.0",
|
"markdown-spellcheck": "=0.11.0",
|
||||||
"node-sass": "=3.8.0",
|
"node-sass": "=4.3.0",
|
||||||
"nsp": "=2.6.1",
|
"nsp": "=2.6.2",
|
||||||
"postcss-cli": "=2.6.0",
|
"postcss-cli": "=2.6.0",
|
||||||
"sass-lint": "=1.9.1",
|
"sass-lint": "=1.10.2",
|
||||||
"uglify-js": "=2.7.3"
|
"uglify-js": "=2.7.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1
pelican_plugins
Submodule
1
pelican_plugins
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6cd5a608b45fdc21bb11ed1f72a151cdb71c1169
|
|
@ -1,23 +1,25 @@
|
||||||
# -*- coding: utf-8 -*- #
|
# -*- coding: utf-8 -*- #
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from git import Repo
|
|
||||||
import sys, os
|
import sys, os
|
||||||
sys.path.insert(0, os.path.realpath('./'))
|
sys.path.insert(0, os.path.realpath('./'))
|
||||||
|
|
||||||
from config import settings
|
|
||||||
|
|
||||||
# Global core settings
|
# Global core settings
|
||||||
AUTHOR = settings.author
|
AUTHOR = "Jake Howard"
|
||||||
SITENAME = settings.site_name
|
SITENAME = "TheOrangeOne"
|
||||||
SITEURL = settings.url
|
SITEURL = "https://theorangeone.net"
|
||||||
PATH = '../content'
|
PATH = 'content'
|
||||||
TIMEZONE = settings.timezone
|
TIMEZONE = "Europe/London"
|
||||||
DEFAULT_LANG = settings.language
|
DEFAULT_LANG = "en"
|
||||||
PAGE_PATHS = ["pages"]
|
PAGE_PATHS = ["pages"]
|
||||||
THEME = "../theme"
|
THEME = "theme"
|
||||||
THEME_STATIC_DIR = "static"
|
THEME_STATIC_DIR = "static"
|
||||||
THEME_STATIC_PATHS = ["static/build"]
|
THEME_STATIC_PATHS = ["static/build"]
|
||||||
STATIC_PATHS = ["assets"]
|
STATIC_PATHS = ["assets"]
|
||||||
|
DEFAULT_DATE = 'fs'
|
||||||
|
WITH_FUTURE_DATES = True
|
||||||
|
LOAD_CONTENT_CACHE = False
|
||||||
|
CACHE_CONTENT = False
|
||||||
|
DELETE_OUTPUT_DIRECTORY = True
|
||||||
|
|
||||||
USE_FOLDER_AS_CATEGORY = True
|
USE_FOLDER_AS_CATEGORY = True
|
||||||
DEFAULT_PAGINATION = False
|
DEFAULT_PAGINATION = False
|
||||||
|
@ -30,11 +32,13 @@ FOOTER_LINKS = links.footer()
|
||||||
INDEX_PROJECTS = links.index_projects()
|
INDEX_PROJECTS = links.index_projects()
|
||||||
|
|
||||||
# Extra config
|
# Extra config
|
||||||
REPO = Repo(search_parent_directories=True)
|
|
||||||
BUILD_PRODUCTION = 'BUILD_PRODUCTION' in os.environ
|
BUILD_PRODUCTION = 'BUILD_PRODUCTION' in os.environ
|
||||||
from plugins import image_resizer
|
from plugins import image_resizer
|
||||||
META_IMAGES = image_resizer.generate()
|
META_IMAGES = image_resizer.generate()
|
||||||
PIWIK = settings.piwik
|
PIWIK = {
|
||||||
|
'url': 'piwik.theorangeone.net',
|
||||||
|
'site_id': 1
|
||||||
|
}
|
||||||
|
|
||||||
# Disable some pages
|
# Disable some pages
|
||||||
TAG_URL = False
|
TAG_URL = False
|
||||||
|
@ -61,14 +65,22 @@ FEED_ATOM = 'feed.atom'
|
||||||
FEED_DOMAIN = SITEURL
|
FEED_DOMAIN = SITEURL
|
||||||
|
|
||||||
# Setup plugins
|
# Setup plugins
|
||||||
PLUGIN_PATHS = ["../pelican_plugins", "../plugins"]
|
PLUGIN_PATHS = ["plugins", "pelican_plugins"]
|
||||||
PLUGINS = settings.pelican_plugins
|
PLUGINS = [
|
||||||
|
'sitemap',
|
||||||
|
'pelican-jinja2content',
|
||||||
|
'metatags',
|
||||||
|
'autopages',
|
||||||
|
'screenfetch',
|
||||||
|
'post_build',
|
||||||
|
'static_build'
|
||||||
|
]
|
||||||
|
|
||||||
if BUILD_PRODUCTION:
|
if BUILD_PRODUCTION:
|
||||||
PLUGINS.append("minify") # only minify on production build
|
PLUGINS.append("minify") # only minify on production build
|
||||||
|
|
||||||
SITEMAP = {
|
SITEMAP = {
|
||||||
"format": settings.sitemap_format
|
"format": 'xml'
|
||||||
}
|
}
|
||||||
CATEGORY_PAGE_PATH = "theme/templates/categories"
|
CATEGORY_PAGE_PATH = "theme/templates/categories"
|
||||||
MINIFY = {
|
MINIFY = {
|
||||||
|
@ -81,14 +93,16 @@ MINIFY = {
|
||||||
from fontawesome_markdown import FontAwesomeExtension
|
from fontawesome_markdown import FontAwesomeExtension
|
||||||
from pyembed.markdown import PyEmbedMarkdown
|
from pyembed.markdown import PyEmbedMarkdown
|
||||||
from mkdcomments import CommentsExtension
|
from mkdcomments import CommentsExtension
|
||||||
MD_EXTENSIONS = [
|
MARKDOWN = {
|
||||||
|
'extensions': [
|
||||||
FontAwesomeExtension(),
|
FontAwesomeExtension(),
|
||||||
PyEmbedMarkdown(),
|
PyEmbedMarkdown(),
|
||||||
CommentsExtension(),
|
CommentsExtension(),
|
||||||
'codehilite(css_class=highlight)',
|
'codehilite(css_class=highlight)',
|
||||||
'extra'
|
'extra'
|
||||||
]
|
],
|
||||||
|
"output_format": "html5"
|
||||||
|
}
|
||||||
# Setup jinja2 filters
|
# Setup jinja2 filters
|
||||||
from plugins import filters
|
from plugins import filters
|
||||||
JINJA_FILTERS = {
|
JINJA_FILTERS = {
|
||||||
|
@ -96,3 +110,12 @@ JINJA_FILTERS = {
|
||||||
"category_find": filters.category_find,
|
"category_find": filters.category_find,
|
||||||
"limit": filters.limit
|
"limit": filters.limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JINJA_ENVIRONMENT = {
|
||||||
|
'trim_blocks': True,
|
||||||
|
'lstrip_blocks': True,
|
||||||
|
'extensions': [
|
||||||
|
'jinja2.ext.with_',
|
||||||
|
'plugins.include_with.IncludeWith'
|
||||||
|
]
|
||||||
|
}
|
53
plugins/include_with.py
Normal file
53
plugins/include_with.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
# Jinja-IncludeWith
|
||||||
|
|
||||||
|
A Jinja2 preprocessor extension that let you update the `include`
|
||||||
|
context like this:
|
||||||
|
|
||||||
|
{% include "something.html" with foo=bar %}
|
||||||
|
{% include "something.html" with a=3, b=2+2, c='yes' %}
|
||||||
|
|
||||||
|
You **must** also include 'jinja2.ext.with_' in the extensions list.
|
||||||
|
|
||||||
|
:copyright: [Juan-Pablo Scaletti] (http://jpscaletti.com).
|
||||||
|
:license: [MIT] (http://www.opensource.org/licenses/mit-license.php).
|
||||||
|
|
||||||
|
Copied from https://github.com/jpscaletti/jinja-includewith due to not pip-installable
|
||||||
|
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
from jinja2.ext import Extension
|
||||||
|
|
||||||
|
|
||||||
|
__version__ = '0.1'
|
||||||
|
|
||||||
|
rx = re.compile(r'\{\%\s*include\s+(?P<tmpl>[^\s]+)\s+with\s+(?P<context>.*?)\s*\%\}',
|
||||||
|
re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
|
class IncludeWith(Extension):
|
||||||
|
|
||||||
|
def preprocess(self, source, name, filename=None):
|
||||||
|
lastpos = 0
|
||||||
|
while 1:
|
||||||
|
m = rx.search(source, lastpos)
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
|
||||||
|
lastpos = m.end()
|
||||||
|
d = m.groupdict()
|
||||||
|
context = d['context'].strip()
|
||||||
|
if context == 'context':
|
||||||
|
continue
|
||||||
|
|
||||||
|
source = ''.join([
|
||||||
|
source[:m.start()],
|
||||||
|
'{% with ', context, ' %}',
|
||||||
|
'{% include ', d['tmpl'].strip(), ' %}',
|
||||||
|
'{% endwith %}',
|
||||||
|
source[m.end():]
|
||||||
|
])
|
||||||
|
|
||||||
|
return source
|
|
@ -1,6 +1,6 @@
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from random import shuffle
|
from random import shuffle
|
||||||
from config import settings, DotDictionary
|
from config import social
|
||||||
|
|
||||||
|
|
||||||
ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
|
ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
|
||||||
|
@ -8,20 +8,20 @@ ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
|
||||||
|
|
||||||
def accounts():
|
def accounts():
|
||||||
links = {}
|
links = {}
|
||||||
for key, (site, user, url, icon) in settings.accounts.items():
|
for key, (site, user, url, icon) in social['accounts'].items():
|
||||||
links[key] = DotDictionary({
|
links[key] = {
|
||||||
'key': key,
|
'key': key,
|
||||||
'site': site,
|
'site': site,
|
||||||
'username': user,
|
'username': user,
|
||||||
'url': url.format(user),
|
'url': url.format(user),
|
||||||
'icon': icon
|
'icon': icon
|
||||||
})
|
}
|
||||||
return links
|
return links
|
||||||
|
|
||||||
|
|
||||||
def footer():
|
def footer():
|
||||||
all_accounts = accounts()
|
all_accounts = accounts()
|
||||||
return [all_accounts[account] for account in settings.footer_accounts]
|
return [all_accounts[account] for account in social['footer_accounts']]
|
||||||
|
|
||||||
|
|
||||||
def index_projects():
|
def index_projects():
|
||||||
|
|
|
@ -17,10 +17,10 @@ def html_to_raw(html):
|
||||||
def get_twiter_tags(instance):
|
def get_twiter_tags(instance):
|
||||||
return {
|
return {
|
||||||
"twitter:card": "summary_large_image",
|
"twitter:card": "summary_large_image",
|
||||||
"twitter:site": instance.settings.get("ACCOUNTS")["twitter"].username,
|
"twitter:site": instance.settings.get("ACCOUNTS")["twitter"]['username'],
|
||||||
"twitter:title": instance.metadata.get("title", ""),
|
"twitter:title": instance.metadata.get("title", ""),
|
||||||
"twitter:description": html_to_raw(instance.metadata.get("summary", "")),
|
"twitter:description": html_to_raw(instance.metadata.get("summary", "")),
|
||||||
"twitter:creator": instance.settings.get("ACCOUNTS")["twitter"].username,
|
"twitter:creator": instance.settings.get("ACCOUNTS")["twitter"]['username'],
|
||||||
"twitter:image": instance.metadata.get("image", ""),
|
"twitter:image": instance.metadata.get("image", ""),
|
||||||
"twitter:image:alt": html_to_raw(instance.metadata.get("summary", "")),
|
"twitter:image:alt": html_to_raw(instance.metadata.get("summary", "")),
|
||||||
"twitter:url": os.path.join(instance.settings.get("SITEURL", ""), instance.url)
|
"twitter:url": os.path.join(instance.settings.get("SITEURL", ""), instance.url)
|
||||||
|
|
39
plugins/pelican-jinja2content.py
Normal file
39
plugins/pelican-jinja2content.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import os
|
||||||
|
from pelican import signals, contents
|
||||||
|
from jinja2 import Environment, ChoiceLoader, FileSystemLoader
|
||||||
|
from config import social
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
def execjinja2(instance):
|
||||||
|
if type(instance) in (contents.Article, contents.Page):
|
||||||
|
jinja2_env = Environment( # nosec
|
||||||
|
loader=ChoiceLoader([
|
||||||
|
FileSystemLoader(
|
||||||
|
os.path.join(BASE_DIR, instance.settings['THEME'], 'templates')
|
||||||
|
),
|
||||||
|
FileSystemLoader(
|
||||||
|
os.path.join(BASE_DIR, instance.settings['PATH'])
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
**instance.settings['JINJA_ENVIRONMENT'],
|
||||||
|
)
|
||||||
|
|
||||||
|
jinja2_env.filters.update(instance.settings['JINJA_FILTERS'])
|
||||||
|
|
||||||
|
jinja2_template = jinja2_env.from_string(instance._content)
|
||||||
|
|
||||||
|
kwargs = instance._context
|
||||||
|
if type(instance) is contents.Article:
|
||||||
|
kwargs['article'] = instance
|
||||||
|
elif type(instance) is contents.Page:
|
||||||
|
kwargs['page'] = instance
|
||||||
|
|
||||||
|
kwargs['social'] = social
|
||||||
|
|
||||||
|
instance._content = jinja2_template.render(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
signals.content_object_init.connect(execjinja2)
|
25
plugins/post_build.py
Normal file
25
plugins/post_build.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from pelican import signals
|
||||||
|
import os
|
||||||
|
from plugins.utils import run_command
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'output')
|
||||||
|
|
||||||
|
|
||||||
|
def post_build(*args, **kwargs):
|
||||||
|
run_command('Copying Robots.txt', [
|
||||||
|
'mv', os.path.join(OUTPUT_PATH, 'assets', 'robots.txt'), OUTPUT_PATH
|
||||||
|
])
|
||||||
|
run_command('Copying Assets', [
|
||||||
|
'cp', '-R', os.path.join(OUTPUT_PATH, 'assets', '*'), os.path.join(OUTPUT_PATH, 'static')
|
||||||
|
], True)
|
||||||
|
run_command('Remove Old Assets', [
|
||||||
|
'rm', '-rf', os.path.join(OUTPUT_PATH, 'assets')
|
||||||
|
])
|
||||||
|
run_command('Remove Drafts', [
|
||||||
|
'rm', '-rf', os.path.join(OUTPUT_PATH, 'drafts')
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
signals.finalized.connect(post_build)
|
36
plugins/static_build.py
Normal file
36
plugins/static_build.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from pelican import signals
|
||||||
|
from plugins.utils import node_bin, run_command, NODE_PRODUCTION
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
def static_build(*args, **kwargs):
|
||||||
|
if NODE_PRODUCTION:
|
||||||
|
logger.info('Building Production...')
|
||||||
|
UGLIFY_ARGS = ['--compress', '--screw-ie8', '--define', '--stats', '--keep-fnames']
|
||||||
|
else:
|
||||||
|
UGLIFY_ARGS = []
|
||||||
|
run_command('Copying Fonts', ['cp', '-r', 'node_modules/font-awesome/fonts', 'theme/static/build/'])
|
||||||
|
run_command('Building Bootstrap', [node_bin('uglifyjs'), 'node_modules/bootstrap-sass/assets/javascripts/bootstrap.js', UGLIFY_ARGS, '-o', 'theme/static/build/js/bootstrap.js'])
|
||||||
|
run_command('Building jQuery', [node_bin('uglifyjs'), 'node_modules/jquery/dist/jquery.js', UGLIFY_ARGS, '-o', 'theme/static/build/js/jquery.js'])
|
||||||
|
run_command('Building Application', [
|
||||||
|
node_bin('browserify'),
|
||||||
|
'theme/static/src/js/app.js',
|
||||||
|
'-o', 'theme/static/build/js/app.js'
|
||||||
|
])
|
||||||
|
|
||||||
|
logger.info('JS built!')
|
||||||
|
|
||||||
|
run_command('Building Styles', [node_bin('node-sass'), 'theme/static/src/scss/index.scss', 'theme/static/build/css/index.css', '--source-map-embed'])
|
||||||
|
|
||||||
|
logger.info('SCSS Built!')
|
||||||
|
|
||||||
|
if NODE_PRODUCTION:
|
||||||
|
run_command('Compressing Application', [node_bin('uglifyjs'), 'theme/static/build/js/app.js', UGLIFY_ARGS, '-o', 'theme/static/build/js/app.js'])
|
||||||
|
run_command('Prefixing Styles', [node_bin('postcss'), '-u', 'autoprefixer', '-o', 'theme/static/build/css/index.css', 'theme/static/build/css/index.css'])
|
||||||
|
run_command('Compressing Styles', [node_bin('cleancss'), '-d', '--s0', '-o', 'theme/static/build/css/index.css', 'theme/static/build/css/index.css'])
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
signals.static_generator_init.connect(static_build)
|
31
plugins/utils.py
Normal file
31
plugins/utils.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import subprocess # nosec
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__file__)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_PRODUCTION = os.environ.get('NODE_ENV') == 'production'
|
||||||
|
|
||||||
|
|
||||||
|
def flatten_list(array):
|
||||||
|
res = []
|
||||||
|
for el in array:
|
||||||
|
if isinstance(el, (list, tuple)):
|
||||||
|
res.extend(flatten_list(el))
|
||||||
|
continue
|
||||||
|
res.append(el)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def run_command(detail, args, wrap=False):
|
||||||
|
if wrap:
|
||||||
|
run_command(detail, ['bash', '-c', ' '.join(flatten_list(args))])
|
||||||
|
else:
|
||||||
|
logger.info(detail + '...')
|
||||||
|
subprocess.run(flatten_list(args), check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def node_bin(exec):
|
||||||
|
return os.path.join('node_modules', '.bin', exec)
|
|
@ -1,16 +1,16 @@
|
||||||
bandit==1.1.0
|
bandit==1.4.0
|
||||||
flake8==3.0.4
|
flake8==3.2.1
|
||||||
fontawesome_markdown==0.2.5
|
fontawesome_markdown==0.2.6
|
||||||
git+https://github.com/ryneeverett/python-markdown-comments.git
|
git+https://github.com/ryneeverett/python-markdown-comments.git
|
||||||
gitpython==2.0.8
|
gitpython==2.1.1
|
||||||
iso8601==0.1.11
|
iso8601==0.1.11
|
||||||
markdown==2.6.7
|
markdown==2.6.7
|
||||||
nose2==0.6.5
|
nose2==0.6.5
|
||||||
pelican-minify==0.9
|
pelican-minify==0.9
|
||||||
pelican==3.6.3
|
pelican==3.7.1
|
||||||
pyembed-markdown==1.1.0
|
pyembed-markdown==1.1.0
|
||||||
pygments-style-github==0.4
|
|
||||||
python-resize-image==1.1.3
|
python-resize-image==1.1.3
|
||||||
pyyaml==3.12
|
pyyaml==3.12
|
||||||
sh==1.11
|
safety==0.5.1
|
||||||
yamllint==1.4.1
|
sh==1.12.9
|
||||||
|
yamllint==1.6.0
|
||||||
|
|
1
runtime.txt
Normal file
1
runtime.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
python-3.5.1
|
18
scripts/build
Executable file
18
scripts/build
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ -z "$NVM_DIR" ]
|
||||||
|
then
|
||||||
|
NVM_DIR="$HOME/.nvm"
|
||||||
|
fi
|
||||||
|
|
||||||
|
. $NVM_DIR/nvm.sh
|
||||||
|
nvm install
|
||||||
|
nvm use
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
pyvenv env
|
||||||
|
|
||||||
|
env/bin/pip install -r requirements.txt
|
||||||
|
|
||||||
|
npm install
|
|
@ -1,44 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">>> WARNING: Building in Production Mode!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p theme/static/build/js/lib
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">> Compressing Libraries..."
|
|
||||||
uglifyjs node_modules/bootstrap-sass/assets/javascripts/bootstrap.js --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/lib/bootstrap.js
|
|
||||||
uglifyjs theme/static/build/js/lib/* --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/libs.js
|
|
||||||
else
|
|
||||||
echo ">> Building Libraries..."
|
|
||||||
cp node_modules/bootstrap-sass/assets/javascripts/bootstrap.js theme/static/build/js/lib/bootstrap.js
|
|
||||||
uglifyjs theme/static/build/js/lib/* --screw-ie8 --stats --keep-fnames -o theme/static/build/js/libs.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf theme/static/build/js/lib
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">> Compressing jQuery..."
|
|
||||||
uglifyjs node_modules/jquery/dist/jquery.js --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/jquery.js
|
|
||||||
else
|
|
||||||
echo ">> Building jQuery..."
|
|
||||||
cp node_modules/jquery/dist/jquery.js theme/static/build/js/jquery.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
echo ">> Building Application JS..."
|
|
||||||
browserify -t [ babelify --presets [ es2015 react ] ] theme/static/src/js/app.js -o theme/static/build/js/app.js
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">> Compressing Application..."
|
|
||||||
uglifyjs theme/static/build/js/app.js --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/app.js
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "> JS Built!"
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">>> WARNING: Building in Production Mode!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ">> Generating Pygments styles..."
|
|
||||||
env/bin/pygmentize -S github -f html -a .highlight > theme/static/src/scss/pygment.css
|
|
||||||
|
|
||||||
echo ">> Building SCSS..."
|
|
||||||
node-sass theme/static/src/scss/index.scss theme/static/build/css/index.css --source-map-embed
|
|
||||||
|
|
||||||
echo ">> Post-Processing..."
|
|
||||||
postcss -u autoprefixer -o theme/static/build/css/index.css theme/static/build/css/index.css
|
|
||||||
|
|
||||||
if [ "$NODE_ENV" = "production" ]
|
|
||||||
then
|
|
||||||
echo ">> Compressing CSS..."
|
|
||||||
cleancss -d --s0 -o theme/static/build/css/index.css theme/static/build/css/index.css
|
|
||||||
fi
|
|
10
scripts/run-build-server.sh
Executable file
10
scripts/run-build-server.sh
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Make pelican build on startup
|
||||||
|
|
||||||
|
|
||||||
|
echo "> Building Site"
|
||||||
|
pelican -v
|
||||||
|
|
||||||
|
echo "Starting Server"
|
||||||
|
npm start
|
|
@ -1,4 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cd output/
|
|
||||||
python3 -m pelican.server $PORT
|
|
36
scripts/test
Executable file
36
scripts/test
Executable file
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
export PATH=env/bin:${PATH}
|
||||||
|
export PATH=node_modules/.bin:${PATH}
|
||||||
|
|
||||||
|
FLAKE8_IGNORE=--ignore=E128,E501,E401,E402
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "> Running tests"
|
||||||
|
|
||||||
|
nose2
|
||||||
|
|
||||||
|
echo ">> Testing client-side"
|
||||||
|
eslint 'theme/static/src/js/'
|
||||||
|
sass-lint -vqc .sass-lint.yml
|
||||||
|
|
||||||
|
echo ">> Running flake8"
|
||||||
|
flake8 plugins/ $FLAKE8_IGNORE
|
||||||
|
flake8 config/ $FLAKE8_IGNORE
|
||||||
|
flake8 tests/ $FLAKE8_IGNORE
|
||||||
|
flake8 pelicanconf.py $FLAKE8_IGNORE
|
||||||
|
|
||||||
|
echo ">> Checking config"
|
||||||
|
yamllint config/social.yml
|
||||||
|
|
||||||
|
echo ">> Running spellcheck"
|
||||||
|
mdspell --en-gb -ranx theme/templates/**/*.* theme/templates/*.*
|
||||||
|
mdspell --en-gb -ranx content/**/*.md content/*.md content/**/*.html content/*.html
|
||||||
|
|
||||||
|
echo ">> Running security check"
|
||||||
|
nsp check
|
||||||
|
safety check
|
||||||
|
bandit -r plugins/ config/ tests/
|
||||||
|
|
||||||
|
echo "> Tests complete!"
|
|
@ -29,7 +29,7 @@ class TestCase(unittest.TestCase):
|
||||||
client = TestClient()
|
client = TestClient()
|
||||||
|
|
||||||
def get_children(self, content):
|
def get_children(self, content):
|
||||||
return str(list(content.children)[0])
|
return str(list(content.children)[0]).strip()
|
||||||
|
|
||||||
def assertTitle(self, content, title):
|
def assertTitle(self, content, title):
|
||||||
self.assertIn(title, content.title.string)
|
self.assertIn(title, content.title.string)
|
||||||
|
@ -37,3 +37,6 @@ class TestCase(unittest.TestCase):
|
||||||
def assertHeaderTitle(self, content, title):
|
def assertHeaderTitle(self, content, title):
|
||||||
header_title = content.find('h1', class_="section-heading")
|
header_title = content.find('h1', class_="section-heading")
|
||||||
self.assertIn(title, self.get_children(header_title))
|
self.assertIn(title, self.get_children(header_title))
|
||||||
|
|
||||||
|
def assertSamePath(self, p1, p2):
|
||||||
|
self.assertEqual(self.client.build_path(p1), self.client.build_path(p2))
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
from config import settings, DotDictionary
|
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
import pelicanconf as settings
|
||||||
|
from config import social as social_settings
|
||||||
from unittest import skipIf
|
from unittest import skipIf
|
||||||
from os import environ
|
from os import environ
|
||||||
|
|
||||||
|
@ -16,22 +17,24 @@ class CorePagesTestCase(TestCase):
|
||||||
|
|
||||||
def test_has_sitemap(self):
|
def test_has_sitemap(self):
|
||||||
content = self.client.get('sitemap.xml')
|
content = self.client.get('sitemap.xml')
|
||||||
self.assertIn(settings.url, content)
|
self.assertIn(settings.SITEURL, content)
|
||||||
|
|
||||||
def test_has_atom_feed(self):
|
def test_has_atom_feed(self):
|
||||||
content = self.client.get('feed.atom')
|
content = self.client.get('feed.atom')
|
||||||
self.assertIn(settings.url, content)
|
self.assertIn(settings.SITEURL, content)
|
||||||
|
|
||||||
def test_has_404_page(self):
|
def test_has_404_page(self):
|
||||||
content = self.client.get('.404.html')
|
content = self.client.get('.404.html')
|
||||||
self.assertTitle(content, '404')
|
self.assertTitle(content, '404')
|
||||||
|
|
||||||
|
|
||||||
|
class CommonPagesTestCase(TestCase):
|
||||||
def test_has_scripts(self):
|
def test_has_scripts(self):
|
||||||
content = self.client.get('index.html')
|
content = self.client.get('index.html')
|
||||||
for script in content.find_all('script'):
|
for script in content.find_all('script'):
|
||||||
if script.attrs.get('id') == 'piwik':
|
if script.attrs.get('id') == 'piwik':
|
||||||
continue
|
continue
|
||||||
self.client.exists(script.attrs['src'])
|
self.assertTrue(self.client.exists(script.attrs['src']))
|
||||||
|
|
||||||
def test_has_stylesheet(self):
|
def test_has_stylesheet(self):
|
||||||
content = self.client.get('index.html')
|
content = self.client.get('index.html')
|
||||||
|
@ -49,9 +52,25 @@ class CorePagesTestCase(TestCase):
|
||||||
content = self.client.get('index.html')
|
content = self.client.get('index.html')
|
||||||
footer = content.footer
|
footer = content.footer
|
||||||
for link in footer.find('p', class_="social").find_all('a'):
|
for link in footer.find('p', class_="social").find_all('a'):
|
||||||
self.assertIn(link.attrs['alt'], settings.footer_accounts)
|
self.assertIn(link.attrs['alt'], social_settings['footer_accounts'])
|
||||||
self.assertIn("fa fa-", str(list(link.children)[0]))
|
self.assertIn("fa fa-", str(list(link.children)[0]))
|
||||||
|
|
||||||
|
def test_navbar_links(self):
|
||||||
|
content = self.client.get('.404.html') # a page that isnt home
|
||||||
|
links = content.find('ul', class_='navbar-nav').find_all('a')
|
||||||
|
self.assertEqual(len(links), 5)
|
||||||
|
for link in links:
|
||||||
|
element = self.get_children(link)
|
||||||
|
self.assertEqual(link.attrs['href'], '/{}/'.format(element.lower()))
|
||||||
|
self.assertTrue(self.client.exists(link.attrs['href']))
|
||||||
|
|
||||||
|
def test_navbar_index_link(self):
|
||||||
|
content = self.client.get('.404.html') # a page that isnt home
|
||||||
|
link = content.find('a', class_='navbar-brand')
|
||||||
|
self.assertTrue(self.client.exists(link.attrs['href']))
|
||||||
|
self.assertSamePath(link.attrs['href'], '/')
|
||||||
|
self.assertEqual(self.get_children(link), settings.SITENAME)
|
||||||
|
|
||||||
@skipIf(not environ.get('BUILD_PRODUCTION', False), 'Not building production')
|
@skipIf(not environ.get('BUILD_PRODUCTION', False), 'Not building production')
|
||||||
def test_has_analytics(self):
|
def test_has_analytics(self):
|
||||||
content = self.client.get('index.html', False)
|
content = self.client.get('index.html', False)
|
||||||
|
@ -59,60 +78,10 @@ class CorePagesTestCase(TestCase):
|
||||||
self.assertNotEqual(piwik_script_tag, None)
|
self.assertNotEqual(piwik_script_tag, None)
|
||||||
piwik_script = self.get_children(piwik_script_tag)
|
piwik_script = self.get_children(piwik_script_tag)
|
||||||
self.assertIn('piwik.js', piwik_script)
|
self.assertIn('piwik.js', piwik_script)
|
||||||
self.assertIn(str(settings.piwik.site_id), piwik_script)
|
self.assertIn(str(settings.PIWIK['site_id']), piwik_script)
|
||||||
piwik_img = content.find('noscript', id='piwik').find('img')
|
piwik_img = content.find('noscript', id='piwik').find('img')
|
||||||
self.assertIn(settings.piwik.url, piwik_img.attrs['src'])
|
self.assertIn(settings.PIWIK['url'], piwik_img.attrs['src'])
|
||||||
self.assertIn(str(settings.piwik.site_id), piwik_img.attrs['src'])
|
self.assertIn(str(settings.PIWIK['site_id']), piwik_img.attrs['src'])
|
||||||
|
|
||||||
|
|
||||||
class DotDictionaryTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.test_dict = DotDictionary({
|
|
||||||
'foo': 'bar',
|
|
||||||
'bar': {
|
|
||||||
'foo': 'bar'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
def test_returns_value(self):
|
|
||||||
self.assertEqual(self.test_dict.foo, 'bar')
|
|
||||||
|
|
||||||
def test_returns_self_on_dict(self):
|
|
||||||
self.assertEqual(self.test_dict.bar, {
|
|
||||||
'foo': 'bar'
|
|
||||||
})
|
|
||||||
self.assertIsInstance(self.test_dict.bar, DotDictionary)
|
|
||||||
|
|
||||||
def test_set(self):
|
|
||||||
self.test_dict.baz = 'foo'
|
|
||||||
self.assertEqual(self.test_dict, {
|
|
||||||
'foo': 'bar',
|
|
||||||
'baz': 'foo',
|
|
||||||
'bar': {
|
|
||||||
'foo': 'bar'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
def test_delete(self):
|
|
||||||
del self.test_dict.bar
|
|
||||||
with self.assertRaises(KeyError):
|
|
||||||
print(self.test_dict.bar)
|
|
||||||
self.assertEqual(self.test_dict, {
|
|
||||||
'foo': 'bar'
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
class WrappedSettingTestCase(TestCase):
|
|
||||||
def test_has_data(self):
|
|
||||||
self.assertIsInstance(settings.settings, dict)
|
|
||||||
self.assertTrue(len(settings.settings))
|
|
||||||
|
|
||||||
def test_returns_values(self):
|
|
||||||
self.assertEqual(settings.language, 'en')
|
|
||||||
self.assertEqual(settings.timezone, 'Europe/London')
|
|
||||||
|
|
||||||
def test_returns_dict(self):
|
|
||||||
self.assertIsInstance(settings.accounts, DotDictionary)
|
|
||||||
|
|
||||||
|
|
||||||
class TestClientTestCase(TestCase):
|
class TestClientTestCase(TestCase):
|
||||||
|
|
|
@ -1,12 +1,24 @@
|
||||||
from tests import TestCase
|
from tests import TestCase
|
||||||
from config import settings
|
from config import social as social_settings
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
class HomepageTestCase(TestCase):
|
class HomepageTestCase(TestCase):
|
||||||
|
def test_about_section(self):
|
||||||
|
content = self.client.get('index.html')
|
||||||
|
about = content.find('section', id='about')
|
||||||
|
self.assertIsNotNone(about)
|
||||||
|
about_content = about.find('p')
|
||||||
|
self.assertNotEqual(self.get_children(about_content), '')
|
||||||
|
about_link = about.find('a')
|
||||||
|
self.assertTrue(self.client.exists(about_link.attrs['href']))
|
||||||
|
|
||||||
def test_blog_links(self):
|
def test_blog_links(self):
|
||||||
content = self.client.get('index.html')
|
content = self.client.get('index.html')
|
||||||
blogs = content.find('section', id='blog').find_all('div', class_="col-xs-12")
|
blog = content.find('section', id='blog')
|
||||||
|
blog_link = blog.find('a', class_='btn')
|
||||||
|
self.assertTrue(self.client.exists(blog_link.attrs['href']))
|
||||||
|
blogs = blog.find_all('div', class_="col-xs-12")
|
||||||
self.assertTrue(len(blogs) <= 4)
|
self.assertTrue(len(blogs) <= 4)
|
||||||
for post in blogs:
|
for post in blogs:
|
||||||
url = os.path.join(post.find('a').attrs['href'], 'index.html')
|
url = os.path.join(post.find('a').attrs['href'], 'index.html')
|
||||||
|
@ -19,6 +31,15 @@ class HomepageTestCase(TestCase):
|
||||||
url = os.path.join(project.attrs['href'], 'index.html')
|
url = os.path.join(project.attrs['href'], 'index.html')
|
||||||
self.assertTrue(self.client.exists(url))
|
self.assertTrue(self.client.exists(url))
|
||||||
|
|
||||||
|
def test_navbar(self):
|
||||||
|
content = self.client.get('index.html')
|
||||||
|
links = content.find('ul', class_='navbar-nav').find_all('a')
|
||||||
|
self.assertEqual(len(links), 5)
|
||||||
|
for link in links:
|
||||||
|
self.assertIn('page-scroll', link.attrs['class'])
|
||||||
|
element = self.get_children(link)
|
||||||
|
self.assertEqual(link.attrs['href'], '#' + element.lower())
|
||||||
|
|
||||||
|
|
||||||
class AboutPageTestCase(TestCase):
|
class AboutPageTestCase(TestCase):
|
||||||
def test_title(self):
|
def test_title(self):
|
||||||
|
@ -44,7 +65,7 @@ class AboutPageTestCase(TestCase):
|
||||||
self.assertEqual(len(tags), 1)
|
self.assertEqual(len(tags), 1)
|
||||||
tag = tags[0]
|
tag = tags[0]
|
||||||
self.assertEqual('medium', tag.attrs['data-theme'])
|
self.assertEqual('medium', tag.attrs['data-theme'])
|
||||||
self.assertEqual(settings.accounts.github[1], tag.attrs['data-github'])
|
self.assertEqual(social_settings['accounts']['github'][1], tag.attrs['data-github'])
|
||||||
|
|
||||||
|
|
||||||
class Page404TestCase(TestCase):
|
class Page404TestCase(TestCase):
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import './creative';
|
'use strict';
|
||||||
import ansi_up from 'ansi_up';
|
|
||||||
|
require('./creative');
|
||||||
|
var ansi_up = require('ansi_up');
|
||||||
|
var consts = require('./consts');
|
||||||
|
|
||||||
|
|
||||||
$('.image').each(function () { // setup div-image hybrids
|
$('.image').each(function () { // setup div-image hybrids
|
||||||
const ele = $(this);
|
var ele = $(this);
|
||||||
if (ele.data('image')) {
|
if (ele.data('image')) {
|
||||||
ele.css('background-image', 'url(' + ele.data('image') + ')');
|
ele.css('background-image', 'url(' + ele.data('image') + ')');
|
||||||
} else {
|
} else {
|
||||||
|
@ -12,6 +15,18 @@ $('.image').each(function () { // setup div-image hybrids
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.ansi-up').each(function () {
|
$('.ansi-up').each(function () {
|
||||||
const ele = $(this);
|
var ele = $(this);
|
||||||
ele.html(ansi_up.ansi_to_html(ele.html()));
|
ele.html(ansi_up.ansi_to_html(ele.html()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('.navbar-brand').bind('click', function (event) {
|
||||||
|
if ($('html').scrollTop() > consts.NAVBAR_HEIGHT) {
|
||||||
|
$('html, body').stop().animate({
|
||||||
|
scrollTop: 0
|
||||||
|
}, consts.SCROLL_SPEED);
|
||||||
|
} else {
|
||||||
|
window.location = '/';
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
6
theme/static/src/js/consts.js
Normal file
6
theme/static/src/js/consts.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
NAVBAR_HEIGHT: $('#main-nav').height(),
|
||||||
|
SCROLL_SPEED: 750
|
||||||
|
};
|
|
@ -1,37 +0,0 @@
|
||||||
/*!
|
|
||||||
* Start Bootstrap - Creative Bootstrap Theme (http://startbootstrap.com)
|
|
||||||
* Code licensed under the Apache License v2.0.
|
|
||||||
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// jQuery for page scrolling feature - requires jQuery Easing plugin
|
|
||||||
$('a.page-scroll').bind('click', function(event) {
|
|
||||||
const anchor = $(this);
|
|
||||||
$('html, body').stop().animate(
|
|
||||||
{
|
|
||||||
scrollTop: ($(anchor.attr('href')).offset().top - 50)
|
|
||||||
},
|
|
||||||
1250,
|
|
||||||
'easeInOutExpo'
|
|
||||||
);
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Highlight the top nav as scrolling occurs
|
|
||||||
$('body').scrollspy({
|
|
||||||
target: '.navbar-fixed-top',
|
|
||||||
offset: 51
|
|
||||||
});
|
|
||||||
|
|
||||||
// Closes the Responsive Menu on Menu Item Click
|
|
||||||
$('.navbar-collapse ul li a').click(function() {
|
|
||||||
$('.navbar-toggle:visible').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// Offset for Main Navigation
|
|
||||||
$('#main-nav').affix({
|
|
||||||
offset: {
|
|
||||||
top: 50
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,6 +1,41 @@
|
||||||
import 'jquery.easing';
|
'use strict';
|
||||||
import './creative';
|
|
||||||
|
|
||||||
import WOW from 'wow.js';
|
var WOW = require('wow.js');
|
||||||
|
var consts = require('../consts');
|
||||||
|
|
||||||
new WOW().init();
|
new WOW().init();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Start Bootstrap - Creative Bootstrap Theme (http://startbootstrap.com)
|
||||||
|
* Code licensed under the Apache License v2.0.
|
||||||
|
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
|
||||||
|
* (Edited for theorangeone.net)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// jQuery for page scrolling feature
|
||||||
|
$('a.page-scroll').bind('click', function(event) {
|
||||||
|
$('html, body').stop().animate(
|
||||||
|
{ scrollTop: $($(this).attr('href')).offset().top - consts.NAVBAR_HEIGHT },
|
||||||
|
consts.SCROLL_SPEED
|
||||||
|
);
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Highlight the top nav as scrolling occurs
|
||||||
|
$('body').scrollspy({
|
||||||
|
target: '.navbar-fixed-top',
|
||||||
|
offset: consts.NAVBAR_HEIGHT + 1
|
||||||
|
});
|
||||||
|
|
||||||
|
// Closes the Responsive Menu on Menu Item Click
|
||||||
|
$('.navbar-collapse ul li a').click(function() {
|
||||||
|
$('.navbar-toggle:visible').click();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Offset for Main Navigation
|
||||||
|
$('#main-nav').affix({
|
||||||
|
offset: {
|
||||||
|
top: consts.NAVBAR_HEIGHT * 1.5
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
/* @group Libraries */
|
/* @group Libraries */
|
||||||
@import "node_modules/bootstrap-sass/assets/stylesheets/_bootstrap";
|
@import "node_modules/bootstrap-sass/assets/stylesheets/_bootstrap";
|
||||||
@import "node_modules/animate.css/animate";
|
@import "node_modules/animate.css/animate";
|
||||||
|
@import "node_modules/pygments-css/github";
|
||||||
|
|
||||||
$fa-font-path: "../fonts";
|
$fa-font-path: "../fonts";
|
||||||
@import "node_modules/font-awesome/scss/font-awesome";
|
@import "node_modules/font-awesome/scss/font-awesome";
|
||||||
|
@ -18,7 +19,6 @@ $fa-font-path: "../fonts";
|
||||||
|
|
||||||
/* @group Other Imports */
|
/* @group Other Imports */
|
||||||
@import "creative/creative";
|
@import "creative/creative";
|
||||||
@import "pygment";
|
|
||||||
@import "homepage";
|
@import "homepage";
|
||||||
@import "footer";
|
@import "footer";
|
||||||
@import "library-overrides";
|
@import "library-overrides";
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'extras/header.html' with context %}
|
{% include 'extras/header.html' with instance=article %}
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{ article.content }}
|
{{ article.content }}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
{% include "extras/footer.html" %}
|
{% include "extras/footer.html" %}
|
||||||
|
|
||||||
<script src="/static/js/jquery.js" type="text/javascript"></script>
|
<script src="/static/js/jquery.js" type="text/javascript"></script>
|
||||||
<script src="/static/js/libs.js" type="text/javascript"></script>
|
<script src="/static/js/bootstrap.js" type="text/javascript"></script>
|
||||||
<script src="/static/js/app.js" type="text/javascript"></script>
|
<script src="/static/js/app.js" type="text/javascript"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'extras/header.html' with context %}
|
{% include 'extras/header.html' with instance=article %}
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="text-right small">
|
<p class="text-right small">
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
---
|
||||||
|
hide_from_nav: true
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
I've always had a keen interest with security and privacy. Whether it's protecting my personal information, or needlessly encrypting things, security is very important to me. With an interest in keeping things secure, comes an interest in breaking through others security.
|
I've always had a keen interest with security and privacy. Whether it's protecting my personal information, or needlessly encrypting things, security is very important to me. With an interest in keeping things secure, comes an interest in breaking through others security.
|
||||||
|
|
||||||
Whenever I start using a new tool, or piece of software, I almost instantly go to the security settings and check out what it supports. The same goes for making the software, I've been known to spend a lot of extra time working on the security aspect more than actual features.
|
Whenever I start using a new tool, or piece of software, I almost instantly go to the security settings and check out what it supports. The same goes for making the software, I've been known to spend a lot of extra time working on the security aspect more than actual features.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section id="header" class="bg-primary image" data-image="{{ article.image }}">
|
<section id="header" class="bg-primary image" data-image="{{ instance.image }}">
|
||||||
<div class="container text-center text-uppercase">
|
<div class="container text-center text-uppercase">
|
||||||
<h1 class="section-heading">{{ article.title }}</h1>
|
<h1 class="section-heading">{{ instance.title }}</h1>
|
||||||
<hr class="light">
|
<hr class="light">
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -8,15 +8,16 @@
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<a class="navbar-brand page-scroll" href="/">
|
<a class="navbar-brand page-scroll" href="/">
|
||||||
TheOrangeOne
|
{{ SITENAME }}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse navbar-collapse" id="main-navbar">
|
<div class="collapse navbar-collapse" id="main-navbar">
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
<li><a href="/about/">About</a></li>
|
{% for category, articles in categories %}
|
||||||
<li><a href="/blog/">Blog</a></li>
|
{% if not category.page.hide_from_nav %}
|
||||||
<li><a href="/projects/">Projects</a></li>
|
<li><a href="/{{ category.url }}">{{ category.name }}</a></li>
|
||||||
<li><a href="/setup/">Setup</a></li>
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
<section id="header" class="bg-primary image" data-image="{{ page.image }}">
|
|
||||||
<div class="container text-center text-uppercase">
|
|
||||||
<h1 class="section-heading">{{ page.title }}</h1>
|
|
||||||
<hr class="light">
|
|
||||||
</div>
|
|
||||||
</section>
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if not page.no_container %}
|
{% if not page.no_container %}
|
||||||
{% include 'extras/page-header.html' with context %}
|
{% include 'extras/header.html' with instance=page %}
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{{ page.content }}
|
{{ page.content }}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include 'extras/header.html' with context %}
|
{% include 'extras/header.html' with instance=article %}
|
||||||
<section>
|
<section>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="text-right small">
|
<p class="text-right small">
|
||||||
|
@ -18,7 +18,9 @@
|
||||||
{{ article.content }}
|
{{ article.content }}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div class="container text-center">
|
{% if article.download_link or article.repo %}
|
||||||
|
<section class="text-center">
|
||||||
|
<div class="container">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
{% if article.download_link %}
|
{% if article.download_link %}
|
||||||
<a class="btn btn-primary btn-xl" href="{{ article.download_link }}">Download {{ article.title }}</a>
|
<a class="btn btn-primary btn-xl" href="{{ article.download_link }}">Download {{ article.title }}</a>
|
||||||
|
@ -29,4 +31,6 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Reference in a new issue