1
Fork 0

Merge pull request #4 from RealOrangeOne/pelican-refresh

Pelican refresh
This commit is contained in:
Jake Howard 2017-01-15 19:47:12 +00:00 committed by GitHub
commit b4c50c26e7
48 changed files with 506 additions and 407 deletions

2
.buildpacks Normal file
View file

@ -0,0 +1,2 @@
https://github.com/heroku/heroku-buildpack-nodejs
https://github.com/heroku/heroku-buildpack-python

View file

@ -1,6 +1,6 @@
{
"extends": "./node_modules/eslint-config/.eslintrc",
"extends": "eslint-config-dabapps/base/.eslintrc",
"globals": {
$: true
"$": true
}
}

2
.gitignore vendored
View file

@ -60,7 +60,5 @@ target/
# Pelican stuff
output/
theme/static/build/
pelican_plugins/
theme/static/src/scss/pygment.css
deploy/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "pelican_plugins"]
path = pelican_plugins
url = https://github.com/getpelican/pelican-plugins

2
.nvmrc
View file

@ -1 +1 @@
5.10.1
6.9.4

View file

@ -56,6 +56,7 @@ rules:
force-pseudo-nesting: 0
force-element-nesting: 0
placeholder-in-extend: 0
no-url-domains: 0
nesting-depth:
- 2
-

View file

@ -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
View file

@ -0,0 +1 @@
web: bash scripts/run-build-server.sh

View file

@ -2,13 +2,14 @@
[![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
```bash
make
./scripts/build
```
## Run Tests
```bash
make test
./scripts/test
```

View file

@ -2,24 +2,22 @@ machine:
python:
version: 3.5.1
node:
version: 5.11.1
version: 6.9.4
environment:
BUILD_PRODUCTION: true
NODE_ENV: production
NPM_CONFIG_PRODUCTION: false
checkout:
post:
- git submodule sync
- git submodule update --init --recursive
dependencies:
pre:
- make -B
override:
- scripts/build
- pelican -v
test:
override:
- make test
deployment:
production:
branch: master
commands:
- git config --global user.email "git@theorangeone.net"
- git config --global user.name "TheOrangeOne"
- make upload
- scripts/test

View file

@ -2,29 +2,12 @@ import yaml
import os.path
class DotDictionary(dict):
def __getattr__(self, attr):
value = self[attr]
if type(value) == dict:
value = DotDictionary(value)
return value
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
settings_dir = os.path.dirname(__file__)
class WrappedSettings:
def __init__(self):
self.settings_dir = os.path.join(os.path.dirname(__file__), 'config.yml')
settings = open(self.settings_dir)
self.settings = yaml.safe_load(settings)
def get_config(filename):
with open(os.path.join(settings_dir, '{}.yml'.format(filename))) as f:
return yaml.safe_load(f)
def __getattr__(self, name):
value = self.settings[name]
if type(value) == dict:
value = DotDictionary(value)
return value
def __str__(self):
return str(self.settings)
settings = WrappedSettings()
social = get_config('social')

View file

@ -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:
github:
- GitHub
@ -103,7 +85,3 @@ footer_accounts:
- instagram
- youtube
- flickr
piwik:
url: piwik.theorangeone.net
site_id: 1

View file

@ -1 +0,0 @@
web: bash scripts/server.sh

View file

@ -3,8 +3,9 @@
"version": "4.0.0",
"description": " Source code for TheOrangeOne.net",
"scripts": {
"build-js": "./scripts/build-js.sh",
"build-scss": "./scripts/build-scss.sh"
"postinstall": "npm run create-build-dirs",
"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": {
"type": "git",
@ -14,27 +15,23 @@
"animate.css": "=3.5.2",
"ansi_up": "=1.3.0",
"bootstrap-sass": "=3.3.7",
"font-awesome": "=4.6.3",
"jquery": "=2.2.3",
"jquery.easing": "=1.3.2",
"font-awesome": "=4.7.0",
"jquery": "=3.1.1",
"pygments-css": "=1.0.0",
"tstatic": "RealOrangeOne/tstatic",
"underscore": "=1.8.3",
"wow.js": "=1.2.0"
"wow.js": "=1.2.2"
},
"devDependencies": {
"autoprefixer": "=6.5.0",
"babel-preset-es2015": "=6.13.2",
"babel-preset-react": "=6.11.1",
"babelify": "=7.3.0",
"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",
"autoprefixer": "=6.6.1",
"browserify": "=13.3.0",
"clean-css": "=3.4.23",
"eslint-config": "dabapps/eslint-config.git#2.0.4",
"markdown-spellcheck": "=0.11.0",
"node-sass": "=3.8.0",
"nsp": "=2.6.1",
"node-sass": "=4.3.0",
"nsp": "=2.6.2",
"postcss-cli": "=2.6.0",
"sass-lint": "=1.9.1",
"uglify-js": "=2.7.3"
"sass-lint": "=1.10.2",
"uglify-js": "=2.7.5"
}
}

1
pelican_plugins Submodule

@ -0,0 +1 @@
Subproject commit 6cd5a608b45fdc21bb11ed1f72a151cdb71c1169

View file

@ -1,23 +1,25 @@
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
from git import Repo
import sys, os
sys.path.insert(0, os.path.realpath('./'))
from config import settings
# Global core settings
AUTHOR = settings.author
SITENAME = settings.site_name
SITEURL = settings.url
PATH = '../content'
TIMEZONE = settings.timezone
DEFAULT_LANG = settings.language
AUTHOR = "Jake Howard"
SITENAME = "TheOrangeOne"
SITEURL = "https://theorangeone.net"
PATH = 'content'
TIMEZONE = "Europe/London"
DEFAULT_LANG = "en"
PAGE_PATHS = ["pages"]
THEME = "../theme"
THEME = "theme"
THEME_STATIC_DIR = "static"
THEME_STATIC_PATHS = ["static/build"]
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
DEFAULT_PAGINATION = False
@ -30,11 +32,13 @@ FOOTER_LINKS = links.footer()
INDEX_PROJECTS = links.index_projects()
# Extra config
REPO = Repo(search_parent_directories=True)
BUILD_PRODUCTION = 'BUILD_PRODUCTION' in os.environ
from plugins import image_resizer
META_IMAGES = image_resizer.generate()
PIWIK = settings.piwik
PIWIK = {
'url': 'piwik.theorangeone.net',
'site_id': 1
}
# Disable some pages
TAG_URL = False
@ -61,14 +65,22 @@ FEED_ATOM = 'feed.atom'
FEED_DOMAIN = SITEURL
# Setup plugins
PLUGIN_PATHS = ["../pelican_plugins", "../plugins"]
PLUGINS = settings.pelican_plugins
PLUGIN_PATHS = ["plugins", "pelican_plugins"]
PLUGINS = [
'sitemap',
'pelican-jinja2content',
'metatags',
'autopages',
'screenfetch',
'post_build',
'static_build'
]
if BUILD_PRODUCTION:
PLUGINS.append("minify") # only minify on production build
SITEMAP = {
"format": settings.sitemap_format
"format": 'xml'
}
CATEGORY_PAGE_PATH = "theme/templates/categories"
MINIFY = {
@ -81,14 +93,16 @@ MINIFY = {
from fontawesome_markdown import FontAwesomeExtension
from pyembed.markdown import PyEmbedMarkdown
from mkdcomments import CommentsExtension
MD_EXTENSIONS = [
FontAwesomeExtension(),
PyEmbedMarkdown(),
CommentsExtension(),
'codehilite(css_class=highlight)',
'extra'
]
MARKDOWN = {
'extensions': [
FontAwesomeExtension(),
PyEmbedMarkdown(),
CommentsExtension(),
'codehilite(css_class=highlight)',
'extra'
],
"output_format": "html5"
}
# Setup jinja2 filters
from plugins import filters
JINJA_FILTERS = {
@ -96,3 +110,12 @@ JINJA_FILTERS = {
"category_find": filters.category_find,
"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
View 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

View file

@ -1,6 +1,6 @@
from collections import namedtuple
from random import shuffle
from config import settings, DotDictionary
from config import social
ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
@ -8,20 +8,20 @@ ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
def accounts():
links = {}
for key, (site, user, url, icon) in settings.accounts.items():
links[key] = DotDictionary({
for key, (site, user, url, icon) in social['accounts'].items():
links[key] = {
'key': key,
'site': site,
'username': user,
'url': url.format(user),
'icon': icon
})
}
return links
def footer():
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():

View file

@ -17,10 +17,10 @@ def html_to_raw(html):
def get_twiter_tags(instance):
return {
"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: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:alt": html_to_raw(instance.metadata.get("summary", "")),
"twitter:url": os.path.join(instance.settings.get("SITEURL", ""), instance.url)

View 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
View 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
View 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
View 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)

View file

@ -1,16 +1,16 @@
bandit==1.1.0
flake8==3.0.4
fontawesome_markdown==0.2.5
bandit==1.4.0
flake8==3.2.1
fontawesome_markdown==0.2.6
git+https://github.com/ryneeverett/python-markdown-comments.git
gitpython==2.0.8
gitpython==2.1.1
iso8601==0.1.11
markdown==2.6.7
nose2==0.6.5
pelican-minify==0.9
pelican==3.6.3
pelican==3.7.1
pyembed-markdown==1.1.0
pygments-style-github==0.4
python-resize-image==1.1.3
pyyaml==3.12
sh==1.11
yamllint==1.4.1
safety==0.5.1
sh==1.12.9
yamllint==1.6.0

1
runtime.txt Normal file
View file

@ -0,0 +1 @@
python-3.5.1

18
scripts/build Executable file
View 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

View file

@ -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!"

View file

@ -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
View file

@ -0,0 +1,10 @@
#/usr/bin/env bash
# Make pelican build on startup
echo "> Building Site"
pelican -v
echo "Starting Server"
npm start

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
cd output/
python3 -m pelican.server $PORT

36
scripts/test Executable file
View 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!"

View file

@ -29,7 +29,7 @@ class TestCase(unittest.TestCase):
client = TestClient()
def get_children(self, content):
return str(list(content.children)[0])
return str(list(content.children)[0]).strip()
def assertTitle(self, content, title):
self.assertIn(title, content.title.string)
@ -37,3 +37,6 @@ class TestCase(unittest.TestCase):
def assertHeaderTitle(self, content, title):
header_title = content.find('h1', class_="section-heading")
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))

View file

@ -1,6 +1,7 @@
from tests import TestCase
from config import settings, DotDictionary
from bs4 import BeautifulSoup
import pelicanconf as settings
from config import social as social_settings
from unittest import skipIf
from os import environ
@ -16,22 +17,24 @@ class CorePagesTestCase(TestCase):
def test_has_sitemap(self):
content = self.client.get('sitemap.xml')
self.assertIn(settings.url, content)
self.assertIn(settings.SITEURL, content)
def test_has_atom_feed(self):
content = self.client.get('feed.atom')
self.assertIn(settings.url, content)
self.assertIn(settings.SITEURL, content)
def test_has_404_page(self):
content = self.client.get('.404.html')
self.assertTitle(content, '404')
class CommonPagesTestCase(TestCase):
def test_has_scripts(self):
content = self.client.get('index.html')
for script in content.find_all('script'):
if script.attrs.get('id') == 'piwik':
continue
self.client.exists(script.attrs['src'])
self.assertTrue(self.client.exists(script.attrs['src']))
def test_has_stylesheet(self):
content = self.client.get('index.html')
@ -49,9 +52,25 @@ class CorePagesTestCase(TestCase):
content = self.client.get('index.html')
footer = content.footer
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]))
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')
def test_has_analytics(self):
content = self.client.get('index.html', False)
@ -59,60 +78,10 @@ class CorePagesTestCase(TestCase):
self.assertNotEqual(piwik_script_tag, None)
piwik_script = self.get_children(piwik_script_tag)
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')
self.assertIn(settings.piwik.url, 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)
self.assertIn(settings.PIWIK['url'], piwik_img.attrs['src'])
self.assertIn(str(settings.PIWIK['site_id']), piwik_img.attrs['src'])
class TestClientTestCase(TestCase):

View file

@ -1,12 +1,24 @@
from tests import TestCase
from config import settings
from config import social as social_settings
import os.path
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):
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)
for post in blogs:
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')
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):
def test_title(self):
@ -44,7 +65,7 @@ class AboutPageTestCase(TestCase):
self.assertEqual(len(tags), 1)
tag = tags[0]
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):

View file

@ -1,9 +1,12 @@
import './creative';
import ansi_up from 'ansi_up';
'use strict';
require('./creative');
var ansi_up = require('ansi_up');
var consts = require('./consts');
$('.image').each(function () { // setup div-image hybrids
const ele = $(this);
var ele = $(this);
if (ele.data('image')) {
ele.css('background-image', 'url(' + ele.data('image') + ')');
} else {
@ -12,6 +15,18 @@ $('.image').each(function () { // setup div-image hybrids
});
$('.ansi-up').each(function () {
const ele = $(this);
var ele = $(this);
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();
});

View file

@ -0,0 +1,6 @@
'use strict';
module.exports = {
NAVBAR_HEIGHT: $('#main-nav').height(),
SCROLL_SPEED: 750
};

View file

@ -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
}
});

View file

@ -1,6 +1,41 @@
import 'jquery.easing';
import './creative';
'use strict';
import WOW from 'wow.js';
var WOW = require('wow.js');
var consts = require('../consts');
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
}
});

View file

@ -10,6 +10,7 @@
/* @group Libraries */
@import "node_modules/bootstrap-sass/assets/stylesheets/_bootstrap";
@import "node_modules/animate.css/animate";
@import "node_modules/pygments-css/github";
$fa-font-path: "../fonts";
@import "node_modules/font-awesome/scss/font-awesome";
@ -18,7 +19,6 @@ $fa-font-path: "../fonts";
/* @group Other Imports */
@import "creative/creative";
@import "pygment";
@import "homepage";
@import "footer";
@import "library-overrides";

View file

@ -9,7 +9,7 @@
{% endblock %}
{% block content %}
{% include 'extras/header.html' with context %}
{% include 'extras/header.html' with instance=article %}
<section>
<div class="container">
{{ article.content }}

View file

@ -31,7 +31,7 @@
{% include "extras/footer.html" %}
<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>
</body>
</html>

View file

@ -9,7 +9,7 @@
{% endblock %}
{% block content %}
{% include 'extras/header.html' with context %}
{% include 'extras/header.html' with instance=article %}
<section>
<div class="container">
<p class="text-right small">

View file

@ -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.
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.

View file

@ -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">
<h1 class="section-heading">{{ article.title }}</h1>
<h1 class="section-heading">{{ instance.title }}</h1>
<hr class="light">
</div>
</section>

View file

@ -8,15 +8,16 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand page-scroll" href="/">
TheOrangeOne
{{ SITENAME }}
</a>
</div>
<div class="collapse navbar-collapse" id="main-navbar">
<ul class="nav navbar-nav navbar-right">
<li><a href="/about/">About</a></li>
<li><a href="/blog/">Blog</a></li>
<li><a href="/projects/">Projects</a></li>
<li><a href="/setup/">Setup</a></li>
{% for category, articles in categories %}
{% if not category.page.hide_from_nav %}
<li><a href="/{{ category.url }}">{{ category.name }}</a></li>
{% endif %}
{% endfor %}
</ul>
</div>
</div>

View file

@ -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>

View file

@ -10,7 +10,7 @@
{% block content %}
{% if not page.no_container %}
{% include 'extras/page-header.html' with context %}
{% include 'extras/header.html' with instance=page %}
<section>
<div class="container">
{{ page.content }}

View file

@ -9,7 +9,7 @@
{% endblock %}
{% block content %}
{% include 'extras/header.html' with context %}
{% include 'extras/header.html' with instance=article %}
<section>
<div class="container">
<p class="text-right small">
@ -18,15 +18,19 @@
{{ article.content }}
</div>
</section>
<div class="container text-center">
<div class="btn-group">
{% if article.download_link %}
<a class="btn btn-primary btn-xl" href="{{ article.download_link }}">Download {{ article.title }}</a>
{% endif %}
{% if article.download_link or article.repo %}
<section class="text-center">
<div class="container">
<div class="btn-group">
{% if article.download_link %}
<a class="btn btn-primary btn-xl" href="{{ article.download_link }}">Download {{ article.title }}</a>
{% endif %}
{% if article.repo %}
<a class="btn btn-github btn-xl" href="{{ article.repo }}"><i class="fa fa-github fa-lg"></i> View on Github</a>
{% endif %}
</div>
</div>
{% if article.repo %}
<a class="btn btn-github btn-xl" href="{{ article.repo }}"><i class="fa fa-github fa-lg"></i> View on Github</a>
{% endif %}
</div>
</div>
</section>
{% endif %}
{% endblock %}