10
.gitignore
vendored
|
@ -50,7 +50,6 @@ coverage.xml
|
||||||
*.log
|
*.log
|
||||||
collected-static/
|
collected-static/
|
||||||
node_modules/
|
node_modules/
|
||||||
static/build
|
|
||||||
|
|
||||||
# Sphinx documentation
|
# Sphinx documentation
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
@ -58,5 +57,10 @@ docs/_build/
|
||||||
# PyBuilder
|
# PyBuilder
|
||||||
target/
|
target/
|
||||||
|
|
||||||
private/
|
# Pelican stuff
|
||||||
database.db
|
output/
|
||||||
|
theme/static/build/
|
||||||
|
pelican_plugins/
|
||||||
|
|
||||||
|
theme/static/src/scss/pygment.css
|
||||||
|
deploy/
|
||||||
|
|
61
.sass-lint.yml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
files:
|
||||||
|
include: 'theme/static/src/scss/**/*.scss'
|
||||||
|
|
||||||
|
rules:
|
||||||
|
clean-import-paths: 0
|
||||||
|
extends-before-mixins: 2
|
||||||
|
extends-before-declarations: 2
|
||||||
|
force-attribute-nesting: 0
|
||||||
|
no-css-comments: 0
|
||||||
|
no-debug: 1
|
||||||
|
no-duplicate-properties:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
exclude:
|
||||||
|
- src
|
||||||
|
- transform
|
||||||
|
no-ids: 0
|
||||||
|
no-important: 0
|
||||||
|
no-transition-all: 0
|
||||||
|
no-vendor-prefixes: 2
|
||||||
|
no-qualifying-elements: 0
|
||||||
|
no-url-protocols: 0
|
||||||
|
no-warn: 1
|
||||||
|
hex-notation:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
style: uppercase
|
||||||
|
indentation:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
size: 2
|
||||||
|
variable-name-format:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
allow-leading-underscore: false
|
||||||
|
convention: hyphenatedlowercase
|
||||||
|
quotes:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
style: double
|
||||||
|
property-sort-order:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
order: 'concentric'
|
||||||
|
empty-line-between-blocks: 2
|
||||||
|
space-after-bang: 2
|
||||||
|
space-after-colon: 2
|
||||||
|
space-after-comma: 2
|
||||||
|
space-before-bang: 2
|
||||||
|
space-before-brace: 2
|
||||||
|
space-before-colon: 2
|
||||||
|
space-between-parens: 2
|
||||||
|
trailing-semicolon: 2
|
||||||
|
leading-zero: 0
|
||||||
|
single-line-per-selector: 0
|
||||||
|
force-pseudo-nesting: 0
|
||||||
|
force-element-nesting: 0
|
||||||
|
nesting-depth:
|
||||||
|
- 2
|
||||||
|
-
|
||||||
|
max-depth: 5
|
59
.spelling
|
@ -3,36 +3,55 @@
|
||||||
# global dictionary is at the start, file overrides afterwards
|
# global dictionary is at the start, file overrides afterwards
|
||||||
# one word per line, to define a file override use ' - filename'
|
# one word per line, to define a file override use ' - filename'
|
||||||
# where filename is relative to this configuration file
|
# where filename is relative to this configuration file
|
||||||
Django
|
_list
|
||||||
SQLite
|
kickstart
|
||||||
eg
|
olds
|
||||||
MyWindowsHosting
|
sitemap.xml
|
||||||
nginx
|
gittime
|
||||||
backends
|
protonmail
|
||||||
PyGame
|
hotmail
|
||||||
|
swiss
|
||||||
|
fastmail
|
||||||
|
rackspace
|
||||||
|
runbox
|
||||||
|
dabapps
|
||||||
|
qwerks
|
||||||
|
LanSchool
|
||||||
|
astrill
|
||||||
|
OpenVPN
|
||||||
|
config
|
||||||
|
openvpn-compatable
|
||||||
|
_link
|
||||||
|
pygame
|
||||||
easter
|
easter
|
||||||
_Enabler
|
bsod-enabler
|
||||||
Hipchat
|
_enabler
|
||||||
DabApps
|
dotfiles
|
||||||
JakeSidSmith
|
owncloud
|
||||||
|
hipchat
|
||||||
|
jakesidsmith
|
||||||
facepalm
|
facepalm
|
||||||
notsureif
|
notsureif
|
||||||
wat
|
wat
|
||||||
premis
|
|
||||||
hipchat
|
|
||||||
plugin
|
plugin
|
||||||
firefox
|
firefox
|
||||||
Jetpack
|
jetpack
|
||||||
Javascript
|
javascript
|
||||||
facebook
|
jsfuckify
|
||||||
github
|
morese-code-decoder
|
||||||
|
morse-code
|
||||||
morse
|
morse
|
||||||
|
centers
|
||||||
|
backends
|
||||||
wikipedia
|
wikipedia
|
||||||
iframe
|
iframe
|
||||||
querystring
|
querystring
|
||||||
javascript
|
|
||||||
jQuery
|
jQuery
|
||||||
gists
|
gists
|
||||||
Lenovo
|
lenovo
|
||||||
Collyer's
|
_title
|
||||||
|
collyer's
|
||||||
til
|
til
|
||||||
|
A.L.I.C.E
|
||||||
|
ethernet
|
||||||
|
everyones
|
||||||
|
|
70
Makefile
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
cp -R node_modules/font-awesome/fonts theme/static/build/
|
||||||
|
npm run build-js
|
||||||
|
npm run build-scss
|
||||||
|
@echo ">> Building pelican..."
|
||||||
|
$(PELICAN) -o $(OUTPUTDIR) -v
|
||||||
|
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: node_modules pelican_plugins
|
||||||
|
|
||||||
|
pelican_plugins:
|
||||||
|
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
|
||||||
|
|
||||||
|
node_modules:
|
||||||
|
npm install
|
||||||
|
|
||||||
|
|
||||||
|
test: lint spellcheck
|
||||||
|
|
||||||
|
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)/pelicanconf.py $(FLAKE8_IGNORE)
|
||||||
|
|
||||||
|
spellcheck:
|
||||||
|
$(NODE_BIN)/mdspell --en-gb -ranx theme/templates/**/*.* theme/templates/*.*
|
||||||
|
$(NODE_BIN)/mdspell --en-gb -ranx content/**/*.* content/*.*
|
||||||
|
|
||||||
|
|
||||||
|
upload: build
|
||||||
|
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
|
||||||
|
rm -rf $(DEPLOY_DIR)
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: build clean test lint install upload
|
11
README.md
|
@ -1,3 +1,14 @@
|
||||||
# My Website
|
# My Website
|
||||||
|
|
||||||
[![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)
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
```bash
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run Tests
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
28
build
|
@ -1,28 +0,0 @@
|
||||||
#!/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 --upgrade
|
|
||||||
|
|
||||||
scripts/get-private-data.sh
|
|
||||||
|
|
||||||
npm install
|
|
||||||
npm run build $@
|
|
||||||
|
|
||||||
env/bin/python manage.py collectstatic --noinput
|
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
|
||||||
then
|
|
||||||
echo ">> Running Migrations..."
|
|
||||||
env/bin/python manage.py migrate
|
|
||||||
fi
|
|
21
circle.yml
|
@ -1,20 +1,23 @@
|
||||||
machine:
|
machine:
|
||||||
python:
|
python:
|
||||||
version: 3.4.2
|
version: 3.5.1
|
||||||
node:
|
node:
|
||||||
version: 5.10.1
|
version: 5.11.1
|
||||||
environment:
|
environment:
|
||||||
DEBUG: true
|
|
||||||
DATABASE_URL: sqlite://~/database.db
|
|
||||||
EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend
|
|
||||||
BUILD_PRODUCTION: true
|
BUILD_PRODUCTION: true
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
pre:
|
pre:
|
||||||
- ./build
|
- make install
|
||||||
cache_directories:
|
- export NODE_ENV=production
|
||||||
- env
|
- make build
|
||||||
|
|
||||||
test:
|
test:
|
||||||
override:
|
override:
|
||||||
- ./runtests
|
- make test
|
||||||
|
|
||||||
|
deployment:
|
||||||
|
production:
|
||||||
|
branch: master
|
||||||
|
commands:
|
||||||
|
- make upload
|
||||||
|
|
BIN
content/assets/img/header.jpg
Normal file
After Width: | Height: | Size: 784 KiB |
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 237 KiB |
4
content/assets/robots.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
||||||
|
|
||||||
|
SITEMAP: /sitemap.xml
|
5
content/blog/7-languages-7-weeks.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
title: 7 languages in 7 weeks
|
||||||
|
gittime: off
|
||||||
|
date: 2016-05-05
|
||||||
|
template: blog
|
||||||
|
status: draft
|
5
content/blog/apprenticeship-level-3.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
title: My Level 3 Apprenticeship
|
||||||
|
gittime: off
|
||||||
|
date: 2016-07-06
|
||||||
|
template: blog
|
||||||
|
status: draft
|
40
content/blog/protonmail-replace-your-email.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
title: ProtonMail - Can it replace your email provider?
|
||||||
|
template: blog
|
||||||
|
date: 2016-06-28
|
||||||
|
image: https://protonmail.com/images/main-banner.jpg
|
||||||
|
summary: Incredibly secure, easy to use, but are it's trade-offs worth it?
|
||||||
|
|
||||||
|
Throughout my life, I've had many different email providers, starting with hotmail almost 10 years ago. In more recent years, I've been focusing more on ways I can secure my emails, both from potential intruders, or governments. No, I may not have anything to hide, but that doesn't mean I don't want the information I to have to be accessible easily.
|
||||||
|
|
||||||
|
Originally I thought the best way to keep things secure, and out of the hands of any government body was to host it all myself. This came with a number of problems, mostly due to my lack of experience running anything like this, which lead to problems with my spam filter not running, and any emails I did send ending up in peoples spam folders. It was after this I decided to look into hosted options, I'm happy to pay money to have someone else look after the email servers for me, even if that's at the cost of using another companies infrastructure.
|
||||||
|
|
||||||
|
After searching around for a while, I stumbled on a company called _ProtonMail_, who claimed to be the most secure email host ever. Reading into the technologies they use, and watching one of their team members on a TED talk, I realised they weren't lying, their platform really was secure!
|
||||||
|
|
||||||
|
ProtonMail use a combination of open-source technologies, a closed-access platform, and swiss data centers to protect emails better than anyone else! The only way you can access your emails is by using their custom apps for Android, iOS, and web. Whilst this is annoying, it does mean the system isn't accessible through conventional protocols such as IMAP and POP3, which would considerably lower the security. The web portal itself is entirely open-source, and highly encourages any contributions, which should help make the platform even more secure, and help pump out features fast.
|
||||||
|
|
||||||
|
In April 2016, I signed up to ProtonMail's _Plus_ tier, and switched all my domains to use their servers as an email provider. The setup process itself was incredibly simple, it showed you every DNS record you needed to create, so all you had to do was copy-paste! It also showed you extra records you could add to aid the security of your emails, and protect against people sending out emails using your domain, including DKIM signatures, and SPF records.
|
||||||
|
|
||||||
|
|
||||||
|
### The Problems
|
||||||
|
|
||||||
|
When I first started using ProtonMail, before I committed, I knew there were a lot of features missing, but I thought I wouldn't need these. Now my email traffic has increased, I'm starting to find myself needing / wanting these features more and more.
|
||||||
|
|
||||||
|
Now, 3 months on, I'm starting to get a little annoyed at ProtonMail, mainly at their lack of features. Yes the security is great, really great, but the fact I have to open a website and login every time I want to check my emails is a little tiring, and their mobile app, whilst fully functional and capable of the basics, has some bugs had glitches out sometimes.
|
||||||
|
|
||||||
|
Another problem is the lack of basic features. Whilst the client does have labels, and _'+ aliases'_, both of which are great features for those like me that like to keep things organised. The platform is missing out on some other key features, like:
|
||||||
|
|
||||||
|
- Custom message filter (currently in beta)
|
||||||
|
- IMAP / POP3 support, for external clients
|
||||||
|
- Custom PGP certificates
|
||||||
|
- Quick folder filters (see only unread...)
|
||||||
|
- Reading your emails offline (even through the mobile app)
|
||||||
|
|
||||||
|
|
||||||
|
These problems are taken for granted in most other mail platform, but because ProtonMail is built from the ground up, and is still a reasonably new product, it's missing a lot of these key features. I have no doubt that eventually, they will have all these features and more, but in the mean time, because I want these features now, I think it's time to look into another platform.
|
||||||
|
|
||||||
|
## _"Didn't you just waste your money?"_
|
||||||
|
|
||||||
|
Yes, this does mean I've paid for a service I'm technically no longer using. Which does annoy me, but considering it'll be used to help out the platform in the future, and I have every intention of switching back once it's got all the features I need, I can live with it.
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
After looking around the internet for a suitable replacement, I settled on [FastMail](https://fastmail.com/?STKI=14665249). I've seen good things from RackSpace, OX and RunBox, but FastMail has all the features I need, with an easy-to-use interface, a cheap price tag, and a pretty decent security policy. For someone looking for a simple to use email provider, that's reasonably secure, and feature-rich, I can't recommend [FastMail](https://fastmail.com/?STKI=14665249) highly enough!
|
17
content/blog/react-native-intro-dev-meeting.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
title: React-Native intro dev meeting
|
||||||
|
gittime: off
|
||||||
|
date: 2016-03-16
|
||||||
|
template: blog
|
||||||
|
summary: Introducing React-Native to the rest of the office
|
||||||
|
|
||||||
|
Recently, at DabApps, we've been migrating our mobile app workflow over to using [react-native](https://facebook.github.io/react-native/) instead of [Ionic](http://ionicframework.com/), mainly because of its near native performance and cross-platform codebase. For the first few projects, there were only a couple of us that knew how to use React Native effectively, and work around the _qwerks_ it has. With the number of app projects growing, we needed to get more people up to speed with the react native workflow, as quickly as possible.
|
||||||
|
|
||||||
|
The workflow that we needed to adopt to use react native is an odd one. To keep the quality of our code at the highest possible, whilst keeping the codebase as maintainable as possible. The workflow we use was created by 4 of us, through experiences with both work and personal projects using the framework, and it works rather well. The only problem was that only 4 of us actually knew it very well.
|
||||||
|
|
||||||
|
After one of the developers wanted to know our workflow, with a couple of potential app projects on the horizon, I set about creating a talk for our (_usually_) bi-weekly dev meetings. With the aim of trying to get everyone up to speed all in 1 go.
|
||||||
|
|
||||||
|
The slides from my talk are available on my [GitHub](https://github.com/RealOrangeOne/react-native-intro-dev-meeting) page, and whilst they are primarily relevant to our workflow, I hope they will be able to help anyone else looking to get started with React Native.
|
||||||
|
|
||||||
|
React Native is a tricky platform to get started on, but once you understand the _qwerks_, it truly is the future of cross-platform mobile development!
|
||||||
|
|
||||||
|
[Click here to view the slides](http://realorangeone.github.io/react-native-intro-dev-meeting/)
|
|
@ -1,4 +1,8 @@
|
||||||
# Student Server
|
title: Student Server
|
||||||
|
slug: student-server
|
||||||
|
summary: The college needed a server, but didn't have any server admins
|
||||||
|
|
||||||
|
|
||||||
Back when I was in college, we needed a server for computing students to learn how to use FTP, and script on a server using python CGI and [PHP](http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/#an-analogy), as well as possibly for some students coursework. Fortunately, the college already had one, running the IT students microsite for extra course information. The problem was that it was majorly out of date, and no one really new how to use it properly. It was up to me and my friend Alex to bring the server up to date, and get it ready for the students who needed it.
|
Back when I was in college, we needed a server for computing students to learn how to use FTP, and script on a server using python CGI and [PHP](http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/#an-analogy), as well as possibly for some students coursework. Fortunately, the college already had one, running the IT students microsite for extra course information. The problem was that it was majorly out of date, and no one really new how to use it properly. It was up to me and my friend Alex to bring the server up to date, and get it ready for the students who needed it.
|
||||||
|
|
||||||
The original plan was to update the server's OS (at that stage running Ubuntu 12.04 LTS), install python and [PHP](http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/#an-analogy) backends, add student users, and then make sure they couldn't edit each others documents. In the end, because we had no idea how the server worked, because it was setup a long time ago, we decided it was just easier to backup what we needed, then do a complete fresh install. Meaning we could set things up exactly how we wanted them, and install the tools we needed.
|
The original plan was to update the server's OS (at that stage running Ubuntu 12.04 LTS), install python and [PHP](http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/#an-analogy) backends, add student users, and then make sure they couldn't edit each others documents. In the end, because we had no idea how the server worked, because it was setup a long time ago, we decided it was just easier to backup what we needed, then do a complete fresh install. Meaning we could set things up exactly how we wanted them, and install the tools we needed.
|
16
content/college/wall-of-sheep.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
title: Wall of Sheep
|
||||||
|
slug: wall-of-sheep
|
||||||
|
summary: Hacking is fun!
|
||||||
|
|
||||||
|
|
||||||
|
Personally, I dislike being limited by technology. If there's a system in place like a proxy filter, network restrictions, or even any remote control software, I just have to try and break through it or work around it. This was mostly the case at college.
|
||||||
|
|
||||||
|
Throughout my time there, I was able to break through many parts of their network, mostly the sections involving keeping students on task. The hardest part of this was doing it in such a way that it couldn't be traced back to me, just in case they didn't appreciate me _testing_ their systems. Whilst I don't condone doing this, it is certainly a lot of fun, and I learnt a whole lot through it. To this day, they still have no idea I did any of it, unless they're reading this, in which case, __Hi__!
|
||||||
|
|
||||||
|
|
||||||
|
<!--- make this dynamic at some point! -->
|
||||||
|
|
||||||
|
## College Sheep
|
||||||
|
|
||||||
|
#### [LanSchool](/wall-of-sheep/LanSchool/)
|
||||||
|
The tool of choice of my college to monitor students usage of computers. Turns out it's not particularly robust!
|
17
content/pages/404.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<head>
|
||||||
|
<meta name="slug" content="404" />
|
||||||
|
<meta name="save_as" content=".404.html">
|
||||||
|
<meta name="title" content="Uh Oh - There's nothing here!" />
|
||||||
|
<meta name="html_title" content="404 - Page not found">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h3>The page you were looking for could not be found.</h3>
|
||||||
|
<h4>Don't worry, I've send these badass tech ninjas to go and find it. Rest assured it will be found!</h4>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
For now, here's a cat GIF!
|
||||||
|
</section>
|
||||||
|
</body>
|
58
content/pages/about.html
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
<head>
|
||||||
|
<meta name="slug" content="about" />
|
||||||
|
<meta name="title" content="About Me" />
|
||||||
|
<meta name="no_container" content="true" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="bg-primary">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8 col-lg-offset-2 text-center">
|
||||||
|
<h1 class="section-heading">About Me</h1>
|
||||||
|
<hr class="light">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="me">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p>
|
||||||
|
Hi, I'm Jake, a software engineer currently working for a company in Brighton. I've been working as a developer since mid-2015, and have been studying IT and computer science since 2013.
|
||||||
|
I work primarily as a full-stack developer, however more recently I've been working more on mobile apps.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4 text-center">
|
||||||
|
<div data-theme="medium" data-github="RealOrangeOne" class="github-card"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script async defer src="//cdn.jsdelivr.net/github-cards/latest/widget.js"></script>
|
||||||
|
</section>
|
||||||
|
<section class="bg-primary" id="website">
|
||||||
|
<div class="container">
|
||||||
|
<h2>About my website</h2>
|
||||||
|
<p>
|
||||||
|
My website is the culmination of all my knowledge, compiled into 1 place. It not only contains all my projects, but it in itself is a project. Making sure this website works properly is a tall order, especially considering it's self hosted.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The site is primarily powered by <a href="http://getpelican.com">Pelican</a>, a static site generator. This converts markdown and HTML templates into static HTML files. The templates are generated using <a href="http://jinja.pocoo.org/">Jinja2</a>, a templating language similar to <a href="https://www.djangoproject.com/">Django</a>'s. This allows me to write nice clean, <i>DRY</i> content, and have it come out as clean HTML.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The static content (JS, CSS etc) is built using a custom build pipeline. The Javascript is built and compiled using <a href="https://babeljs.io/">Babel</a> and <a href="http://browserify.org/">Browserify</a>, and the CSS is written using <a href="https://github.com/sass/node-sass">node-SCSS</a> as a pre-processor.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="server">
|
||||||
|
<div class="container">
|
||||||
|
<h2>The Server</h2>
|
||||||
|
<p>
|
||||||
|
The website is hosted on my <a href="">UK VPS</a>. Previous versions have been hosted on <a href="https://www.1and1.com/">1&1</a> and <a href="http://www.myWindowsHosting.com/index?r=100196823">MyWindowsHosting</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The static files are served using a <a href="https://github.com/RealOrangeOne/host-container">custom Express server</a>, which allows me to auto-deploy the site from my <a href="https://circleci.com/">CI</a>. This is then served using a custom, closed-source reverse proxy, which makes local development and domain switching much easier. This is pointed to by <a href="https://www.nginx.com/">NGINX</a>, which actually serves port 80.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
155
content/pages/homepage.html
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
<head>
|
||||||
|
<meta name="url" content="" />
|
||||||
|
<meta name="save_as" content="index.html" />
|
||||||
|
<meta name="title" content="Homepage" />
|
||||||
|
<meta name="template" content="page-home">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="bg-primary">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-content-inner">
|
||||||
|
<h1>Welcome to my website</h1>
|
||||||
|
<hr>
|
||||||
|
<p>The place where my knowledge lies, or at least most of it!</p>
|
||||||
|
<a href="#about" class="btn btn-primary btn-xl page-scroll">Find Out More</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="bg-primary" id="about">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8 col-lg-offset-2 text-center">
|
||||||
|
<h2 class="section-heading">About</h2>
|
||||||
|
<hr class="light">
|
||||||
|
<p>
|
||||||
|
Hi, I'm Jake. I've been working as a full-stack developer for the past year now, and studying computer-science for several years. This is my website, where all my projects, blog posts, and other stuff live. A 1-stop place to find all my content!
|
||||||
|
</p>
|
||||||
|
<a href="/about/" class="btn btn-primary-dark btn-xl">More Info</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="skills">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12 text-center">
|
||||||
|
<h2 class="section-heading">Skills</h2>
|
||||||
|
<hr class="primary">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-3 col-md-6 text-center">
|
||||||
|
<div class="service-box">
|
||||||
|
<i class="fa fa-4x fa-server wow bounceIn text-primary" data-wow-delay=".3s"></i>
|
||||||
|
<h3>Server</h3>
|
||||||
|
<p>Designing and building high-performance server applications and environments</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 text-center">
|
||||||
|
<div class="service-box">
|
||||||
|
<i class="fa fa-4x fa-internet-explorer wow bounceIn text-primary" data-wow-delay=".5s"></i>
|
||||||
|
<h3>Client</h3>
|
||||||
|
<p>Creating functional, fast, and mobile-friendly websites</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 text-center">
|
||||||
|
<div class="service-box">
|
||||||
|
<i class="fa fa-4x fa-mobile wow bounceIn text-primary" data-wow-delay=".7s"></i>
|
||||||
|
<h3>Mobile</h3>
|
||||||
|
<p>Building lightweight, cross-platform mobile applications</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-3 col-md-6 text-center">
|
||||||
|
<div class="service-box">
|
||||||
|
<i class="fa fa-4x fa-usb wow bounceIn text-primary" data-wow-delay=".9s"></i>
|
||||||
|
<h3>Hardware</h3>
|
||||||
|
<p>Constructing workflow-specific Desktops, Servers and Notebooks</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="projects">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row no-gutter">
|
||||||
|
{% for project in INDEX_PROJECTS %}
|
||||||
|
<div class="col-lg-4 col-sm-6">
|
||||||
|
<a href="{{ project.url }}" class="portfolio-box image" data-image="{{ project.image }}">
|
||||||
|
<div class="portfolio-box-caption">
|
||||||
|
<div class="portfolio-box-caption-content">
|
||||||
|
<div class="project-name">
|
||||||
|
<h2>
|
||||||
|
{{ project.name }}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 text-center">
|
||||||
|
<a href="/projects/" class="btn btn-primary btn-xl margin">
|
||||||
|
View all projects
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="bg-primary" id="blog">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-10 col-lg-offset-1 text-center">
|
||||||
|
<h2 class="section-heading">Blog</h2>
|
||||||
|
<hr class="light">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-10 col-sm-offset-1">
|
||||||
|
{% for article in categories|category_find("blog")|limit(4) %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12">
|
||||||
|
<p class="h3"><a href="{{ article.url }}">{{ article.title }}</a></p>
|
||||||
|
<p>{{ article.summary|striptags|e }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="light">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a href="/blog/" class="btn btn-primary-dark btn-xl">View all posts</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="more">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-8 col-lg-offset-2 text-center">
|
||||||
|
<h2 class="section-heading">More Content</h2>
|
||||||
|
<hr class="primary">
|
||||||
|
<p>There's more content on my site than just my Projects and Blog!</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-12 text-center">
|
||||||
|
<a class="btn btn-primary btn-xl margin" href="/college/">
|
||||||
|
College
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-xl margin" href="/setup/">
|
||||||
|
Setup
|
||||||
|
</a>
|
||||||
|
<a class="btn btn-primary btn-xl margin" href="/work/">
|
||||||
|
Work
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
|
@ -1,8 +1,9 @@
|
||||||
{% extends 'base.html' %}
|
<head>
|
||||||
{% load staticfiles %}
|
<meta name="slug" content="no-js" />
|
||||||
{% block htmltitle %}Javascript is Disabled!{% endblock %}
|
<meta name="title" content="Javascript is disabled" />
|
||||||
{% block bodyClass %}no-js{% endblock %}
|
<meta name="template" content="page-no-js">
|
||||||
{% block content %}
|
</head>
|
||||||
|
<body>
|
||||||
<div class="jumbotron header">
|
<div class="jumbotron header">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>You have Javascript is disabled!</h1>
|
<h1>You have Javascript is disabled!</h1>
|
||||||
|
@ -10,10 +11,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="container message">
|
<div class="container message">
|
||||||
<p>
|
<p>
|
||||||
You appear to have javascript disabled. For my site to function properly, javascript must be enabled! The javascript is used to dynamically change the webpage on your device. Without them then the site will break, cause a tonne of errors, and not look right, all of which aren't very nice, for you or me.
|
You appear to have javascript disabled. For my site to function properly, javascript must be enabled. To re-enable javascript, simply follow the instructions below.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The javascript on this page won't damage your computer in any way, and has been written entirely by me, or has used trusted and open-source 3rd-party libraries. You can trust this site! Re-enabling the javascript functions inside your browser is very easy, and helpful tutorials can be found below!
|
After enabling javascript, the page should automatically redirect to the homepage. If it doesn't, just refresh the page, or <a href="/">Click here</a> to return home.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -33,41 +34,32 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-success" id="enable-chrome">
|
<div class="panel panel-green" id="enable-chrome">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">Re-enabling in Chrome</h3>
|
<h3 class="panel-title">Re-enabling in Chrome</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<ol>
|
<ol>
|
||||||
<li>Click the Chrome menu icon (<i class="icon ion-navicon-round"></i>) in the top right hand corder of the window.</li>
|
<li>Click the Chrome menu icon (<i class="fa fa-bars"></i>) in the top right hand corder of the window.</li>
|
||||||
<li>Click 'Settings'.</li>
|
<li>Click 'Settings'.</li>
|
||||||
<li>Under the 'Privacy' section, select 'Content settings'.</li>
|
<li>Under the 'Privacy' section, select 'Content settings'.</li>
|
||||||
<li>Under 'Javascript', select 'Allow all sites to run Javascript(Recommended)'.</li>
|
<li>Under 'Javascript', select 'Allow all sites to run Javascript (Recommended)'.</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel panel-info" id="enable-ie">
|
<div class="panel panel-blue" id="enable-ie">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<h3 class="panel-title">Re-enabling in Internet Explorer</h3>
|
<h3 class="panel-title">Re-enabling in Internet Explorer</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<img src="{% static 'img/projects/IE-scare.png' %}" id="ie-scare"/>
|
|
||||||
<ol>
|
<ol>
|
||||||
<li>Select the gear icon in the top right hand corner <i class="icon ion-ios-gear"></i>, and click 'Internet Options'.</li>
|
<li>Select the gear icon in the top right hand corner (<i class="fa fa-cog"></i>), and click 'Internet Options'.</li>
|
||||||
<li>Select Security > Internet > Custom Level.</li>
|
<li>Select Security > Internet > Custom Level.</li>
|
||||||
<li>Scroll down until you find 'Scripting', and select 'Enable'</li>
|
<li>Scroll down until you find 'Scripting', and select 'Enable'</li>
|
||||||
</ol>
|
</ol>
|
||||||
<p>
|
|
||||||
<strong>Also: </strong> If you are using Internet Explorer (Or Microsoft Edge for that matter), <stong>Stop!</stong> Use one of the other ones. They're much faster, and better, especially <a href="//firefox.com">Firefox</a>!
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<script>window.location.href=location.href.replace('/no-js', '');</script>
|
||||||
<h5>
|
</body>
|
||||||
After enabling javascript, the page should automatically redirect to the homepage. If it doesn't, just refresh the page, or <a href="{% url 'pages:index'%}">Click here</a> to return home.
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<script>window.location.href=location.href.replace("{% url 'pages:no-js' %}", '');</script>
|
|
||||||
{% endblock%}
|
|
16
content/projects/astrill-extractor.md
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
title: Astrill Extractor
|
||||||
|
template: projects
|
||||||
|
slug: astrill-extractor
|
||||||
|
repo: https://gist.github.com/RealOrangeOne/050da86871fb952ba7bfe97eece8555c
|
||||||
|
|
||||||
|
Astrill, my VPN of choice, allows you to export OpenVPN config files for all it's VPNs, allowing you to connect on platforms it doesn't provide clients for. The AUR package `astrill` has started becoming really unstable on my machine, so I decided to switch it out for these VPN files, as gnome has excellent support for OpenVPN.
|
||||||
|
|
||||||
|
The export step is really simple, you just login to the web portal, create an entry for your machine, and export the config files. Their tutorial for this can be found [here](http://wiki.astrill.com/index.php/Astrill_Setup_Manual:How_to_configure_OpenVPN_with_Network_Manager_on_Linux). The problem with this is that some applications wont accept the certificates embedded into the file like this. (Gnome does, but I only realised that whilst writing this). So I started writing a simple parser to extract the certificates from the config files, and saving them as separate files.
|
||||||
|
|
||||||
|
|
||||||
|
<script src="https://gist.github.com/RealOrangeOne/050da86871fb952ba7bfe97eece8555c.js"></script>
|
||||||
|
|
||||||
|
The above script will split out the files and save them into separate directories for each config file. These files can then be imported and used in an openvpn-compatable application.
|
||||||
|
|
||||||
|
### Is it even needed?
|
||||||
|
Certain network-manager packages actually supports importing `.ovpn` files directly, and sets everything up for you, including the files for the keys etc, without needing to extract them before. This does make my script useless to me, but hopefully someone will find it useful!
|
|
@ -1,4 +1,8 @@
|
||||||
# Attack on Blocks Game!
|
title: Attack on blocks game
|
||||||
|
template: projects
|
||||||
|
slug: attack-on-blocks
|
||||||
|
repo: https://github.com/RealOrangeOne/attack-on-blocks/
|
||||||
|
download_link: https://github.com/RealOrangeOne/attack-on-blocks/archive/master.zip
|
||||||
|
|
||||||
Attack on Blocks is a space invaders style game that I wrote for my IT coursework, for the games development unit. We were allowed to make any game that we wanted, provided it could be done within the time limits, be very easy to play, and easily run on the college computer (which were pretty terrible). I had never written a game before, so I knew this was going to be a challenge.
|
Attack on Blocks is a space invaders style game that I wrote for my IT coursework, for the games development unit. We were allowed to make any game that we wanted, provided it could be done within the time limits, be very easy to play, and easily run on the college computer (which were pretty terrible). I had never written a game before, so I knew this was going to be a challenge.
|
||||||
|
|
||||||
|
@ -8,11 +12,3 @@ I decided to write the game in Python, seeing as there were other people in the
|
||||||
One of the key features of this take on space invaders (and unfortunately the thing I spent the most time on), is the easter eggs. There are a few dotted around the game, which make the game either much easier, or way more fun! At the moment, there are 3 main easter eggs, the first enabling the other 2. If you would like to know what they are, click the button below. If not, pay the game and try and find them, or search through the source to find them (it's not too hard through the source).
|
One of the key features of this take on space invaders (and unfortunately the thing I spent the most time on), is the easter eggs. There are a few dotted around the game, which make the game either much easier, or way more fun! At the moment, there are 3 main easter eggs, the first enabling the other 2. If you would like to know what they are, click the button below. If not, pay the game and try and find them, or search through the source to find them (it's not too hard through the source).
|
||||||
|
|
||||||
As you will see (If and when you find the easter eggs), most of them are completely useless, and completely unrelated to the game or anything else. The main reason they were put in was because I'm friends with people that pester to the point it's just easier to give in. Hence there are some really very odd ones!
|
As you will see (If and when you find the easter eggs), most of them are completely useless, and completely unrelated to the game or anything else. The main reason they were put in was because I'm friends with people that pester to the point it's just easier to give in. Hence there are some really very odd ones!
|
||||||
|
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified">
|
|
||||||
<p class="center-text">
|
|
||||||
<a class="btn btn-primary btn-lg" href="https://github.com/RealOrangeOne/attack-on-blocks/archive/master.zip" download="Attack-on-blocks.zip">Download Attack on Blocks</a>
|
|
||||||
<a class="btn btn-default btn-lg" href="https://github.com/RealOrangeOne/attack-on-blocks">View on Github <i class="icon ion-social-github"></i></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
|
@ -1,4 +1,7 @@
|
||||||
# BSOD Enabler
|
title: BSOD Enabler
|
||||||
|
template: projects
|
||||||
|
slug: bsod-enabler
|
||||||
|
summary: Call a _Blue Screen of Death_ on demand! It's more fun than it sounds
|
||||||
|
|
||||||
For those that use Windows, the famous [Blue Screen of Death](https://en.wikipedia.org/wiki/Blue_Screen_of_Death) is an annoyance that plagues computers, causing error, frustration, and even data loss. They happened to me a lot whilst I was trying to configure my computer, and I thought _I wonder who else I can annoy with a BSOD_
|
For those that use Windows, the famous [Blue Screen of Death](https://en.wikipedia.org/wiki/Blue_Screen_of_Death) is an annoyance that plagues computers, causing error, frustration, and even data loss. They happened to me a lot whilst I was trying to configure my computer, and I thought _I wonder who else I can annoy with a BSOD_
|
||||||
|
|
||||||
|
@ -7,13 +10,3 @@ __And thus the BSOD_Enabler was born!__
|
||||||
After researching into it for a while, it turns out that there are a few different ways to cause a BSOD, unfortunately most of which are by doing things that are meant to cause a BSOD, and can therefore be dangerous to a computer, something I didn't really want. Then I stumbled upon [this article](http://www.wikihow.com/Force-a-Blue-Screen-in-Windows), which shows that you can in fact raise a BSOD without causing any errors or damage to your computer. Now to write a program that can do it too!
|
After researching into it for a while, it turns out that there are a few different ways to cause a BSOD, unfortunately most of which are by doing things that are meant to cause a BSOD, and can therefore be dangerous to a computer, something I didn't really want. Then I stumbled upon [this article](http://www.wikihow.com/Force-a-Blue-Screen-in-Windows), which shows that you can in fact raise a BSOD without causing any errors or damage to your computer. Now to write a program that can do it too!
|
||||||
|
|
||||||
Obviously there are many different ways, and probably far better ways of doing this, but I wanted something that was simple to use, fast, and could be done by anyone, no matter how technically illiterate. So I decided to write it in C#, and use a windows console interface.
|
Obviously there are many different ways, and probably far better ways of doing this, but I wanted something that was simple to use, fast, and could be done by anyone, no matter how technically illiterate. So I decided to write it in C#, and use a windows console interface.
|
||||||
|
|
||||||
Below you can find a download link to the application, as well as a link to the source code. I am hoping to upgrade the project in the future to allow for automated triggering, a much faster UI, with options, as well as a simple one-click setup and BSOD.
|
|
||||||
|
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified">
|
|
||||||
<p class="center-text">
|
|
||||||
<a class="btn btn-primary btn-lg" href="" download="BSOD_Enabler.exe">Download BSOD Enabler Here</a>
|
|
||||||
<a class="btn btn-default btn-lg" href="javascript:alert('Coming in next version.');">View on Github <i class="icon ion-social-github"></i></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
|
@ -1,4 +1,8 @@
|
||||||
# My Dotfiles
|
title: My Dotfiles
|
||||||
|
template: projects
|
||||||
|
slug: dotfiles
|
||||||
|
repo: https://github.com/RealOrangeOne/dotfiles/
|
||||||
|
summary: How I set up my machines just the way I like them!
|
||||||
|
|
||||||
### What are dotfiles?
|
### What are dotfiles?
|
||||||
Dotfiles are a way for people to store their settings and preferences to make setting up a new computer that much easier. They can usually be found in a persons VCS profile.
|
Dotfiles are a way for people to store their settings and preferences to make setting up a new computer that much easier. They can usually be found in a persons VCS profile.
|
||||||
|
@ -9,6 +13,6 @@ I use both my laptop and work machines almost every day, and want them to be set
|
||||||
## How I did it
|
## How I did it
|
||||||
Originally, I used my owncloud server to sync all my dotfiles between my computer, and then used symlinks to split out the relevant files into the relevant locations.
|
Originally, I used my owncloud server to sync all my dotfiles between my computer, and then used symlinks to split out the relevant files into the relevant locations.
|
||||||
|
|
||||||
This worked brilliantly, config files were automatically synced as soon as I made a change. This was especially great for my `.bashrc` file! The main problem was with atom packages, I had to manually store what files were installed, then manually install them on the other machine from the saved file. This was made easier by `apm` allowing me to list them and automatically save them in a file, but it wasnt perfect.
|
This worked brilliantly, config files were automatically synced as soon as I made a change. This was especially great for my `.bashrc` file! The main problem was with atom packages, I had to manually store what files were installed, then manually install them on the other machine from the saved file. This was made easier by `apm` allowing me to list them and automatically save them in a file, but it wasn't perfect.
|
||||||
|
|
||||||
Eventually, after looking into possible solutions, I came across the [`Sync settings`](https://atom.io/packages/sync-settings) package, which seemed to be the answer to my prayers! It saved all my config data for atom into a gist, which I could then backup and restore too from within the application. It also warned me when my local data was out of date from the remote, and prompt me to downnload the updated data.
|
Eventually, after looking into possible solutions, I came across the [`Sync settings`](https://atom.io/packages/sync-settings) package, which seemed to be the answer to my prayers! It saved all my config data for atom into a gist, which I could then backup and restore too from within the application. It also warned me when my local data was out of date from the remote, and prompt me to download the updated data.
|
|
@ -1,17 +1,14 @@
|
||||||
# Hipchat Emoticons for All
|
title: Hipchat emoticons for all
|
||||||
|
template: projects
|
||||||
|
slug: hipchat-emoticons-for-all
|
||||||
|
repo: https://github.com/RealOrangeOne/hipchat-emoticons-for-all/
|
||||||
|
|
||||||
After starting my new job at DabApps, I was introduced to the world of [Hipchat](https://hipchat.com), and it's wonderful array of emoticons, as well as the ones added. It was wonderful, it made communicating with friends and colleagues much more interesting!
|
After starting my new job at DabApps, I was introduced to the world of [Hipchat](https://hipchat.com), and it's wonderful array of emoticons, as well as the ones added. It was wonderful, it made communicating with friends and colleagues much more interesting!
|
||||||
|
|
||||||
Unfortunately, the emoticons on the other services we use, like [GitHub](https://github.com), were terrible in comparison. So it was after a discussion with [@JakeSidSmith](https://github.com/jakesidsmith) about him just using things like (facepalm), (notsureif), and (wat) in [Facebook messenger](https://www.messenger.com/) and hoping people understand what it means, that I decided to make 'Hipchat Emoticons for all', so people like him could use a much better set of emoticons.
|
Unfortunately, the emoticons on the other services we use, like [GitHub](https://github.com), were terrible in comparison. So it was after a discussion with [@JakeSidSmith](https://github.com/jakesidsmith) about him just using things like (facepalm), (notsureif), and (wat) in [Facebook messenger](https://www.messenger.com/) and hoping people understand what it means, that I decided to make 'Hipchat Emoticons for all', so people like him could use a much better set of emoticons.
|
||||||
|
|
||||||
The premis is very simple, whenever it sees a hipchat emoticon code, like (notsureif), it replaces it with an emoticon. If only writing the code could have been this simple! I started writing the plugin in firefox, using [Jetpack](https://wiki.mozilla.org/Jetpack), which uses Javascript. The initial stages of the code were very simple, but I encountered problems making sure that anything loaded after the page was loaded (such as a facebook message), be changed too.
|
The premiss is very simple, whenever it sees a hipchat emoticon code, like (notsureif), it replaces it with an emoticon. If only writing the code could have been this simple! I started writing the plugin in firefox, using [Jetpack](https://wiki.mozilla.org/Jetpack), which uses Javascript. The initial stages of the code were very simple, but I encountered problems making sure that anything loaded after the page was loaded (such as a Facebook message), be changed too.
|
||||||
|
|
||||||
Fortunately after many hours of testing, and changing the code, I finally got everything working perfectly, and in a way that made adding new sites incredibly easy! The code isn't the greatest in terms of performance, and there are some things that could have obviously been done better, but this was all done to help with a shared codebase between Chrome and Firefox, which don't play nice when it comes to extensions.
|
Fortunately after many hours of testing, and changing the code, I finally got everything working perfectly, and in a way that made adding new sites incredibly easy! The code isn't the greatest in terms of performance, and there are some things that could have obviously been done better, but this was all done to help with a shared codebase between Chrome and Firefox, which don't play nice when it comes to extensions.
|
||||||
|
|
||||||
Currently the application is in very beta stages right now, only having tested partial support for github, but the code is all available on GitHub, if people have their own suggestions of improvements.
|
Currently the application is in very beta stages right now, only having tested partial support for GitHub, but the code is all available on GitHub, if people have their own suggestions of improvements.
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified">
|
|
||||||
<p class="center-text">
|
|
||||||
<a class="btn btn-default btn-lg" href="https://github.com/RealOrangeOne/hipchat-emoticons-for-all">View on Github <i class="icon ion-social-github"></i></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
5
content/projects/jsfuckify.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
title: JSFuckify
|
||||||
|
template: projects
|
||||||
|
slug: jsfuckify
|
||||||
|
repo: https://github.com/RealOrangeOne/JSFuckify
|
||||||
|
status: draft
|
|
@ -1,15 +1,18 @@
|
||||||
# Morse Code Decoder
|
title: Morse code decoder
|
||||||
|
template: projects
|
||||||
|
slug: morese-code-decoder
|
||||||
|
repo: https://gist.github.com/RealOrangeOne/6dc94875c93b787e5834
|
||||||
|
summary: A JSON file to help decode morse-code
|
||||||
|
|
||||||
It's not often someone will need to decode text into morse code (and visa-versa), but if I had something like this when I needed it, it would have saved me hours of time!
|
It's not often someone will need to decode text into morse code (and visa-versa), but if I had something like this when I needed it, it would have saved me hours of time!
|
||||||
|
|
||||||
I originally wrote this code for the [Student Robotics 2015](/robotics/2015/) Entry, to convert a string message into a morse code message that would be transmitted using LEDs, for aesthetics and debugging. Unfortunately due to a fixed time frame, this idea was scraped before it could be fully implemented. Fortunately the decoder worked perfectly!
|
I originally wrote this code for the [Student Robotics 2015](/robotics/2015/) Entry, to convert a string message into a morse code message that would be transmitted using LEDs, for aesthetics and debugging. Unfortunately due to a fixed time frame, this idea was scraped before it could be fully implemented. Fortunately the decoder worked perfectly!
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
In order to make it accessible for as many people in as many different languages as possible, the data is in JSON format. Just find a JSON library for your desired language, and it'll work perfectly!
|
In order to make it accessible for as many people in as many different languages as possible, the data is in JSON format. Just find a JSON library for your desired language, and it'll work perfectly!
|
||||||
|
|
||||||
The source of the library is on github as a gist. I recommend downloading the file to use yourself, however for testing you can use github's raw view as a hotlink.
|
The source of the library is on GitHub as a gist. I recommend downloading the file to use yourself, however for testing you can use GitHub's raw view as a hotlink.
|
||||||
|
|
||||||
|
|
||||||
<script src="https://gist.github.com/RealOrangeOne/6dc94875c93b787e5834.js"></script>
|
<script src="https://gist.github.com/RealOrangeOne/6dc94875c93b787e5834.js"></script>
|
5
content/projects/react-native-busy-indicator.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
title: React native busy indicator
|
||||||
|
template: projects
|
||||||
|
slug: react-native-busy-indicator
|
||||||
|
repo: https://github.com/RealOrangeOne/react-native-busy-indicator
|
||||||
|
status: draft
|
5
content/projects/react-native-mock.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
title: React native mock
|
||||||
|
template: projects
|
||||||
|
slug: react-native-mock
|
||||||
|
repo: https://github.com/lelandrichardson/react-native-mock
|
||||||
|
status: draft
|
|
@ -1,4 +1,8 @@
|
||||||
# Wiki Game Solver
|
title: Wiki Game solver
|
||||||
|
template: projects
|
||||||
|
slug: wiki-game-solver
|
||||||
|
repo: https://gist.github.com/RealOrangeOne/7da9a3dd1bf90ecdf7be
|
||||||
|
summary: Simple script to win the Wiki Game
|
||||||
|
|
||||||
For those who don't know what the Wiki Game is: [The Wiki Game](http://thewikigame.com) is an online game where you attempt to navigate through wikipedia from a start page to a goal page using as few other pages as possible. Once I was shown the Wiki Game by my friend, and after I realised that I really wasn't very good at it, I looked into how the system worked, and how I could beat it.
|
For those who don't know what the Wiki Game is: [The Wiki Game](http://thewikigame.com) is an online game where you attempt to navigate through wikipedia from a start page to a goal page using as few other pages as possible. Once I was shown the Wiki Game by my friend, and after I realised that I really wasn't very good at it, I looked into how the system worked, and how I could beat it.
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
# Yoga Pal
|
title: Yoga Pal
|
||||||
|
template: projects
|
||||||
|
slug: yoga-pal
|
||||||
|
repo: https://github.com/RealOrangeOne/yoga-pal
|
||||||
|
summary: Control screen rotation, touch screen, and trackpad using the terminal
|
||||||
|
|
||||||
Once I started work, I bought myself a _Lenovo Yoga 3 14"_ laptop, because I needed a thin and light for trains etc. Unfortunately this came with windows, which within 10 minutes was running Ubuntu Gnome! Ubuntu greatly increased the performance, but I had to sacrifice all the screen, touchpad and keyboard customisation when changing 'modes'.
|
Once I started work, I bought myself a _Lenovo Yoga 3 14"_ laptop, because I needed a thin and light for trains etc. Unfortunately this came with windows, which within 10 minutes was running Ubuntu Gnome! Ubuntu greatly increased the performance, but I had to sacrifice all the screen, touchpad and keyboard customisation when changing 'modes'.
|
||||||
|
|
||||||
I found [this thread](askubuntu.com/questions/450066/rotate-touchscreen-and-disable-the-touchpad-on-yoga-2-pro-in-rotated-mode) on _Ask Ubuntu_ with someone else trying to find a solution to this, to find a nice way of rotating the screen when in tablet mode. On the thread was a really nice simple [script](http://askubuntu.com/a/485685/432138) that rotated the screen perfectly, and did the touchscreen too. This script worked great, doing exactly what it said it did, nicely and quickly, however it wasn't a great solution for me. Yes it worked, but it didn't allow me to change anything else, like the touchpad.
|
I found [this thread](askubuntu.com/questions/450066/rotate-touchscreen-and-disable-the-touchpad-on-yoga-2-pro-in-rotated-mode) on _Ask Ubuntu_ with someone else trying to find a solution to this, to find a nice way of rotating the screen when in tablet mode. On the thread was a really nice simple [script](http://askubuntu.com/a/485685/432138) that rotated the screen perfectly, and did the touchscreen too. This script worked great, doing exactly what it said it did, nicely and quickly, however it wasn't a great solution for me. Yes it worked, but it didn't allow me to change anything else, like the touchpad.
|
||||||
|
|
||||||
So I started working on my own CLI, based off the above script, to allow me to tweak everything, so the laptop can be used as it was intended.
|
So I started working on my own CLI, based off the above script, to allow me to tweak everything, so the laptop can be used as it was intended.
|
||||||
|
|
||||||
<div class="btn-group btn-group-justified">
|
|
||||||
<p class="center-text">
|
|
||||||
<a class="btn btn-default btn-lg" href="https://github.com/RealOrangeOne/yoga-pal">View on Github <i class="icon ion-social-github"></i></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
37
content/robotics/2014-index.html
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<head>
|
||||||
|
<meta name="title" content="Student Robotics 2014">
|
||||||
|
<meta name="slug" content="2014">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<img class="img-rounded" src="https://c2.staticflickr.com/8/7670/17115168179_1ef30ac6e9_b.jpg" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p>
|
||||||
|
Welcome to the homepage of Collyer's Student Robotics 2014 team. Originally, this page was a part of the competition, but due to 3 different site rewrites, the original content and formatting has been lost.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<h2>The Competition</h2>
|
||||||
|
<p>
|
||||||
|
The game for this year was called <em>Slots</em>. Teams compeated to get as many of their tokens into a scoring zone in 3 minutes. Teams would also get extra points if they could get the token into a zones 'slot', an area the size of a token, raised by around 3 cm.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To see a copy of the rules from the competition, <a href="https://www.studentrobotics.org/resources/2014/rulebook.pdf">Click Here!</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="list-group">
|
||||||
|
<a class="list-group-item active h4">Quick Links</a>
|
||||||
|
<a href="https://github.com/SR-CLY/2014" class="list-group-item">The Code</a>
|
||||||
|
<a href="https://www.flickr.com/photos/theorangeone/albums/72157651820386449" class="list-group-item">Gallery</a>
|
||||||
|
<a href="http://collyers-robotics14.tumblr.com/" class="list-group-item">Blog</a>
|
||||||
|
<a href="https://www.studentrobotics.org/schools/game#2014" class="list-group-item">Competition Information</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
|
@ -1,4 +1,6 @@
|
||||||
# The Code | SR2015
|
title: The Code
|
||||||
|
html_title: The Code | SR2015
|
||||||
|
slug: 2015/code
|
||||||
|
|
||||||
The code used for this competition was by far the most complicated and advanced code that had ever been written by a Collyer's team.
|
The code used for this competition was by far the most complicated and advanced code that had ever been written by a Collyer's team.
|
||||||
|
|
||||||
|
@ -18,4 +20,4 @@ There were a lot of really great features in this years code, that made our robo
|
||||||
### So, where is this code?
|
### So, where is this code?
|
||||||
Unfortunately, due to the number of features, we have decided to keep the source private, so that only other Collyer's teams may access it. A lot of blood, sweat and tears (not literally) went into writing this code, and it would be a shame if that were to be used to help any of our opponents.
|
Unfortunately, due to the number of features, we have decided to keep the source private, so that only other Collyer's teams may access it. A lot of blood, sweat and tears (not literally) went into writing this code, and it would be a shame if that were to be used to help any of our opponents.
|
||||||
|
|
||||||
You can however see the code from our other years on our [GitHub organisation](https://github.com/SR-CLY).
|
You can however see the code from some of our other years on our [GitHub organisation](https://github.com/SR-CLY).
|
42
content/robotics/2015-index.html
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<head>
|
||||||
|
<meta name="title" content="Student Robotics 2015">
|
||||||
|
<meta name="slug" content="2015">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<img class="img-rounded" src="https://c2.staticflickr.com/8/7726/17309695331_584e7de16c_z.jpg" />
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p>
|
||||||
|
Welcome to the homepage of Collyer's Student Robotics Team 2015 (The 'A' Team) - Creators of 'A.L.I.C.E'! Here you can see everything that goes on throughout the competition.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<h2>The Competition</h2>
|
||||||
|
<p>
|
||||||
|
As was announced at kickstart, the game for this year is a take on the classic gamemode Capture the Flag. 4 teams compete over 5 flags to move as many of them as they can into their scoring zones. The person with the most flags in their scoring zone wins.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The 'flags' are 25cm cubes of wood on caster wheels weighing roughly 2kg. The rules prevent us from lifting them, so the idea is to drag them around!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
To see a copy of the rules from the competition, <a href="https://www.studentrobotics.org/resources/2015/rulebook.pdf">Click Here!</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<div class="list-group">
|
||||||
|
<a class="list-group-item active h4">Quick Links</a>
|
||||||
|
<a href="/robotics/2015/robot/" class="list-group-item">The Robot - A.L.I.C.E</a>
|
||||||
|
<a href="/robotics/2015/code/" class="list-group-item">The Code</a>
|
||||||
|
<a href="https://flic.kr/s/aHska26DoH" class="list-group-item">Gallery</a>
|
||||||
|
<a href="http://collyersstudentrobotics.blogspot.co.uk/" class="list-group-item">Blog</a>
|
||||||
|
<a href="https://www.studentrobotics.org/schools/game#2015" class="list-group-item">Competition Information</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-panel" data-image="https://c2.staticflickr.com/8/7674/17308375182_a172a341d6_h.jpg"></div>
|
||||||
|
</body>
|
44
content/robotics/2015-robot.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
title: The Robot - A.L.I.C.E
|
||||||
|
html_title: The Robot | SR2015
|
||||||
|
slug: 2015/robot
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<!-- Image of initial design (on paper) -->
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<p>
|
||||||
|
Our entry for 2015, named 'A.L.I.C.E', was a massive improvement over last years model, in both design, and the code for it. Before ALICE was built, the design team built us a very basic chassis usind scrap parts from 2014, which allowed us to write a large amount of the code base before we even had the robot built. Originally I wanted the final chassis to be built before the end of January, so we had a lot of time to test out the design for the robot and test using the final, in reality, it was closer to the middle of march before this was a reality.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
The entire chassis was made from sheets of plywood, which we laser cut in college, allowing us to be very precise in the design of the robot to make sure that all the pieces would fit together properly, making the chassis less likely to break.
|
||||||
|
|
||||||
|
|
||||||
|
The initial design was conceived by Ben, at kickstart, and was then refined over the coming weeks by me and the rest of the building team. This was made much easier from the use of the 3D model, that Ben made, which helped us visualize any changes that were to be made, as well as work out strategy by seeing the measurements we had to work with.
|
||||||
|
|
||||||
|
|
||||||
|
Once the build was completed, it was to a much higher design and quality than I could have ever imagined! It allowed us to forget about any shortcomings when it came to chassis, not having to compensate for weight distribution, or worry about the grip on the wheels.
|
||||||
|
|
||||||
|
<div class="image-panel" data-image="https://farm1.staticflickr.com/737/20984044320_ccbba155f9_o.gif"></div>
|
||||||
|
|
||||||
|
## Why call it _A.L.I.C.E_?
|
||||||
|
The decision to name the robot 'Alice' was a decision made by the whole group!
|
||||||
|
|
||||||
|
|
||||||
|
That's a lie, Ben suggested it and as no one had any better ideas and because he gets overruling vote on this, for some reason , it stuck.
|
||||||
|
|
||||||
|
|
||||||
|
After this decisions was made, I decided to set out to find the cheesiest acronym we could for 'Alice', to make the name slightly more interesting, and not some random girls name pulled from thin air on the bus ride home from kickstart. There were a few rather good ideas, most of them coming from Sam:
|
||||||
|
|
||||||
|
- 'Automated Laser-cut Interactive Capturing Entity'</li>
|
||||||
|
- 'Abnormally Lame and Inaccurate Control-less Engine'</li>
|
||||||
|
- 'Anti-Losing Immaculate Competitive Extravaganza'</li>
|
||||||
|
|
||||||
|
One idea was also suggested that we name the robot after the first sponsor we got, but as we didn't get one until after the team split, the other team took that name instead. In the end we decided to go with one that Sam initially suggested, so 'ALICE' officially stands for:
|
||||||
|
|
||||||
|
<h3 class="text-center">'Autonomous Logistics and Inevitable Collision Engine'</h3>
|
||||||
|
|
||||||
|
<div class="image-panel" data-image="https://c2.staticflickr.com/8/7656/16687742984_ee1c76d1d9_h.jpg"></div>
|
41
content/wall-of-sheep/LanSchool.md
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
title: LanSchool
|
||||||
|
slug: LanSchool
|
||||||
|
summary: The tool of choice of my college to monitor students usage of computers. Turns out it's not particularly robust!
|
||||||
|
|
||||||
|
LanSchool is the software of choice for my college to monitor and control computer usage. It allows teachers to see students screens, interact with them, and even block certain functionality like the internet and removable storage. My secondary school had a system much like this, but they weren't as active in using it.
|
||||||
|
|
||||||
|
By far the worst feature of LanSchool was the screen blocking. At will, a teacher could show an overlay on your screen, usually consisting of test saying _"Eyes front"_ etc. The problem with this pop up was there was no way to hide it, it just appeared suddenly and forced you to stop using your computer. This tool had to be stopped!
|
||||||
|
|
||||||
|
Within a few weeks of realising this tool, A friend of mine discovered how to get around it in a really primitive way: simply unplugging the ethernet. It's rather incredible this worked, but it had some problems:
|
||||||
|
|
||||||
|
- It took around 10 seconds after disconnect before the screen was restored, not a major problem, but still annoying.
|
||||||
|
- With all documents and programs bring stored on the network, you couldn't access any programs and documents you didn't already have open.
|
||||||
|
- When you reconnect the cable, the screen returns to the state of everyone else.
|
||||||
|
- Your computer would suddenly disappear from the list of machines on the teachers screen. If they were observant, they'd notice!
|
||||||
|
|
||||||
|
This solution worked, but wasn't ideal. Another solution was to log out (using the `ctrl + alt + delete` shortcut, which worked for some reason), and log in again. Our network was slow, so sometimes it wasn't worth the wait if your screens were only disabled for a short period of time. However the main problem with this was the fact it didn't always work, only around 30% of the time.
|
||||||
|
|
||||||
|
#### The best fix
|
||||||
|
|
||||||
|
The original idea for this came from someone else, but the implementation and refinement was mine, so I like to think it was mostly me.
|
||||||
|
|
||||||
|
Using an ubuntu live CD loaded onto a USB drive, we booted to ubuntu, and renamed the LanSchool executable. This meant the program wouldn't be able to run on start up, and so the client couldn't communicate with the teacher to lock our computers. __Result!__
|
||||||
|
|
||||||
|
This method worked almost perfectly, however had a few problems:
|
||||||
|
- It took at least 10 minutes to go from completely enabled, to completely disabled
|
||||||
|
- It worked for every user account on that computer, so it had to be done on each computer I used.
|
||||||
|
- A teacher would notice, as you would never show up on their list.
|
||||||
|
|
||||||
|
Fortunately this last point is a non-issue, as usually the teachers put it down to the software messing up, not a student breaking the install. Another key problem with this is that it's rather obvious when everyones computer other than yours is disabled, as you're still actively using yours, whilst everyone else is actually listening!
|
||||||
|
|
||||||
|
#### The silver lining
|
||||||
|
|
||||||
|
Disabling the client on a machine also allowed for another feature that I had never thought of, but was by far the greatest bi-product of disabling LanSchool: __Teacher Mode!__
|
||||||
|
|
||||||
|
Due to me being able to have access to a teachers computer one evening, I was able to copy the executables for the teachers console onto a USB drive, and then run them later on my computer. Obviously I know most software won't work in this way, but I'm so glad this one did!
|
||||||
|
|
||||||
|
Now, I had access to everything the teacher did, which made lessons much more exciting. I could block peoples screens, send them messages, or even take complete control of their computer, it was great! Eventually a few more in my class knew [I had the power](), and I became a tool for trolling people, which was made extra simple by the fact I had access to all student computers in the college, not just my class. The only downside to this (something I didn't realise until I tried to prank a friend), is that it comes up with your name on the client computer if you try and take control of one that's not in your class, an annoying and dangerous feature.
|
||||||
|
|
||||||
|
#### Phase 2
|
||||||
|
|
||||||
|
Whilst writing this article, over 2 years after all this, I realised I could improve it considerable. Writing some kind of wrapper program, to detect my username, and run LanSchool for users other than me, would be harder to work out there was a problem with the computer, as well as prevent it disabling LanSchool for every user.
|
|
@ -1,6 +0,0 @@
|
||||||
links:
|
|
||||||
github: https://github.com/RealOrangeOne
|
|
||||||
twitter: https://twitter.com/RealOrangeOne
|
|
||||||
instagram: https://instagram.com/RealOrangeOne
|
|
||||||
youtube: https://www.youtube.com/user/TheOrangeOneOfficial
|
|
||||||
reddit: https://www.reddit.com/user/realorangeone
|
|
|
@ -1,9 +0,0 @@
|
||||||
index:
|
|
||||||
body_class: index
|
|
||||||
html_title: Homepage
|
|
||||||
|
|
||||||
projects/hipchat-emoticons-for-all:
|
|
||||||
header_image: https://hipchat-magnolia-cdn.atlassian.com/assets/img/hipchat/hipchat_og_image.jpg
|
|
||||||
|
|
||||||
projects/yoga-pal:
|
|
||||||
header_image: http://www.lenovo.com/images/OneWebImages/SubSeries/gallery/laptops/IdeaPad-Yoga-13-Convertible-Laptop-PC-Clementine-Orange-Closed-Cover-View-gallery-940x529.jpg
|
|
|
@ -1 +0,0 @@
|
||||||
college/attack-on-blocks: projects/attack-on-blocks
|
|
|
@ -1,3 +0,0 @@
|
||||||
DEBUG=false
|
|
||||||
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
|
||||||
BUILD_PRODUCTION=true
|
|
|
@ -1 +0,0 @@
|
||||||
web: waitress-serve --port $PORT project.wsgi:application
|
|
|
@ -1,2 +0,0 @@
|
||||||
DEBUG=true
|
|
||||||
EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
|
|
|
@ -1,2 +1 @@
|
||||||
web: manage.py runserver 0.0.0.0:$PORT
|
web: bash scripts/server.sh
|
||||||
watcher: npm run watch
|
|
||||||
|
|
10
manage.py
|
@ -1,10 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
|
|
||||||
|
|
||||||
from django.core.management import execute_from_command_line
|
|
||||||
|
|
||||||
execute_from_command_line(sys.argv)
|
|
52
package.json
|
@ -1,50 +1,38 @@
|
||||||
{
|
{
|
||||||
"name": "TheOrangeOne-Site",
|
"name": "TheOrangeOne-Site",
|
||||||
"version": "3.0.0",
|
"version": "4",
|
||||||
"description": " Source code for TheOrangeOne.net",
|
"description": " Source code for TheOrangeOne.net",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint 'static/src/js/'",
|
|
||||||
"build-js": "./scripts/build-js.sh",
|
"build-js": "./scripts/build-js.sh",
|
||||||
"create-build-dirs": "mkdir -p static/build/js/lib static/build/fonts static/build/css static/build/img",
|
"build-scss": "./scripts/build-scss.sh"
|
||||||
"build": "npm run create-build-dirs && npm run build-fonts && npm run build-images && npm run build-js && npm run build-less",
|
|
||||||
"build-less": "./scripts/build-less.sh",
|
|
||||||
"build-fonts": "cp -R node_modules/bootstrap/dist/fonts static/build/ && cp -R node_modules/ionicons/fonts static/build/ ",
|
|
||||||
"build-images": "cp -r static/src/img/* static/build/img/",
|
|
||||||
"watch-less": "watch 'npm run build-less' static/src/less/",
|
|
||||||
"watch": "npm run watch-less",
|
|
||||||
"clean": "./scripts/clean.js",
|
|
||||||
"test": "npm run lint",
|
|
||||||
"spellcheck": "mdspell --en-gb -ranx \"templates/**/*.*\""
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/RealOrangeOne/theorangeone.net"
|
"url": "https://github.com/RealOrangeOne/theorangeone.net"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"animate.css": "=3.4.0",
|
"animate.css": "=3.5.1",
|
||||||
"bootstrap": "=3.3.5",
|
"bootstrap-sass": "=3.3.7",
|
||||||
"ionicons": "=2.0.1",
|
"font-awesome": "=4.6.3",
|
||||||
"jquery": "=2.1.4",
|
"jquery": "=2.2.3",
|
||||||
"react": "=0.13.3",
|
"jquery.easing": "=1.3.2",
|
||||||
"react-bootstrap": "=0.25.1",
|
"underscore": "=1.8.3",
|
||||||
"whatwg-fetch": "=0.10.1"
|
"wow.js": "=1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "=6.3.3",
|
"autoprefixer": "=6.4.0",
|
||||||
"babel-preset-es2015": "=6.1.18",
|
"babel-preset-es2015": "=6.13.2",
|
||||||
"babel-preset-react": "=6.1.18",
|
"babel-preset-react": "=6.11.1",
|
||||||
"babelify": "=7.2.0",
|
"babelify": "=7.3.0",
|
||||||
"browserify": "=11.0.1",
|
"browserify": "=13.1.0",
|
||||||
"clean-css": "=3.4.1",
|
"clean-css": "=3.4.19",
|
||||||
"eslint": "=1.5.0",
|
"eslint": "=1.5.0",
|
||||||
"eslint-config": "git://github.com/dabapps/eslint-config.git",
|
"eslint-config": "git://github.com/dabapps/eslint-config.git",
|
||||||
"eslint-plugin-react": "=3.4.2",
|
"eslint-plugin-react": "=3.4.2",
|
||||||
"less": "=2.5.1",
|
"markdown-spellcheck": "=0.11.0",
|
||||||
"less-mixins": "git://github.com/RealOrangeOne/less-mixins.git",
|
"node-sass": "=3.8.0",
|
||||||
"markdown-spellcheck": "=0.10.0",
|
"postcss-cli": "=2.5.2",
|
||||||
"postcss-cli": "=2.5.1",
|
"sass-lint": "=1.8.2",
|
||||||
"react-tools": "=0.13.2",
|
"uglify-js": "=2.7.0"
|
||||||
"uglify-js": "=2.4.24",
|
|
||||||
"watch": "=0.16.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
98
pelicanconf.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
# -*- coding: utf-8 -*- #
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
from git import Repo
|
||||||
|
import sys, os
|
||||||
|
sys.path.insert(0, os.path.realpath('./plugins'))
|
||||||
|
|
||||||
|
# Global core settings
|
||||||
|
AUTHOR = 'Jake Howard'
|
||||||
|
SITENAME = 'TheOrangeOne'
|
||||||
|
SITEURL = 'http://theorangeone.net'
|
||||||
|
PATH = 'content'
|
||||||
|
TIMEZONE = 'Europe/London'
|
||||||
|
DEFAULT_LANG = 'en'
|
||||||
|
PAGE_PATHS = ["pages"]
|
||||||
|
THEME = "theme"
|
||||||
|
THEME_STATIC_DIR = "static"
|
||||||
|
THEME_STATIC_PATHS = ["static/build"]
|
||||||
|
STATIC_PATHS = ["assets"]
|
||||||
|
|
||||||
|
USE_FOLDER_AS_CATEGORY = True
|
||||||
|
DEFAULT_PAGINATION = False
|
||||||
|
SLUGIFY_SOURCE = 'basename'
|
||||||
|
|
||||||
|
# Social widget
|
||||||
|
import links
|
||||||
|
ACCOUNTS = links.accounts()
|
||||||
|
FOOTER_LINKS = links.footer()
|
||||||
|
INDEX_PROJECTS = links.index_projects()
|
||||||
|
REPO = Repo(search_parent_directories=True)
|
||||||
|
|
||||||
|
# Disable some pages
|
||||||
|
TAG_URL = False
|
||||||
|
TAG_SAVE_AS = False
|
||||||
|
TAGS_SAVE_AS = False
|
||||||
|
AUTHORS_URL = False
|
||||||
|
AUTHORS_SAVE_AS = False
|
||||||
|
CATEGORIES_SAVE_AS = False
|
||||||
|
ARCHIVES_URL = False
|
||||||
|
ARCHIVES_SAVE_AS = False
|
||||||
|
AUTHOR_URL = False
|
||||||
|
AUTHOR_SAVE_AS = False
|
||||||
|
|
||||||
|
# Override page URLs
|
||||||
|
PAGE_SAVE_AS = "{slug}/index.html"
|
||||||
|
PAGE_URL = "{slug}"
|
||||||
|
ARTICLE_SAVE_AS = "{category}/{slug}/index.html"
|
||||||
|
ARTICLE_URL = "{category}/{slug}/"
|
||||||
|
CATEGORY_SAVE_AS = "{slug}/index.html"
|
||||||
|
CATEGORY_URL = "{slug}/"
|
||||||
|
|
||||||
|
# Add ATOM feed
|
||||||
|
FEED_ATOM = 'feed.atom'
|
||||||
|
FEED_DOMAIN = SITEURL
|
||||||
|
|
||||||
|
# Setup plugins
|
||||||
|
PLUGIN_PATHS = ["pelican_plugins", "plugins"]
|
||||||
|
PLUGINS = [
|
||||||
|
"sitemap",
|
||||||
|
"filetime_from_git",
|
||||||
|
"pelican-jinja2content",
|
||||||
|
"metatags",
|
||||||
|
"autopages",
|
||||||
|
"minify"
|
||||||
|
]
|
||||||
|
|
||||||
|
if "BUILD_PRODUCTION" in os.environ:
|
||||||
|
PLUGINS.append("minify") # only minify on production build
|
||||||
|
|
||||||
|
SITEMAP = {
|
||||||
|
"format": "xml"
|
||||||
|
}
|
||||||
|
CATEGORY_PAGE_PATH = "theme/templates/categories"
|
||||||
|
MINIFY = {
|
||||||
|
"remove_comments": True,
|
||||||
|
"remove_optional_attribute_quotes": False,
|
||||||
|
"reduce_boolean_attributes": True
|
||||||
|
}
|
||||||
|
|
||||||
|
# Setup markdown extensions
|
||||||
|
from fontawesome_markdown import FontAwesomeExtension
|
||||||
|
from pyembed.markdown import PyEmbedMarkdown
|
||||||
|
from mkdcomments import CommentsExtension
|
||||||
|
MD_EXTENSIONS = [
|
||||||
|
FontAwesomeExtension(),
|
||||||
|
PyEmbedMarkdown(),
|
||||||
|
CommentsExtension(),
|
||||||
|
'codehilite(css_class=highlight)',
|
||||||
|
'extra'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Setup jinja2 filters
|
||||||
|
import filters
|
||||||
|
JINJA_FILTERS = {
|
||||||
|
"datetime": filters.format_datetime,
|
||||||
|
"category_find": filters.category_find,
|
||||||
|
"limit": filters.limit,
|
||||||
|
"format_title": filters.format_title
|
||||||
|
}
|
26
plugins/filters.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import iso8601
|
||||||
|
|
||||||
|
|
||||||
|
def format_datetime(value):
|
||||||
|
return iso8601.parse_date(str(value)).strftime("%x")
|
||||||
|
|
||||||
|
|
||||||
|
def category_find(categories, name):
|
||||||
|
for category_name, articles in categories:
|
||||||
|
if category_name == name:
|
||||||
|
return articles
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def limit(line, length):
|
||||||
|
if isinstance(line, str):
|
||||||
|
if len(line) <= length:
|
||||||
|
return line
|
||||||
|
return " ".join(line.split(" ")[:length]) + '...'
|
||||||
|
elif isinstance(line, list):
|
||||||
|
return line[:length]
|
||||||
|
|
||||||
|
|
||||||
|
def format_title(value):
|
||||||
|
space_split = " ".join(value.split("-"))
|
||||||
|
return space_split.title()
|
46
plugins/links.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
from collections import namedtuple
|
||||||
|
from random import shuffle
|
||||||
|
|
||||||
|
ProjectLink = namedtuple("ProjectLink", ["name", "url", "image"])
|
||||||
|
Account = namedtuple("Account", ["name", "url", "icon", "username"])
|
||||||
|
|
||||||
|
|
||||||
|
def accounts():
|
||||||
|
links = {
|
||||||
|
"github": Account("GitHub", "https://github.com/RealOrangeOne/", "fa-github", "RealOrangeOne"),
|
||||||
|
"twitter": Account("Twitter", "https://twitter.com/RealOrangeOne", "fa-twitter", "@RealOrangeOne"),
|
||||||
|
"reddit": Account("Reddit", "https://reddit.com/user/RealOrangeOne", "fa-reddit", "/u/RealOrangeOne"),
|
||||||
|
"instagram": Account("Instagram", "https://instagram.com/RealOrangeOne", "fa-instagram", "RealOrangeOne"),
|
||||||
|
"youtube": Account("YouTube", "https://youtube.com/user/TheOrangeOneOfficial", "fa-youtube", "TheOrangeOneOfficial"),
|
||||||
|
"flickr": Account("Flickr", "https://flickr.com/photos/TheOrangeOne/", "fa-flickr", "TheOrangeOne"),
|
||||||
|
"bitbucket": Account("BitBucket", "https://bitbucket.org/TheOrangeOne", "fa-bitbucket", "TheOrangeOne"),
|
||||||
|
"trello": Account("Trello", "https://trello.com/TheOrangeOne", "fa-trello", "TheOrangeOne"),
|
||||||
|
"freenode": Account("Freenode", "https://webchat.freenode.net", "fa-rss", "TheOrangeOne"),
|
||||||
|
"atomio": Account("Atomio Slack", "https://atomio.slack.com", "fa-slack", "TheOrangeOne"),
|
||||||
|
"pcpartpicker": Account("PCPartPicker", "https://uk.pcpartpicker.com/user/theorangeone97", "fa-desktop", "TheOrangeOne97"),
|
||||||
|
"codepen": Account("CodePen", "https://codepen.io/TheOrangeOne", "fa-codepen", "TheOrangeOne"),
|
||||||
|
"npm": Account("npm", "https://www.npmjs.com/~TheOrangeOne", "fa-file-code-io", "TheOrangeOne")
|
||||||
|
}
|
||||||
|
return links
|
||||||
|
|
||||||
|
|
||||||
|
def footer():
|
||||||
|
footer_accounts = ["github", "twitter", "reddit", "instagram", "youtube", "flickr"]
|
||||||
|
all_accounts = accounts()
|
||||||
|
footer_links = []
|
||||||
|
for account in footer_accounts:
|
||||||
|
footer_links.append(all_accounts[account])
|
||||||
|
return footer_links
|
||||||
|
|
||||||
|
|
||||||
|
def index_projects():
|
||||||
|
projects = [
|
||||||
|
ProjectLink("Student Robotics", "/robotics/", "https://c2.staticflickr.com/8/7711/17122633430_e1b599fe47.jpg"),
|
||||||
|
ProjectLink("Dotfiles", "/projects/dotfiles/", "http://jleajones.com/assets/images/dotfiles.png"),
|
||||||
|
ProjectLink("Custom PC", "/setup/custom-pc/", "https://c2.staticflickr.com/8/7083/27071954860_f6096ccce6.jpg"),
|
||||||
|
ProjectLink("Yoga-Pal", "/projects/yoga-pal/", "http://brain-images.cdn.dixons.com/8/1/10135218/l_10135218_002.jpg"),
|
||||||
|
ProjectLink("React-Native Mock", "/projects/react-native-mock/", "http://i.imgur.com/ZB8O0DL.jpg"),
|
||||||
|
ProjectLink("Wall of Sheep", "/wall-of-sheep/", "http://www.hackerstickers.com/uploaded/thumbnails/db_file_img_3582_475xauto.jpg")
|
||||||
|
]
|
||||||
|
shuffle(projects)
|
||||||
|
return projects
|
107
plugins/metatags.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
from pelican import signals
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
|
||||||
|
def get_content_type(instance):
|
||||||
|
return type(instance).__name__
|
||||||
|
|
||||||
|
|
||||||
|
def html_to_raw(html):
|
||||||
|
summary = BeautifulSoup(html, "html.parser")
|
||||||
|
for script in summary(["script", "style"]): # Remove extra tags
|
||||||
|
script.extract()
|
||||||
|
return summary.get_text()
|
||||||
|
|
||||||
|
|
||||||
|
def get_twiter_tags(instance):
|
||||||
|
return {
|
||||||
|
"twitter:card": "summary_large_image",
|
||||||
|
"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: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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_og_tags(instance):
|
||||||
|
return {
|
||||||
|
"og:title": instance.metadata.get("title", ""),
|
||||||
|
"og:type": get_content_type(instance).lower(),
|
||||||
|
"og:url": os.path.join(instance.settings.get("SITEURL"), instance.url),
|
||||||
|
"og:image": instance.metadata.get("image", ""),
|
||||||
|
"og:description": html_to_raw(instance.metadata.get("summary", "")),
|
||||||
|
"og:site_name": instance.settings.get("SITENAME"),
|
||||||
|
"og:locale": instance.metadata.get("locale", "en_GB")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_schema_tags(instance):
|
||||||
|
return {
|
||||||
|
"name": instance.metadata.get("title", ""),
|
||||||
|
"description": html_to_raw(instance.metadata.get("summary", "")),
|
||||||
|
"image": instance.metadata.get("image", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_general_tags(instance):
|
||||||
|
return {
|
||||||
|
"article:author": instance.settings.get("AUTHOR"),
|
||||||
|
"article:modified_time": instance.metadata.get("modified", ""), # Set build time as default?
|
||||||
|
"article:published_time": instance.metadata.get("date", ""),
|
||||||
|
"article:section": instance.category.name if hasattr(instance, "category") else "",
|
||||||
|
"description": html_to_raw(instance.metadata.get("summary", "")),
|
||||||
|
"author": instance.metadata.get("author", instance.settings.get("AUTHOR")),
|
||||||
|
"canonical": instance.settings.get("SITEURL")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def tag_item(instance):
|
||||||
|
instance_type = get_content_type(instance)
|
||||||
|
|
||||||
|
if instance_type not in ['Article', 'Page', 'Draft']:
|
||||||
|
return
|
||||||
|
|
||||||
|
metatags = []
|
||||||
|
|
||||||
|
for tag, value in get_twiter_tags(instance).items():
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
metatags.append(
|
||||||
|
"<meta name='{0}' content='{1}' />".format(tag, value)
|
||||||
|
)
|
||||||
|
|
||||||
|
for tag, value in get_og_tags(instance).items():
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
metatags.append(
|
||||||
|
"<meta property='{0}' content='{1}' />".format(tag, value)
|
||||||
|
)
|
||||||
|
|
||||||
|
for tag, value in get_schema_tags(instance).items():
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
metatags.append(
|
||||||
|
"<meta itemprop='{0}' content='{1}' />".format(tag, value)
|
||||||
|
)
|
||||||
|
|
||||||
|
general_tags = get_general_tags(instance).items()
|
||||||
|
if hasattr(instance, 'tags'):
|
||||||
|
for tag in instance.tags:
|
||||||
|
general_tags.append(('article:tag', tag.name))
|
||||||
|
|
||||||
|
for tag, value in general_tags:
|
||||||
|
if not value:
|
||||||
|
continue
|
||||||
|
metatags.append(
|
||||||
|
"<meta name='{0}' content='{1}' />".format(tag, value)
|
||||||
|
)
|
||||||
|
|
||||||
|
instance.metatags = '\n'.join(metatags)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
signals.content_object_init.connect(tag_item)
|
|
@ -1,67 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
import requests_mock, json
|
|
||||||
from . import utils
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
|
|
||||||
|
|
||||||
@requests_mock.mock()
|
|
||||||
class WordPressAPITestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.test_blog_data = {
|
|
||||||
"title": "Test Blog Post",
|
|
||||||
"ID": 1,
|
|
||||||
"content": "<p>Test blog post content</p>",
|
|
||||||
"slug": "test-post"
|
|
||||||
}
|
|
||||||
self.invalid_blog_data = {
|
|
||||||
"title": "Invalid blog post",
|
|
||||||
"content": "<p></p>",
|
|
||||||
"slug": "invalid"
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_gets_correct_data(self, m):
|
|
||||||
payload = json.dumps(self.test_blog_data)
|
|
||||||
m.get(utils.build_url(self.test_blog_data['slug']), text=payload)
|
|
||||||
blog_data = utils.get_post(self.test_blog_data['slug'])
|
|
||||||
self.assertEqual(blog_data, self.test_blog_data)
|
|
||||||
|
|
||||||
def test_invalid_response(self, m):
|
|
||||||
payload = json.dumps(self.invalid_blog_data)
|
|
||||||
m.get(utils.build_url(self.invalid_blog_data['slug']), text=payload)
|
|
||||||
blog_data = utils.get_post(self.invalid_blog_data['slug'])
|
|
||||||
self.assertFalse(blog_data)
|
|
||||||
|
|
||||||
def test_invalid_status(self, m):
|
|
||||||
payload = json.dumps(self.test_blog_data)
|
|
||||||
m.get(utils.build_url(self.test_blog_data['slug']), text=payload, status_code=500)
|
|
||||||
blog_data = utils.get_post(self.test_blog_data['slug'])
|
|
||||||
self.assertFalse(blog_data)
|
|
||||||
|
|
||||||
def test_no_slug(self, m):
|
|
||||||
blog_data = utils.get_post('')
|
|
||||||
self.assertFalse(blog_data)
|
|
||||||
|
|
||||||
|
|
||||||
@requests_mock.mock()
|
|
||||||
class BlogViewTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
self.test_blog_data = {
|
|
||||||
"title": "Test Blog Post",
|
|
||||||
"ID": 1,
|
|
||||||
"content": "<p>Test blog post content</p>",
|
|
||||||
"slug": "test-post",
|
|
||||||
"date": "2000-01-01T18:05:00+00:00"
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_accessable(self, m):
|
|
||||||
payload = json.dumps(self.test_blog_data)
|
|
||||||
m.get(utils.build_url(self.test_blog_data['slug']), text=payload)
|
|
||||||
response = self.client.get(reverse('blog:blog-post', args=[self.test_blog_data['slug']]))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_correct_content(self, m):
|
|
||||||
payload = json.dumps(self.test_blog_data)
|
|
||||||
m.get(utils.build_url(self.test_blog_data['slug']), text=payload)
|
|
||||||
response = self.client.get(reverse('blog:blog-post', args=[self.test_blog_data['slug']]))
|
|
||||||
self.assertContains(response, self.test_blog_data['content'])
|
|
||||||
self.assertEqual(response.context['html_title'], self.test_blog_data['title'])
|
|
|
@ -1,6 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^(?P<slug>.+)/?$', views.BlogView.as_view(), name='blog-post'),
|
|
||||||
]
|
|
|
@ -1,25 +0,0 @@
|
||||||
import requests, iso8601
|
|
||||||
from django.conf import settings
|
|
||||||
|
|
||||||
API_PATH = "https://public-api.wordpress.com/rest/v1.1/sites/{0}/posts/slug:{1}"
|
|
||||||
|
|
||||||
|
|
||||||
def build_url(slug):
|
|
||||||
if not slug:
|
|
||||||
return
|
|
||||||
return API_PATH.format(settings.WORDPRESS_URL, slug)
|
|
||||||
|
|
||||||
|
|
||||||
def get_post(slug):
|
|
||||||
if not slug:
|
|
||||||
return
|
|
||||||
response = requests.get(build_url(slug))
|
|
||||||
|
|
||||||
if response.status_code != 200:
|
|
||||||
return
|
|
||||||
data = response.json()
|
|
||||||
return data if "ID" in data else False
|
|
||||||
|
|
||||||
|
|
||||||
def reformat_date(iso_date):
|
|
||||||
return iso8601.parse_date(iso_date).strftime("%x %I:%M")
|
|
|
@ -1,22 +0,0 @@
|
||||||
from django.views.generic import TemplateView
|
|
||||||
from .utils import get_post, reformat_date
|
|
||||||
from django.http import Http404
|
|
||||||
|
|
||||||
|
|
||||||
class BlogView(TemplateView):
|
|
||||||
template_name = "blog/post.html"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['blog'] = self.blog_data
|
|
||||||
context['blog']['date'] = reformat_date(self.blog_data['date'])
|
|
||||||
context['html_title'] = self.blog_data['title']
|
|
||||||
if 'featured_image' in self.blog_data:
|
|
||||||
context['header_image'] = self.blog_data['featured_image']
|
|
||||||
return context
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.blog_data = get_post(kwargs['slug'])
|
|
||||||
if not self.blog_data:
|
|
||||||
raise Http404
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
|
@ -1,46 +0,0 @@
|
||||||
import os.path
|
|
||||||
import yaml
|
|
||||||
from glob import glob
|
|
||||||
from project.pages.utils import get_title_from_markdown, parse_content
|
|
||||||
|
|
||||||
|
|
||||||
def get_data_from_file(base_dir, filename):
|
|
||||||
with open(os.path.join(base_dir, 'data', filename)) as data_file:
|
|
||||||
return yaml.load(data_file) or {}
|
|
||||||
|
|
||||||
|
|
||||||
def generate_config(base_dir):
|
|
||||||
default = get_data_from_file(base_dir, 'context.yml')
|
|
||||||
page = get_data_from_file(base_dir, 'page_context.yml')
|
|
||||||
switcher = get_data_from_file(base_dir, 'path_switch.yml')
|
|
||||||
|
|
||||||
# Add projects config
|
|
||||||
default['projects'] = generate_projects(base_dir)
|
|
||||||
# Join projects config with it's page context
|
|
||||||
for i in range(len(default['projects'])):
|
|
||||||
project = default['projects'][i]
|
|
||||||
if project['path'] in page: # If there's a custom config
|
|
||||||
default['projects'][i] = dict(project, **page[project['path']])
|
|
||||||
|
|
||||||
return default, page, switcher
|
|
||||||
|
|
||||||
|
|
||||||
def generate_projects(base_dir):
|
|
||||||
projects_path = os.path.join(base_dir, 'templates/projects')
|
|
||||||
files = []
|
|
||||||
for path in glob(projects_path + '/*.*'):
|
|
||||||
filename = path.replace(projects_path, '')
|
|
||||||
if filename == '/index.html':
|
|
||||||
continue
|
|
||||||
with open(path) as f:
|
|
||||||
if filename.split('.')[1] == 'md':
|
|
||||||
parsed_content = parse_content(f.read(), filename.split('.')[1])
|
|
||||||
filename = get_title_from_markdown(parsed_content)
|
|
||||||
else:
|
|
||||||
filename = filename.split('.')[0]
|
|
||||||
files.append({
|
|
||||||
"name": filename,
|
|
||||||
"path": 'projects' + path.replace(projects_path, '').split('.')[0],
|
|
||||||
"url": '/projects' + path.replace(projects_path, '').split('.')[0],
|
|
||||||
})
|
|
||||||
return files
|
|
|
@ -1,19 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
from django.conf import settings
|
|
||||||
import os.path
|
|
||||||
from glob import glob
|
|
||||||
|
|
||||||
|
|
||||||
class PagesTestCase(TestCase):
|
|
||||||
def setUp(self):
|
|
||||||
directories = glob(os.path.join(settings.BASE_DIR, 'templates') + '/**/*.*')
|
|
||||||
self.urls = []
|
|
||||||
for directory in directories:
|
|
||||||
if 'email' in directory or 'blog' in directory:
|
|
||||||
continue
|
|
||||||
self.urls.append(directory.replace(os.path.join(settings.BASE_DIR, 'templates'), '').split('.')[0].replace('index', ''))
|
|
||||||
|
|
||||||
def test_pages_accessable(self):
|
|
||||||
for path in self.urls:
|
|
||||||
response = self.client.get(path)
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from .views import page_view
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^(?P<path>.*)', page_view, name='page'),
|
|
||||||
]
|
|
|
@ -1,29 +0,0 @@
|
||||||
from django.conf import settings
|
|
||||||
from bs4 import BeautifulSoup
|
|
||||||
import markdown2
|
|
||||||
|
|
||||||
|
|
||||||
def get_context(path):
|
|
||||||
if path in settings.PAGE_CONTEXT:
|
|
||||||
context = dict(settings.DEFAULT_CONTEXT, **settings.PAGE_CONTEXT[path])
|
|
||||||
else:
|
|
||||||
context = dict(settings.DEFAULT_CONTEXT)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
def get_title_from_markdown(md):
|
|
||||||
html_tree = BeautifulSoup(md, "html.parser")
|
|
||||||
tag = html_tree.find('h1')
|
|
||||||
return tag.contents[0]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_content(content, extension):
|
|
||||||
if extension == 'md':
|
|
||||||
return markdown2.markdown(content)
|
|
||||||
return content
|
|
||||||
|
|
||||||
|
|
||||||
def swap_page(path):
|
|
||||||
if path in settings.PAGE_SWITCH:
|
|
||||||
return settings.PAGE_SWITCH[path]
|
|
||||||
return path
|
|
|
@ -1,33 +0,0 @@
|
||||||
import os.path
|
|
||||||
from django.conf import settings
|
|
||||||
from django.http import HttpResponse, Http404
|
|
||||||
from django.template.loader import get_template
|
|
||||||
from .utils import get_context, parse_content, get_title_from_markdown, swap_page
|
|
||||||
|
|
||||||
|
|
||||||
def page_view(request, path):
|
|
||||||
template = None
|
|
||||||
if path.endswith('/'): # Strip trailing slash
|
|
||||||
path = path[:-1]
|
|
||||||
|
|
||||||
path = swap_page(path)
|
|
||||||
|
|
||||||
if os.path.isdir(os.path.join(settings.BASE_DIR, 'templates', path)):
|
|
||||||
path = os.path.join(path, 'index')
|
|
||||||
for extension in ['md', 'html']:
|
|
||||||
try:
|
|
||||||
template = get_template("{}.{}".format(path, extension))
|
|
||||||
break
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if not template:
|
|
||||||
raise Http404
|
|
||||||
context = get_context(path)
|
|
||||||
parsed_content = parse_content(template.render(context, request), extension)
|
|
||||||
if extension == 'md':
|
|
||||||
template = get_template('markdown_content.html')
|
|
||||||
context['markdown_content'] = parsed_content
|
|
||||||
context['page_title'] = get_title_from_markdown(parsed_content)
|
|
||||||
context['html_title'] = context['page_title']
|
|
||||||
parsed_content = template.render(context, request)
|
|
||||||
return HttpResponse(parsed_content)
|
|
|
@ -1,90 +0,0 @@
|
||||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
|
||||||
import dj_database_url, os
|
|
||||||
from private import export
|
|
||||||
|
|
||||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
|
||||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
|
||||||
SECRET_KEY = export('SECRET_KEY')
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
|
||||||
DEBUG = True
|
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['too.ctf.sh', 'theorangeone.net']
|
|
||||||
|
|
||||||
# Application definition
|
|
||||||
INSTALLED_APPS = (
|
|
||||||
'django.contrib.auth',
|
|
||||||
'django.contrib.contenttypes',
|
|
||||||
'django.contrib.staticfiles',
|
|
||||||
|
|
||||||
'project.pages',
|
|
||||||
'project.common',
|
|
||||||
'project.blog'
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = (
|
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
|
||||||
'django.middleware.common.CommonMiddleware',
|
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
|
||||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
|
||||||
'django.middleware.security.SecurityMiddleware',
|
|
||||||
)
|
|
||||||
|
|
||||||
ROOT_URLCONF = 'project.urls'
|
|
||||||
|
|
||||||
TEMPLATES = [
|
|
||||||
{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
|
||||||
'APP_DIRS': True,
|
|
||||||
'OPTIONS': {
|
|
||||||
'context_processors': [
|
|
||||||
'django.template.context_processors.debug',
|
|
||||||
'django.template.context_processors.request',
|
|
||||||
'django.contrib.auth.context_processors.auth',
|
|
||||||
'django.contrib.messages.context_processors.messages',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
WSGI_APPLICATION = 'project.wsgi.application'
|
|
||||||
|
|
||||||
|
|
||||||
# Database
|
|
||||||
# https://docs.djangoproject.com/en/1.8/ref/settings/#databases
|
|
||||||
DATABASES = {
|
|
||||||
'default': dj_database_url.config(default='sqlite://memory')
|
|
||||||
}
|
|
||||||
|
|
||||||
EMAIL_BACKEND = os.environ['EMAIL_BACKEND']
|
|
||||||
|
|
||||||
# Internationalization
|
|
||||||
# https://docs.djangoproject.com/en/1.8/topics/i18n/
|
|
||||||
LANGUAGE_CODE = 'en-gb'
|
|
||||||
TIME_ZONE = 'UTC'
|
|
||||||
USE_I18N = True
|
|
||||||
USE_L10N = True
|
|
||||||
USE_TZ = True
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
|
||||||
# https://docs.djangoproject.com/en/1.8/howto/static-files/
|
|
||||||
STATIC_URL = '/static/'
|
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, 'collected-static')
|
|
||||||
STATICFILES_DIRS = (
|
|
||||||
os.path.join(BASE_DIR, 'static', 'build'),
|
|
||||||
)
|
|
||||||
|
|
||||||
WORDPRESS_URL = "realorangeone.wordpress.com"
|
|
||||||
|
|
||||||
# Generate config data
|
|
||||||
from project.common.data import generate_config
|
|
||||||
DEFAULT_CONTEXT, PAGE_CONTEXT, PAGE_SWITCH = generate_config(BASE_DIR)
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls import include, url
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^blog/', include('project.blog.urls', namespace='blog')),
|
|
||||||
url(r'', include('project.pages.urls', namespace='pages'))
|
|
||||||
]
|
|
|
@ -1,8 +0,0 @@
|
||||||
import os
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
|
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
|
||||||
from whitenoise.django import DjangoWhiteNoise
|
|
||||||
|
|
||||||
application = get_wsgi_application()
|
|
||||||
application = DjangoWhiteNoise(application)
|
|
|
@ -1,13 +1,9 @@
|
||||||
beautifulsoup4==4.4.1
|
|
||||||
coverage==4.0.3
|
|
||||||
colorama==0.3.6
|
|
||||||
Django==1.8.11
|
|
||||||
dj-database-url==0.3.0
|
|
||||||
flake8==2.5.0
|
flake8==2.5.0
|
||||||
|
fontawesome_markdown==0.2.5
|
||||||
|
gitpython==2.0.7
|
||||||
iso8601==0.1.11
|
iso8601==0.1.11
|
||||||
markdown2==2.3.0
|
markdown==2.6.6
|
||||||
PyYAML==3.11
|
git+https://github.com/ryneeverett/python-markdown-comments.git
|
||||||
requests==2.9.1
|
pelican==3.6.3
|
||||||
requests-mock==0.7.0
|
pelican-minify==0.9
|
||||||
whitenoise==2.0.6
|
pyembed-markdown==1.1.0
|
||||||
waitress==0.8.10
|
|
||||||
|
|
67
runtests
|
@ -1,67 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
import coverage
|
|
||||||
import os, sys
|
|
||||||
import subprocess
|
|
||||||
from colorama import Fore, init
|
|
||||||
|
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
|
|
||||||
from django.core.management import execute_from_command_line
|
|
||||||
init(autoreset=True)
|
|
||||||
|
|
||||||
|
|
||||||
bin_dir = os.path.abspath(os.path.join(sys.executable, os.path.pardir))
|
|
||||||
PERCENTAGE = 95
|
|
||||||
EXIT_CODE = 0
|
|
||||||
|
|
||||||
|
|
||||||
def check_if_exit_code():
|
|
||||||
if EXIT_CODE != 0:
|
|
||||||
print("\n{}Tests Failed. {}Please check messages above and then re-run the command.".format(Fore.RED, Fore.YELLOW))
|
|
||||||
exit(EXIT_CODE)
|
|
||||||
|
|
||||||
|
|
||||||
cov = coverage.Coverage(
|
|
||||||
source=["project"],
|
|
||||||
omit=["*/wsgi.py", "*/settings.py", "*/migrations/*.py", "*/__init__*", "*/tests.py"]
|
|
||||||
)
|
|
||||||
cov.start()
|
|
||||||
|
|
||||||
print(Fore.YELLOW + "Running Tests...")
|
|
||||||
execute_from_command_line([sys.argv[0], 'test'])
|
|
||||||
cov.stop()
|
|
||||||
|
|
||||||
print("{}Tests Complete. {}Collecting Coverage...".format(Fore.GREEN, Fore.YELLOW))
|
|
||||||
cov.save()
|
|
||||||
cov.html_report()
|
|
||||||
covered = cov.report()
|
|
||||||
if covered < PERCENTAGE:
|
|
||||||
print("{}ERROR: Your coverage needs to be higher. Current coverage: {}%. Required: {}%.".format(Fore.RED, covered, PERCENTAGE))
|
|
||||||
EXIT_CODE = 1
|
|
||||||
|
|
||||||
check_if_exit_code()
|
|
||||||
print("{}Coverage Complete. {}Linting...".format(Fore.GREEN, Fore.YELLOW))
|
|
||||||
|
|
||||||
|
|
||||||
FLAKE8_IGNORE = '--ignore=E128,E501,E401,E402'
|
|
||||||
try:
|
|
||||||
subprocess.check_output([os.path.join(bin_dir, 'flake8'), 'project', FLAKE8_IGNORE, '--exclude=migrations,settings,wsgi.py,__init__.py'])
|
|
||||||
subprocess.check_output([os.path.join(bin_dir, 'flake8'), 'scripts', FLAKE8_IGNORE])
|
|
||||||
subprocess.check_output([os.path.join(bin_dir, 'flake8'), sys.argv[0], FLAKE8_IGNORE])
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(Fore.RED, e.output.decode())
|
|
||||||
EXIT_CODE = 1
|
|
||||||
|
|
||||||
check_if_exit_code()
|
|
||||||
print("{}All Python tests passed! {}Testing Static Files...".format(Fore.GREEN, Fore.YELLOW))
|
|
||||||
|
|
||||||
|
|
||||||
EXIT_CODE = os.system('npm test')
|
|
||||||
check_if_exit_code()
|
|
||||||
|
|
||||||
print("{}All static tests passed! {}Running spell check...".format(Fore.GREEN, Fore.YELLOW))
|
|
||||||
|
|
||||||
EXIT_CODE = os.system('')
|
|
||||||
check_if_exit_code()
|
|
||||||
|
|
||||||
print("{}All Tests Passed!".format(Fore.GREEN))
|
|
|
@ -2,43 +2,43 @@
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
if [ "$NODE_ENV" = "production" ]
|
||||||
then
|
then
|
||||||
echo ">>> WARNING: Building in Production Mode!"
|
echo ">>> WARNING: Building in Production Mode!"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p static/build/js/lib
|
mkdir -p theme/static/build/js/lib
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
if [ "$NODE_ENV" = "production" ]
|
||||||
then
|
then
|
||||||
echo ">> Compressing Libraries..."
|
echo ">> Compressing Libraries..."
|
||||||
uglifyjs node_modules/bootstrap/dist/js/bootstrap.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/lib/bootstrap.js
|
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 static/build/js/lib/* --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/libs.js
|
uglifyjs theme/static/build/js/lib/* --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/libs.js
|
||||||
else
|
else
|
||||||
echo ">> Building Libraries..."
|
echo ">> Building Libraries..."
|
||||||
cp node_modules/bootstrap/dist/js/bootstrap.js static/build/js/lib/bootstrap.js
|
cp node_modules/bootstrap-sass/assets/javascripts/bootstrap.js theme/static/build/js/lib/bootstrap.js
|
||||||
uglifyjs static/build/js/lib/* --screw-ie8 --stats --keep-fnames -o static/build/js/libs.js
|
uglifyjs theme/static/build/js/lib/* --screw-ie8 --stats --keep-fnames -o theme/static/build/js/libs.js
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf static/build/js/lib
|
rm -rf theme/static/build/js/lib
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
if [ "$NODE_ENV" = "production" ]
|
||||||
then
|
then
|
||||||
echo ">> Compressing jQuery..."
|
echo ">> Compressing jQuery..."
|
||||||
uglifyjs node_modules/jquery/dist/jquery.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/jquery.js
|
uglifyjs node_modules/jquery/dist/jquery.js --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/jquery.js
|
||||||
else
|
else
|
||||||
echo ">> Building jQuery..."
|
echo ">> Building jQuery..."
|
||||||
uglifyjs node_modules/jquery/dist/jquery.js --screw-ie8 --stats --keep-fnames -o static/build/js/jquery.js
|
cp node_modules/jquery/dist/jquery.js theme/static/build/js/jquery.js
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
echo ">> Building Application JS..."
|
echo ">> Building Application JS..."
|
||||||
browserify -t [ babelify --presets [ es2015 react ] ] static/src/js/app.js -o static/build/js/app.js
|
browserify -t [ babelify --presets [ es2015 react ] ] theme/static/src/js/app.js -o theme/static/build/js/app.js
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
if [ "$NODE_ENV" = "production" ]
|
||||||
then
|
then
|
||||||
echo ">> Compressing Application..."
|
echo ">> Compressing Application..."
|
||||||
uglifyjs static/build/js/app.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/app.js
|
uglifyjs theme/static/build/js/app.js --compress --screw-ie8 --define --stats --keep-fnames -o theme/static/build/js/app.js
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "> JS Built!"
|
echo "> JS Built!"
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
|
||||||
then
|
|
||||||
echo ">>> WARNING: Building in Production Mode!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ">> Building LESS..."
|
|
||||||
lessc --silent static/src/less/style.less static/build/css/style.css
|
|
||||||
|
|
||||||
echo ">> Post-Processing..."
|
|
||||||
postcss -u autoprefixer -o static/build/css/style.css static/build/css/style.css
|
|
||||||
|
|
||||||
if [[ $BUILD_PRODUCTION ]]
|
|
||||||
then
|
|
||||||
echo ">> Compressing LESS..."
|
|
||||||
cleancss -d --s0 -o static/build/css/style.css static/build/css/style.css
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "> LESS Built!"
|
|
23
scripts/build-scss.sh
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ "$NODE_ENV" = "production" ]
|
||||||
|
then
|
||||||
|
echo ">>> WARNING: Building in Production Mode!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ">> Generating Pygments styles..."
|
||||||
|
pygmentize -S monokai -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
|
|
@ -1,15 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
|
|
||||||
echo ">> Removing VirtualEnv..."
|
|
||||||
rm -rf env/
|
|
||||||
|
|
||||||
echo ">> Removing Node Modules..."
|
|
||||||
rm -rf node_modules/
|
|
||||||
|
|
||||||
echo ">> Removing Static Build Directory..."
|
|
||||||
rm -rf static/build/
|
|
||||||
|
|
||||||
echo "> Cleaning Complete."
|
|
|
@ -1,24 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
|
|
||||||
echo ">> Removing VirtualEnv..."
|
|
||||||
rm -rf env/
|
|
||||||
|
|
||||||
echo ">> Removing Collected Static Files..."
|
|
||||||
rm -rf collected-static/
|
|
||||||
|
|
||||||
echo ">> Removing Private Data..."
|
|
||||||
rm -rf private/
|
|
||||||
|
|
||||||
echo ">> Removing Node Modules..."
|
|
||||||
rm -rf node_modules/
|
|
||||||
|
|
||||||
echo ">> Removing Static Build Directory..."
|
|
||||||
rm -rf static/build/
|
|
||||||
|
|
||||||
echo ">> Removing Stray Files and Folders..."
|
|
||||||
rm -rf htmlcov/ .coverage
|
|
||||||
|
|
||||||
echo "> Much Fresher!"
|
|
|
@ -1,9 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo ">> Removing old Private Data..."
|
|
||||||
rm -rf private/
|
|
||||||
|
|
||||||
echo ">> Getting Private Data..."
|
|
||||||
git clone git@github.com:RealOrangeOne/theorangeone.net-site-private-data.git --branch master --single-branch private/
|
|
4
scripts/server.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
cd output/
|
||||||
|
python3 -m pelican.server $PORT
|
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 458 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 1 MiB |
|
@ -1,24 +0,0 @@
|
||||||
import './events.js';
|
|
||||||
import './globals.js';
|
|
||||||
|
|
||||||
import 'whatwg-fetch';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
import NavBar from './components/navbar/navbar';
|
|
||||||
import Breadcrumbs from './components/breadcrumbs';
|
|
||||||
import renderImagePanels from './components/image-panel';
|
|
||||||
|
|
||||||
renderImagePanels();
|
|
||||||
|
|
||||||
if ($('navbar').length > 0) {
|
|
||||||
React.render(<NavBar />, $('navbar')[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('#breadcrumbs').length > 0) {
|
|
||||||
React.render(<Breadcrumbs />, $('#breadcrumbs')[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('.header h1').text()) {
|
|
||||||
$('.markdown-content h1').eq(0).hide();
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default class Breadcrumbs extends React.Component {
|
|
||||||
render() {
|
|
||||||
const loc = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname;
|
|
||||||
const urlParts = Object.freeze(loc.split('/').slice(1));
|
|
||||||
if (urlParts.length < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var elements = [];
|
|
||||||
for (var i = 0; i < urlParts.length; i++) {
|
|
||||||
var dirs = [];
|
|
||||||
for (var j = 0; j <= i; j++) {
|
|
||||||
dirs.push(urlParts[j]);
|
|
||||||
}
|
|
||||||
if (i === (urlParts.length - 1)) {
|
|
||||||
elements.push(
|
|
||||||
<li className="active">{urlParts[i]}</li>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
var url = '/' + dirs.join('/') + '/';
|
|
||||||
elements.push(
|
|
||||||
<li><a href={url}>{urlParts[i]}</a></li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<ol className="breadcrumb">
|
|
||||||
{ elements }
|
|
||||||
</ol>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
export default function renderImagePanels() {
|
|
||||||
$('.image-panel').each(function () {
|
|
||||||
const element = $(this);
|
|
||||||
if (!element.data('image')) { // if it doesnt have an image, ignore it.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
element.css('background-image', 'url("' + element.data('image') + '")');
|
|
||||||
|
|
||||||
let height;
|
|
||||||
switch (element.data('size')) {
|
|
||||||
case 'small':
|
|
||||||
height = '30';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case 'medium':
|
|
||||||
height = '60';
|
|
||||||
break;
|
|
||||||
case 'large':
|
|
||||||
height = '100';
|
|
||||||
}
|
|
||||||
element.css('height', `${height}vh`);
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default class Dropdown extends React.Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<li className="dropdown">
|
|
||||||
<a className="dropdown-toggle"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
role="button"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false">
|
|
||||||
{ this.props.title } <i className="icon ion-ios-arrow-up navbar-icon h4 hidden-sm"></i>
|
|
||||||
</a>
|
|
||||||
<ul className="dropdown-menu dropup">
|
|
||||||
{ this.props.children }
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export default class Header extends React.Component {
|
|
||||||
render() {
|
|
||||||
const items = [0, 1, 2];
|
|
||||||
var iconBars = items.map(function (item) {
|
|
||||||
return (
|
|
||||||
<span className="icon-bar" key={item} />
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div className="navbar-header">
|
|
||||||
<button type="button"
|
|
||||||
className="navbar-toggle collapsed"
|
|
||||||
data-toggle="collapse"
|
|
||||||
data-target="#navbar"
|
|
||||||
aria-expanded="false" >
|
|
||||||
<span className="sr-only">Toggle navigation</span>
|
|
||||||
{{ iconBars }}
|
|
||||||
</button>
|
|
||||||
<a className="navbar-brand visible-xs">
|
|
||||||
<i className="icon ion-compass" />
|
|
||||||
Navigation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
|
|
||||||
export default class Item extends React.Component {
|
|
||||||
render() {
|
|
||||||
const href = this.props.href.endsWith('/') ? this.props.href : this.props.href + '/';
|
|
||||||
let icon;
|
|
||||||
if (this.props.icon) {
|
|
||||||
if (this.props.icon.startsWith('ion')) {
|
|
||||||
icon = (
|
|
||||||
<i className={'icon ' + this.props.icon} />
|
|
||||||
);
|
|
||||||
} else if (this.props.icon.startsWith('glyphicon')) {
|
|
||||||
icon = (
|
|
||||||
<span className={'glyphicon ' + this.props.icon} aria-hidden="true" />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<li><a href={href}>{icon}{this.props.children}</a></li>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import Header from './header';
|
|
||||||
import Dropdown from './dropdown';
|
|
||||||
import Item from './item';
|
|
||||||
|
|
||||||
export default class NavBar extends React.Component {
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<span>
|
|
||||||
<div id="navbar-anchor" />
|
|
||||||
<div id="navbar-container" className="align">
|
|
||||||
<nav className="navbar navbar-inverse">
|
|
||||||
<div className="container-fluid">
|
|
||||||
<Header />
|
|
||||||
<div className="collapse navbar-collapse" id="navbar">
|
|
||||||
<ul className="nav navbar-nav home-button hidden-sm hidden-md">
|
|
||||||
<Item href="/" icon="ion-home" />
|
|
||||||
</ul>
|
|
||||||
<ul className="nav navbar-nav" id="navigation">
|
|
||||||
<Dropdown title="Projects">
|
|
||||||
<Item href="/projects/yoga-pal/" icon="ion-laptop">Yoga Pal</Item>
|
|
||||||
<Item href="/projects/bsod-enabler/" icon="ion-ios-monitor">BSOD Enabler</Item>
|
|
||||||
<Item href="/projects/hipchat-emoticons-for-all/" icon="ion-chatbox-working">
|
|
||||||
Hipchat Emoticons for All
|
|
||||||
</Item>
|
|
||||||
<Item href="#" icon="ion-social-windows">Custom PC</Item>
|
|
||||||
<Item href="/projects/" icon="ion-android-more-vertical">All Projects...</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown title="Code">
|
|
||||||
<Item href="#" icon="ion-code-working">Code Challenges</Item>
|
|
||||||
<Item href="#" icon="ion-ios-circle-filled">Pi Time Lapse</Item>
|
|
||||||
<Item href="/projects/wiki-game-solver/" icon="ion-ios-game-controller-a">The Wiki Game Solver</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown title="College">
|
|
||||||
<Item href="/robotics/">Student Robotics</Item>
|
|
||||||
<Item href="/college/attack-on-blocks/" icon="ion-cube">Attack on Blocks Game</Item>
|
|
||||||
<Item href="/college/student-server/" icon="glyphicon-hdd">Student Server</Item>
|
|
||||||
<Item href="#" icon="ion-ios-paper">EPQ</Item>
|
|
||||||
<Item href="#">Wall of Sheep</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown title="Setup">
|
|
||||||
<Item href="#">💁 Desk</Item>
|
|
||||||
<Item href="#" icon="ion-briefcase">Work</Item>
|
|
||||||
<Item href="#" icon="ion-model-s">On The Go</Item>
|
|
||||||
<Item href="#" icon="ion-android-phone-portrait">Devices</Item>
|
|
||||||
<Item href="#">Servers</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown title="Work">
|
|
||||||
<Item href="#" icon="ion-form-repo">Projects</Item>
|
|
||||||
<Item href="#" icon="ion-android-desktop">Setup</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Dropdown title="Media">
|
|
||||||
<Item
|
|
||||||
href="https://www.youtube.com/user/TheOrangeOneOfficial"
|
|
||||||
icon="ion-social-youtube">
|
|
||||||
YouTube Channel
|
|
||||||
</Item>
|
|
||||||
<Item href="/blog/" icon="ion-social-rss">Blog</Item>
|
|
||||||
<Item href="https://www.flickr.com/photos/theorangeone/" icon="ion-social-camera">Gallery</Item>
|
|
||||||
</Dropdown>
|
|
||||||
<Item href="#">Links</Item>
|
|
||||||
<Dropdown title="About">
|
|
||||||
<Item href="/about/me/" icon="ion-android-person">Me</Item>
|
|
||||||
<Item href="/about/website/" icon="ion-cloud">Website</Item>
|
|
||||||
</Dropdown>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
/* global $ */
|
|
||||||
var is_navbar_attached = false;
|
|
||||||
|
|
||||||
$(window).load(function() {
|
|
||||||
$(window).trigger('scroll');
|
|
||||||
});
|
|
||||||
|
|
||||||
function detach_navbar() {
|
|
||||||
$('#navbar-container').removeClass('stick-top').addClass('align');
|
|
||||||
$('.dropdown-menu').removeClass('dropdown').addClass('dropup');
|
|
||||||
}
|
|
||||||
|
|
||||||
function attach_navbar() {
|
|
||||||
$('#navbar-container').removeClass('align').addClass('stick-top');
|
|
||||||
$('.dropdown-menu').removeClass('dropup').addClass('dropdown');
|
|
||||||
}
|
|
||||||
|
|
||||||
function flip_dropdowns(obj, direction) {
|
|
||||||
if (obj.hasClass('drop' + direction)) { return; }
|
|
||||||
var reverse = ((direction === 'up') ? 'down' : 'up');
|
|
||||||
obj.removeClass('drop' + reverse).addClass('drop' + direction);
|
|
||||||
obj.prev().find('i').removeClass('ion-ios-arrow-' + reverse).addClass('ion-ios-arrow-' + direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
function position_navbar() {
|
|
||||||
if ($(window).width() < 862) { // @screen-sm
|
|
||||||
$('.navbar-icon').removeClass('ion-ios-arrow-up').addClass('ion-ios-arrow-down');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($(window).scrollTop() > $('#navbar-anchor').offset().top) {
|
|
||||||
if (!is_navbar_attached) {
|
|
||||||
attach_navbar();
|
|
||||||
is_navbar_attached = true;
|
|
||||||
}
|
|
||||||
} else if (is_navbar_attached) {
|
|
||||||
detach_navbar();
|
|
||||||
is_navbar_attached = false;
|
|
||||||
}
|
|
||||||
$('.dropdown-menu').each(function() {
|
|
||||||
var direction = ($(this).height() + 10 < $('nav').offset().top - $(window).scrollTop()) ? 'up' : 'down';
|
|
||||||
flip_dropdowns($(this), direction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() { // https://css-tricks.com/snippets/jquery/smooth-scrolling/
|
|
||||||
$('a[href*=#]:not([href=#])').click(function() {
|
|
||||||
if ($(this).data('toggle') === 'collapse') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (location.pathname.replace(/^\//, '') === this.pathname.replace(/^\//, '')
|
|
||||||
&& location.hostname === this.hostname) {
|
|
||||||
var target = $(this.hash);
|
|
||||||
target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
|
|
||||||
if (target.length) {
|
|
||||||
$('html,body').animate({
|
|
||||||
scrollTop: target.offset().top
|
|
||||||
}, 1000);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('a[href="soon"]').click(function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
alert('Content coming soon, stand by!');
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).scroll(position_navbar);
|
|
|
@ -1,7 +0,0 @@
|
||||||
window.updateTitle = function (value) {
|
|
||||||
document.title = value + ' | TheOrangeOne';
|
|
||||||
};
|
|
||||||
|
|
||||||
if ($('.header h1').length) {
|
|
||||||
window.updateTitle($('.header h1').text());
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
@btn-srobo-color: @gray-lighter;
|
|
||||||
@btn-srobo-bg: #253571;
|
|
||||||
@btn-srobo-border: darken(@btn-srobo-bg, 5%);
|
|
||||||
.btn-srobo {
|
|
||||||
.button-variant(@btn-srobo-color; @btn-srobo-bg; @btn-srobo-border);
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
.image-panel {
|
|
||||||
background-attachment: fixed;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-position: center;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
|
@ -1,197 +0,0 @@
|
||||||
@blueprint-logo: url(http://ultimatestorytellingblueprint.com/wp-content/uploads/2014/07/Blueprint-Background1.jpg);
|
|
||||||
@container-inset: 35px;
|
|
||||||
|
|
||||||
body.index {
|
|
||||||
#header {
|
|
||||||
height: 100vh;
|
|
||||||
background: #232323;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: fixed;
|
|
||||||
.jumbotron {
|
|
||||||
margin-top: 19vh;
|
|
||||||
text-align: center;
|
|
||||||
background-color: transparent;
|
|
||||||
color: white;
|
|
||||||
padding-bottom: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
.box-shadow(initial);
|
|
||||||
h1 {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
p.scroll-icon {
|
|
||||||
margin-top: 45px;
|
|
||||||
}
|
|
||||||
i.icon {
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 30px;
|
|
||||||
.transition(color 0.4s);
|
|
||||||
&:hover {
|
|
||||||
color: #ff7f00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
height: 275px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#intro-text {
|
|
||||||
background-color: #333;
|
|
||||||
.container {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: @container-inset;
|
|
||||||
margin-bottom: @container-inset;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#twitter-feed {
|
|
||||||
text-align: center;
|
|
||||||
.container {
|
|
||||||
margin-top: @container-inset;
|
|
||||||
margin-bottom: @container-inset;
|
|
||||||
}
|
|
||||||
.twitter-timeline {
|
|
||||||
height: 65vh !important; // Because twitter uses inline styles.
|
|
||||||
.box-shadow(4px 5px 2px 0 rgba(0,0,0,0.4));
|
|
||||||
}
|
|
||||||
.twitter-icon {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 175px;
|
|
||||||
height: 65vh;
|
|
||||||
position: relative;
|
|
||||||
.icon {
|
|
||||||
position: absolute;
|
|
||||||
text-shadow: 4px 5px 2px rgba(0,0,0,0.4);
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
.transition(color 0.6s);
|
|
||||||
&:hover {
|
|
||||||
color: #55ACEE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#project-thumbnails {
|
|
||||||
background-image: @blueprint-logo;
|
|
||||||
background-size: cover;
|
|
||||||
background-attachment: fixed;
|
|
||||||
& > div > .row {
|
|
||||||
margin-top: @container-inset;
|
|
||||||
margin-bottom: @container-inset;
|
|
||||||
& > h1 {
|
|
||||||
color: white;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.thumbnail {
|
|
||||||
padding: 15px;
|
|
||||||
height: 275px;
|
|
||||||
text-align: center;
|
|
||||||
&.larger {
|
|
||||||
.inner {
|
|
||||||
position: relative;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
h1, h2 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.project h2 {
|
|
||||||
@media (min-width: @screen-sm-min) {
|
|
||||||
font-size: 30px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 45%;
|
|
||||||
}
|
|
||||||
h3 {
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
.caption {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @group project-images */
|
|
||||||
.project-image {
|
|
||||||
.wrapper {
|
|
||||||
color: white;
|
|
||||||
margin: 0 auto;
|
|
||||||
cursor: pointer;
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
.box-shadow(0px 4px 5px 2px rgba(0,0,0,0.7));
|
|
||||||
|
|
||||||
&:hover .project {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: @screen-xs-max) {
|
|
||||||
&, & .project {
|
|
||||||
height: 240px;
|
|
||||||
width: 240px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: @screen-sm-min) {
|
|
||||||
&, & .project {
|
|
||||||
height: 210px;
|
|
||||||
width: 210px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: @screen-md-min) {
|
|
||||||
&, & .project {
|
|
||||||
height: 280px;
|
|
||||||
width: 280px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .project {
|
|
||||||
padding: 8%;
|
|
||||||
.flexbox();
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
.transition(~"opacity 0.5s ease-in");
|
|
||||||
opacity: 0;
|
|
||||||
background-color: rgba(0,0,0,0.7);
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
@media screen and (max-width: @screen-xs-max) {
|
|
||||||
p, a.btn {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-bottom: 13px;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 25px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: @screen-sm-min) {
|
|
||||||
p, a.btn {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 21px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@media screen and (min-width: @screen-md-min) {
|
|
||||||
p, a.btn {
|
|
||||||
font-size: 17px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
h4 {
|
|
||||||
font-size: 25px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* @end project-images */
|
|
|
@ -1,136 +0,0 @@
|
||||||
#navigation {
|
|
||||||
@media (min-width: @screen-xs-max) {
|
|
||||||
.flexbox();
|
|
||||||
justify-content: center;
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
& a {
|
|
||||||
.transition(~"ease-in-out .3s color");
|
|
||||||
}
|
|
||||||
& > li {
|
|
||||||
max-height: @navbar-height;
|
|
||||||
@media (min-width: @screen-md-min) {
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dropdown-toggle, & > li > a {
|
|
||||||
font-size: 19px;
|
|
||||||
max-height: @navbar-height;
|
|
||||||
}
|
|
||||||
.dropdown-menu {
|
|
||||||
.transition(~"ease-in-out .2s all");
|
|
||||||
@media (min-width: @screen-sm) {
|
|
||||||
right: auto;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, 0);
|
|
||||||
}
|
|
||||||
& > li > a {
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
&.dropup {
|
|
||||||
bottom: 100%;
|
|
||||||
top: initial;
|
|
||||||
}
|
|
||||||
&.dropdown {
|
|
||||||
top: 100%;
|
|
||||||
bottom: initial;
|
|
||||||
}
|
|
||||||
.open & {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.navbar {
|
|
||||||
border-radius: 0;
|
|
||||||
border: none;
|
|
||||||
.home-button a {
|
|
||||||
.transition(~"ease-in-out .3 all");
|
|
||||||
}
|
|
||||||
@media (min-height: @screen-sm-min) {
|
|
||||||
max-height: @navbar-height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-nav.nav {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-toggle {
|
|
||||||
right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#navbar-container {
|
|
||||||
margin: 0;
|
|
||||||
z-index: 10000;
|
|
||||||
@media (max-width: @screen-xs-max) {
|
|
||||||
width: 96%;
|
|
||||||
left: 2%;
|
|
||||||
}
|
|
||||||
@media (min-width: @screen-sm-min) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
i + *, .glyphicon + * {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
& .container-fluid{
|
|
||||||
padding-right: 0;
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
&.align {
|
|
||||||
@media (min-width: @screen-sm) {
|
|
||||||
position: initial;
|
|
||||||
height: @navbar-height;
|
|
||||||
margin-top: -50px;
|
|
||||||
}
|
|
||||||
@media (max-width: @screen-sm) {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
width: 95%;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#navbar-anchor {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: @screen-xs-max) {
|
|
||||||
.home-button {
|
|
||||||
margin-right: 15px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#navbar {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-brand {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stick-top {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
z-index: 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
// /* @group Header Navbar*/
|
|
||||||
// @media (min-width: @screen-sm-min) {
|
|
||||||
// .header {
|
|
||||||
// #navbar-container {
|
|
||||||
// margin: 0 auto;
|
|
||||||
// &:not(.stick-top) {
|
|
||||||
// position: initial;
|
|
||||||
// }
|
|
||||||
// &.stick-top {
|
|
||||||
// width: 100%;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// /* @end Header Navbar*/
|
|
|
@ -1,233 +0,0 @@
|
||||||
@import "node_modules/less-mixins/index.less";
|
|
||||||
@import "node_modules/bootstrap/less/bootstrap";
|
|
||||||
@import "node_modules/ionicons/less/ionicons";
|
|
||||||
@import "node_modules/bootstrap/less/theme";
|
|
||||||
@import url(https://fonts.googleapis.com/css?family=Roboto:400,300,400italic,700);
|
|
||||||
@import (inline) "node_modules/animate.css/animate.css";
|
|
||||||
@import 'homepage';
|
|
||||||
@import 'variables';
|
|
||||||
@import 'navbar';
|
|
||||||
@import 'assets';
|
|
||||||
@import 'components';
|
|
||||||
|
|
||||||
/* @group Global */
|
|
||||||
.btn {
|
|
||||||
.transition(~"ease-in-out .4s background-color");
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body{
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
a {
|
|
||||||
.transition(color 0.4s);
|
|
||||||
}
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol , ul{
|
|
||||||
font-size: inherit;
|
|
||||||
& > li {
|
|
||||||
font-size: inherit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
font-size: @font-size-base + 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.no-color-change {
|
|
||||||
color: inherit;
|
|
||||||
&:hover {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @end Global */
|
|
||||||
|
|
||||||
/* @group Functional */
|
|
||||||
|
|
||||||
.center-align {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-text {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mega-icon {
|
|
||||||
font-size: 110px;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
& ~ div.caption {
|
|
||||||
padding-top: 0;
|
|
||||||
h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @end Functional */
|
|
||||||
|
|
||||||
.content-wrapper {
|
|
||||||
min-height: %(~"calc(100% - %d)", @footer-height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @group Footer */
|
|
||||||
@footer-height: 250px;
|
|
||||||
footer {
|
|
||||||
height: @footer-height;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 30px;
|
|
||||||
color: white;
|
|
||||||
background-color: #232323;
|
|
||||||
a {
|
|
||||||
color: #ff7f00;
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.bar-links {
|
|
||||||
margin: 0 7px;
|
|
||||||
}
|
|
||||||
.powered-by {
|
|
||||||
font-size: @font-size-h6;
|
|
||||||
}
|
|
||||||
.social {
|
|
||||||
font-size: 23px;
|
|
||||||
margin: 0;
|
|
||||||
padding: 7px;
|
|
||||||
a {
|
|
||||||
margin: 0 4px;
|
|
||||||
color: white;
|
|
||||||
&:hover {
|
|
||||||
color: #ff7f00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.ci-badge {
|
|
||||||
height: @font-size-h4;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* @end Footer */
|
|
||||||
|
|
||||||
/* @group 404 */
|
|
||||||
.four-o-four {
|
|
||||||
.header {
|
|
||||||
height: 15vh;
|
|
||||||
font-size: 35px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.image {
|
|
||||||
background: url(../img/ninjas.png);
|
|
||||||
background-size: cover;
|
|
||||||
background-position: center;
|
|
||||||
height: 70vh;
|
|
||||||
}
|
|
||||||
.message {
|
|
||||||
height: 15vh;
|
|
||||||
color: white;
|
|
||||||
background-color: #232323;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.move-on {
|
|
||||||
text-align: center;
|
|
||||||
background-color: #333;
|
|
||||||
color: white;
|
|
||||||
height: 5vh;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* @end 404 */
|
|
||||||
|
|
||||||
/* @group no-js */
|
|
||||||
.no-js {
|
|
||||||
.header {
|
|
||||||
background-color: #232323;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
#ie-scare {
|
|
||||||
height: 150px;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* @end no-js*/
|
|
||||||
|
|
||||||
/* @group content base */
|
|
||||||
.jumbotron.header {
|
|
||||||
padding-top: 10px;
|
|
||||||
padding-bottom: 0;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
background-color: @body-bg;
|
|
||||||
@media (max-width: @screen-xs-max) {
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin: 10px 0;
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 60px;
|
|
||||||
}
|
|
||||||
.image {
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
width: 100%;
|
|
||||||
height: 225px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#breadcrumbs {
|
|
||||||
text-align: right;
|
|
||||||
.breadcrumb {
|
|
||||||
background: transparent;
|
|
||||||
font-size: @font-size-h4;
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 0;
|
|
||||||
li::before {
|
|
||||||
content: "/";
|
|
||||||
padding: 0 3px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
.transition(color 0.4s);
|
|
||||||
color: #333;
|
|
||||||
&:hover {
|
|
||||||
color: #ff7f00;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* @end content base */
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
margin-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
|
||||||
width: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gh-card iframe {
|
|
||||||
.box-shadow(0px 4px 4px 3px rgba(0,0,0,0.6));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @group X-Large */
|
|
||||||
|
|
||||||
@media (min-width: 1500px) {
|
|
||||||
.container {
|
|
||||||
width: 1400px + @grid-gutter-width;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @end X-Large */
|
|
|
@ -1,383 +0,0 @@
|
||||||
@gray-base: #000;
|
|
||||||
@gray-darker: lighten(@gray-base, 13.5%);
|
|
||||||
@gray-dark: lighten(@gray-base, 20%);
|
|
||||||
@gray: lighten(@gray-base, 33.5%);
|
|
||||||
@gray-light: lighten(@gray-base, 46.7%);
|
|
||||||
@gray-lighter: lighten(@gray-base, 93.5%);
|
|
||||||
@brand-primary: #ff7f00;
|
|
||||||
@brand-success: #5cb85c;
|
|
||||||
@brand-info: #5bc0de;
|
|
||||||
@brand-warning: #f0ad4e;
|
|
||||||
@brand-danger: #d9534f;
|
|
||||||
@body-bg: #fff;
|
|
||||||
@text-color: @gray-dark;
|
|
||||||
@link-color: @brand-primary;
|
|
||||||
@link-hover-color: darken(@link-color, 15%);
|
|
||||||
@link-hover-decoration: underline;
|
|
||||||
@font-family-sans-serif: 'Roboto', sans-serif;
|
|
||||||
@font-family-serif: 'Roboto';
|
|
||||||
@font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;
|
|
||||||
@font-family-base: @font-family-sans-serif;
|
|
||||||
@font-size-base: 18px;
|
|
||||||
@font-size-large: ceil((@font-size-base * 1.25));
|
|
||||||
@font-size-small: ceil((@font-size-base * 0.85));
|
|
||||||
@font-size-h1: floor((@font-size-base * 2.6));
|
|
||||||
@font-size-h2: floor((@font-size-base * 2.15));
|
|
||||||
@font-size-h3: ceil((@font-size-base * 1.7));
|
|
||||||
@font-size-h4: ceil((@font-size-base * 1.25));
|
|
||||||
@font-size-h5: @font-size-base;
|
|
||||||
@font-size-h6: ceil((@font-size-base * 0.85));
|
|
||||||
@line-height-base: 1.428571429;
|
|
||||||
@line-height-computed: floor((@font-size-base * @line-height-base));
|
|
||||||
@headings-font-family: inherit;
|
|
||||||
@headings-font-weight: 300;
|
|
||||||
@headings-line-height: 1.1;
|
|
||||||
@headings-color: inherit;
|
|
||||||
@icon-font-path: "../fonts/";
|
|
||||||
@icon-font-name: "glyphicons-halflings-regular";
|
|
||||||
@icon-font-svg-id: "glyphicons_halflingsregular";
|
|
||||||
@padding-base-vertical: 6px;
|
|
||||||
@padding-base-horizontal: 12px;
|
|
||||||
@padding-large-vertical: 10px;
|
|
||||||
@padding-large-horizontal: 16px;
|
|
||||||
@padding-small-vertical: 5px;
|
|
||||||
@padding-small-horizontal: 10px;
|
|
||||||
@padding-xs-vertical: 1px;
|
|
||||||
@padding-xs-horizontal: 5px;
|
|
||||||
@line-height-large: 1.3333333;
|
|
||||||
@line-height-small: 1.5;
|
|
||||||
@border-radius-base: 4px;
|
|
||||||
@border-radius-large: 6px;
|
|
||||||
@border-radius-small: 3px;
|
|
||||||
@component-active-color: #fff;
|
|
||||||
@component-active-bg: @brand-primary;
|
|
||||||
@caret-width-base: 4px;
|
|
||||||
@caret-width-large: 5px;
|
|
||||||
@table-cell-padding: 8px;
|
|
||||||
@table-condensed-cell-padding: 5px;
|
|
||||||
@table-bg: transparent;
|
|
||||||
@table-bg-accent: #f9f9f9;
|
|
||||||
@table-bg-hover: #f5f5f5;
|
|
||||||
@table-bg-active: @table-bg-hover;
|
|
||||||
@table-border-color: #ddd;
|
|
||||||
@btn-font-weight: normal;
|
|
||||||
@btn-default-color: #333;
|
|
||||||
@btn-default-bg: #fff;
|
|
||||||
@btn-default-border: #ccc;
|
|
||||||
@btn-primary-color: #fff;
|
|
||||||
@btn-primary-bg: @brand-primary;
|
|
||||||
@btn-primary-border: darken(@btn-primary-bg, 5%);
|
|
||||||
@btn-success-color: #fff;
|
|
||||||
@btn-success-bg: @brand-success;
|
|
||||||
@btn-success-border: darken(@btn-success-bg, 5%);
|
|
||||||
@btn-info-color: #fff;
|
|
||||||
@btn-info-bg: @brand-info;
|
|
||||||
@btn-info-border: darken(@btn-info-bg, 5%);
|
|
||||||
@btn-warning-color: #fff;
|
|
||||||
@btn-warning-bg: @brand-warning;
|
|
||||||
@btn-warning-border: darken(@btn-warning-bg, 5%);
|
|
||||||
@btn-danger-color: #fff;
|
|
||||||
@btn-danger-bg: @brand-danger;
|
|
||||||
@btn-danger-border: darken(@btn-danger-bg, 5%);
|
|
||||||
@btn-link-disabled-color: @gray-light;
|
|
||||||
@input-bg: #fff;
|
|
||||||
@input-bg-disabled: @gray-lighter;
|
|
||||||
@input-color: @gray;
|
|
||||||
@input-border: #ccc;
|
|
||||||
@input-border-radius: @border-radius-base;
|
|
||||||
@input-border-radius-large: @border-radius-large;
|
|
||||||
@input-border-radius-small: @border-radius-small;
|
|
||||||
@input-border-focus: #66afe9;
|
|
||||||
@input-color-placeholder: #999;
|
|
||||||
@input-height-base: (@line-height-computed + (@padding-base-vertical * 2) + 2);
|
|
||||||
@input-height-large: (ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2);
|
|
||||||
@input-height-small: (floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2);
|
|
||||||
@form-group-margin-bottom: 15px;
|
|
||||||
@legend-color: @gray-dark;
|
|
||||||
@legend-border-color: #e5e5e5;
|
|
||||||
@input-group-addon-bg: @gray-lighter;
|
|
||||||
@input-group-addon-border-color: @input-border;
|
|
||||||
@cursor-disabled: not-allowed;
|
|
||||||
@dropdown-bg: #fff;
|
|
||||||
@dropdown-border: rgba(0,0,0,.15);
|
|
||||||
@dropdown-fallback-border: #ccc;
|
|
||||||
@dropdown-divider-bg: #e5e5e5;
|
|
||||||
@dropdown-link-color: @gray-dark;
|
|
||||||
@dropdown-link-hover-color: #fff;
|
|
||||||
@dropdown-link-hover-bg: darken(@gray-dark, 30%);
|
|
||||||
@dropdown-link-active-color: @component-active-color;
|
|
||||||
@dropdown-link-active-bg: @component-active-bg;
|
|
||||||
@dropdown-link-disabled-color: @gray-light;
|
|
||||||
@dropdown-header-color: @gray-light;
|
|
||||||
@dropdown-caret-color: #000;
|
|
||||||
@zindex-navbar: 1000;
|
|
||||||
@zindex-dropdown: 1000;
|
|
||||||
@zindex-popover: 1060;
|
|
||||||
@zindex-tooltip: 1070;
|
|
||||||
@zindex-navbar-fixed: 1030;
|
|
||||||
@zindex-modal-background: 1040;
|
|
||||||
@zindex-modal: 1050;
|
|
||||||
@screen-xs: 480px;
|
|
||||||
@screen-xs-min: @screen-xs;
|
|
||||||
@screen-phone: @screen-xs-min;
|
|
||||||
@screen-sm: 768px;
|
|
||||||
@screen-sm-min: @screen-sm;
|
|
||||||
@screen-tablet: @screen-sm-min;
|
|
||||||
@screen-md: 992px;
|
|
||||||
@screen-md-min: @screen-md;
|
|
||||||
@screen-desktop: @screen-md-min;
|
|
||||||
@screen-lg: 1200px;
|
|
||||||
@screen-lg-min: @screen-lg;
|
|
||||||
@screen-lg-desktop: @screen-lg-min;
|
|
||||||
@screen-xs-max: (@screen-sm-min - 1);
|
|
||||||
@screen-sm-max: (@screen-md-min - 1);
|
|
||||||
@screen-md-max: (@screen-lg-min - 1);
|
|
||||||
@grid-columns: 12;
|
|
||||||
@grid-gutter-width: 30px;
|
|
||||||
@grid-float-breakpoint: @screen-sm-min;
|
|
||||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
|
||||||
@container-tablet: (720px + @grid-gutter-width);
|
|
||||||
@container-sm: @container-tablet;
|
|
||||||
@container-desktop: (940px + @grid-gutter-width);
|
|
||||||
@container-md: @container-desktop;
|
|
||||||
@container-large-desktop: (1140px + @grid-gutter-width);
|
|
||||||
@container-lg: @container-large-desktop;
|
|
||||||
@navbar-height: 50px;
|
|
||||||
@navbar-margin-bottom: @line-height-computed;
|
|
||||||
@navbar-border-radius: @border-radius-base;
|
|
||||||
@navbar-padding-horizontal: floor((@grid-gutter-width / 2));
|
|
||||||
@navbar-padding-vertical: ((@navbar-height - @line-height-computed) / 2);
|
|
||||||
@navbar-collapse-max-height: 340px;
|
|
||||||
@navbar-default-color: #777;
|
|
||||||
@navbar-default-bg: #f8f8f8;
|
|
||||||
@navbar-default-border: darken(@navbar-default-bg, 6.5%);
|
|
||||||
@navbar-default-link-color: #777;
|
|
||||||
@navbar-default-link-hover-color: #333;
|
|
||||||
@navbar-default-link-hover-bg: transparent;
|
|
||||||
@navbar-default-link-active-color: #555;
|
|
||||||
@navbar-default-link-active-bg: darken(@navbar-default-bg, 6.5%);
|
|
||||||
@navbar-default-link-disabled-color: #ccc;
|
|
||||||
@navbar-default-link-disabled-bg: transparent;
|
|
||||||
@navbar-default-brand-color: @navbar-default-link-color;
|
|
||||||
@navbar-default-brand-hover-color: darken(@navbar-default-brand-color, 10%);
|
|
||||||
@navbar-default-brand-hover-bg: transparent;
|
|
||||||
@navbar-default-toggle-hover-bg: #ddd;
|
|
||||||
@navbar-default-toggle-icon-bar-bg: #888;
|
|
||||||
@navbar-default-toggle-border-color: #ddd;
|
|
||||||
@navbar-inverse-color: lighten(@gray-light, 15%);
|
|
||||||
@navbar-inverse-bg: #222;
|
|
||||||
@navbar-inverse-border: darken(@navbar-inverse-bg, 10%);
|
|
||||||
@navbar-inverse-link-color: lighten(@gray-light, 15%);
|
|
||||||
@navbar-inverse-link-hover-color: #fff;
|
|
||||||
@navbar-inverse-link-hover-bg: transparent;
|
|
||||||
@navbar-inverse-link-active-color: @navbar-inverse-link-hover-color;
|
|
||||||
@navbar-inverse-link-active-bg: darken(@navbar-inverse-bg, 10%);
|
|
||||||
@navbar-inverse-link-disabled-color: #444;
|
|
||||||
@navbar-inverse-link-disabled-bg: transparent;
|
|
||||||
@navbar-inverse-brand-color: @navbar-inverse-link-color;
|
|
||||||
@navbar-inverse-brand-hover-color: #fff;
|
|
||||||
@navbar-inverse-brand-hover-bg: transparent;
|
|
||||||
@navbar-inverse-toggle-hover-bg: #333;
|
|
||||||
@navbar-inverse-toggle-icon-bar-bg: #fff;
|
|
||||||
@navbar-inverse-toggle-border-color: #333;
|
|
||||||
//=== Shared nav styles
|
|
||||||
@nav-link-padding: 10px 15px;
|
|
||||||
@nav-link-hover-bg: @gray-lighter;
|
|
||||||
@nav-disabled-link-color: @gray-light;
|
|
||||||
@nav-disabled-link-hover-color: @gray-light;
|
|
||||||
@nav-tabs-border-color: #ddd;
|
|
||||||
@nav-tabs-link-hover-border-color: @gray-lighter;
|
|
||||||
@nav-tabs-active-link-hover-bg: @body-bg;
|
|
||||||
@nav-tabs-active-link-hover-color: @gray;
|
|
||||||
@nav-tabs-active-link-hover-border-color: #ddd;
|
|
||||||
@nav-tabs-justified-link-border-color: #ddd;
|
|
||||||
@nav-tabs-justified-active-link-border-color: @body-bg;
|
|
||||||
@nav-pills-border-radius: @border-radius-base;
|
|
||||||
@nav-pills-active-link-hover-bg: @component-active-bg;
|
|
||||||
@nav-pills-active-link-hover-color: @component-active-color;
|
|
||||||
@pagination-color: @link-color;
|
|
||||||
@pagination-bg: #fff;
|
|
||||||
@pagination-border: #ddd;
|
|
||||||
@pagination-hover-color: @link-hover-color;
|
|
||||||
@pagination-hover-bg: @gray-lighter;
|
|
||||||
@pagination-hover-border: #ddd;
|
|
||||||
@pagination-active-color: #fff;
|
|
||||||
@pagination-active-bg: @brand-primary;
|
|
||||||
@pagination-active-border: @brand-primary;
|
|
||||||
@pagination-disabled-color: @gray-light;
|
|
||||||
@pagination-disabled-bg: #fff;
|
|
||||||
@pagination-disabled-border: #ddd;
|
|
||||||
@pager-bg: @pagination-bg;
|
|
||||||
@pager-border: @pagination-border;
|
|
||||||
@pager-border-radius: 15px;
|
|
||||||
@pager-hover-bg: @pagination-hover-bg;
|
|
||||||
@pager-active-bg: @pagination-active-bg;
|
|
||||||
@pager-active-color: @pagination-active-color;
|
|
||||||
@pager-disabled-color: @pagination-disabled-color;
|
|
||||||
@jumbotron-padding: 30px;
|
|
||||||
@jumbotron-color: inherit;
|
|
||||||
@jumbotron-bg: @gray-lighter;
|
|
||||||
@jumbotron-heading-color: inherit;
|
|
||||||
@jumbotron-font-size: ceil((@font-size-base * 1.5));
|
|
||||||
@state-success-text: #3c763d;
|
|
||||||
@state-success-bg: #dff0d8;
|
|
||||||
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
|
|
||||||
@state-info-text: #31708f;
|
|
||||||
@state-info-bg: #d9edf7;
|
|
||||||
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
|
|
||||||
@state-warning-text: #8a6d3b;
|
|
||||||
@state-warning-bg: #fcf8e3;
|
|
||||||
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
|
|
||||||
@state-danger-text: #a94442;
|
|
||||||
@state-danger-bg: #f2dede;
|
|
||||||
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
|
|
||||||
@tooltip-max-width: 200px;
|
|
||||||
@tooltip-color: #fff;
|
|
||||||
@tooltip-bg: #000;
|
|
||||||
@tooltip-opacity: .9;
|
|
||||||
@tooltip-arrow-width: 5px;
|
|
||||||
@tooltip-arrow-color: @tooltip-bg;
|
|
||||||
@popover-bg: #fff;
|
|
||||||
@popover-max-width: 276px;
|
|
||||||
@popover-border-color: rgba(0,0,0,.2);
|
|
||||||
@popover-fallback-border-color: #ccc;
|
|
||||||
@popover-title-bg: darken(@popover-bg, 3%);
|
|
||||||
@popover-arrow-width: 10px;
|
|
||||||
@popover-arrow-color: @popover-bg;
|
|
||||||
@popover-arrow-outer-width: (@popover-arrow-width + 1);
|
|
||||||
@popover-arrow-outer-color: fadein(@popover-border-color, 5%);
|
|
||||||
@popover-arrow-outer-fallback-color: darken(@popover-fallback-border-color, 20%);
|
|
||||||
@label-default-bg: @gray-light;
|
|
||||||
@label-primary-bg: @brand-primary;
|
|
||||||
@label-success-bg: @brand-success;
|
|
||||||
@label-info-bg: @brand-info;
|
|
||||||
@label-warning-bg: @brand-warning;
|
|
||||||
@label-danger-bg: @brand-danger;
|
|
||||||
@label-color: #fff;
|
|
||||||
@label-link-hover-color: #fff;
|
|
||||||
@modal-inner-padding: 15px;
|
|
||||||
@modal-title-padding: 15px;
|
|
||||||
@modal-title-line-height: @line-height-base;
|
|
||||||
@modal-content-bg: #fff;
|
|
||||||
@modal-content-border-color: rgba(0,0,0,.2);
|
|
||||||
@modal-content-fallback-border-color: #999;
|
|
||||||
@modal-backdrop-bg: #000;
|
|
||||||
@modal-backdrop-opacity: .5;
|
|
||||||
@modal-header-border-color: #e5e5e5;
|
|
||||||
@modal-footer-border-color: @modal-header-border-color;
|
|
||||||
@modal-lg: 900px;
|
|
||||||
@modal-md: 600px;
|
|
||||||
@modal-sm: 300px;
|
|
||||||
@alert-padding: 15px;
|
|
||||||
@alert-border-radius: @border-radius-base;
|
|
||||||
@alert-link-font-weight: bold;
|
|
||||||
@alert-success-bg: @state-success-bg;
|
|
||||||
@alert-success-text: @state-success-text;
|
|
||||||
@alert-success-border: @state-success-border;
|
|
||||||
@alert-info-bg: @state-info-bg;
|
|
||||||
@alert-info-text: @state-info-text;
|
|
||||||
@alert-info-border: @state-info-border;
|
|
||||||
@alert-warning-bg: @state-warning-bg;
|
|
||||||
@alert-warning-text: @state-warning-text;
|
|
||||||
@alert-warning-border: @state-warning-border;
|
|
||||||
@alert-danger-bg: @state-danger-bg;
|
|
||||||
@alert-danger-text: @state-danger-text;
|
|
||||||
@alert-danger-border: @state-danger-border;
|
|
||||||
@progress-bg: #f5f5f5;
|
|
||||||
@progress-bar-color: #fff;
|
|
||||||
@progress-border-radius: @border-radius-base;
|
|
||||||
@progress-bar-bg: @brand-primary;
|
|
||||||
@progress-bar-success-bg: @brand-success;
|
|
||||||
@progress-bar-warning-bg: @brand-warning;
|
|
||||||
@progress-bar-danger-bg: @brand-danger;
|
|
||||||
@progress-bar-info-bg: @brand-info;
|
|
||||||
@list-group-bg: #fff;
|
|
||||||
@list-group-border: #ddd;
|
|
||||||
@list-group-border-radius: @border-radius-base;
|
|
||||||
@list-group-hover-bg: #f5f5f5;
|
|
||||||
@list-group-active-color: @component-active-color;
|
|
||||||
@list-group-active-bg: @component-active-bg;
|
|
||||||
@list-group-active-border: @list-group-active-bg;
|
|
||||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
|
||||||
@list-group-disabled-color: @gray-light;
|
|
||||||
@list-group-disabled-bg: @gray-lighter;
|
|
||||||
@list-group-disabled-text-color: @list-group-disabled-color;
|
|
||||||
@list-group-link-color: #555;
|
|
||||||
@list-group-link-hover-color: @list-group-link-color;
|
|
||||||
@list-group-link-heading-color: #333;
|
|
||||||
@panel-bg: #fff;
|
|
||||||
@panel-body-padding: 15px;
|
|
||||||
@panel-heading-padding: 10px 15px;
|
|
||||||
@panel-footer-padding: @panel-heading-padding;
|
|
||||||
@panel-border-radius: @border-radius-base;
|
|
||||||
@panel-inner-border: #ddd;
|
|
||||||
@panel-footer-bg: #f5f5f5;
|
|
||||||
@panel-default-text: @gray-dark;
|
|
||||||
@panel-default-border: #ddd;
|
|
||||||
@panel-default-heading-bg: #f5f5f5;
|
|
||||||
@panel-primary-text: #fff;
|
|
||||||
@panel-primary-border: @brand-primary;
|
|
||||||
@panel-primary-heading-bg: @brand-primary;
|
|
||||||
@panel-success-text: @state-success-text;
|
|
||||||
@panel-success-border: @state-success-border;
|
|
||||||
@panel-success-heading-bg: @state-success-bg;
|
|
||||||
@panel-info-text: @state-info-text;
|
|
||||||
@panel-info-border: @state-info-border;
|
|
||||||
@panel-info-heading-bg: @state-info-bg;
|
|
||||||
@panel-warning-text: @state-warning-text;
|
|
||||||
@panel-warning-border: @state-warning-border;
|
|
||||||
@panel-warning-heading-bg: @state-warning-bg;
|
|
||||||
@panel-danger-text: @state-danger-text;
|
|
||||||
@panel-danger-border: @state-danger-border;
|
|
||||||
@panel-danger-heading-bg: @state-danger-bg;
|
|
||||||
@thumbnail-padding: 4px;
|
|
||||||
@thumbnail-bg: @body-bg;
|
|
||||||
@thumbnail-border: #ddd;
|
|
||||||
@thumbnail-border-radius: @border-radius-base;
|
|
||||||
@thumbnail-caption-color: @text-color;
|
|
||||||
@thumbnail-caption-padding: 9px;
|
|
||||||
@well-bg: #f5f5f5;
|
|
||||||
@well-border: darken(@well-bg, 7%);
|
|
||||||
@badge-color: #fff;
|
|
||||||
@badge-link-hover-color: #fff;
|
|
||||||
@badge-bg: @gray-light;
|
|
||||||
@badge-active-color: @link-color;
|
|
||||||
@badge-active-bg: #fff;
|
|
||||||
@badge-font-weight: bold;
|
|
||||||
@badge-line-height: 1;
|
|
||||||
@badge-border-radius: 10px;
|
|
||||||
@breadcrumb-padding-vertical: 8px;
|
|
||||||
@breadcrumb-padding-horizontal: 15px;
|
|
||||||
@breadcrumb-bg: #f5f5f5;
|
|
||||||
@breadcrumb-color: #ccc;
|
|
||||||
@breadcrumb-active-color: @gray-light;
|
|
||||||
@breadcrumb-separator: "/";
|
|
||||||
@carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6);
|
|
||||||
@carousel-control-color: #fff;
|
|
||||||
@carousel-control-width: 15%;
|
|
||||||
@carousel-control-opacity: .5;
|
|
||||||
@carousel-control-font-size: 20px;
|
|
||||||
@carousel-indicator-active-bg: #fff;
|
|
||||||
@carousel-indicator-border-color: #fff;
|
|
||||||
@carousel-caption-color: #fff;
|
|
||||||
@close-font-weight: bold;
|
|
||||||
@close-color: #000;
|
|
||||||
@close-text-shadow: 0 1px 0 #fff;
|
|
||||||
@code-color: #c7254e;
|
|
||||||
@code-bg: #f9f2f4;
|
|
||||||
@kbd-color: #fff;
|
|
||||||
@kbd-bg: #333;
|
|
||||||
@pre-bg: #f5f5f5;
|
|
||||||
@pre-color: @gray-dark;
|
|
||||||
@pre-border-color: #ccc;
|
|
||||||
@pre-scrollable-max-height: 340px;
|
|
||||||
@component-offset-horizontal: 180px;
|
|
||||||
@text-muted: @gray-light;
|
|
||||||
@abbr-border-color: @gray-light;
|
|
||||||
@headings-small-color: @gray-light;
|
|
||||||
@blockquote-small-color: @gray-light;
|
|
||||||
@blockquote-font-size: (@font-size-base * 1.25);
|
|
||||||
@blockquote-border-color: @gray-lighter;
|
|
||||||
@page-header-border-color: @gray-lighter;
|
|
||||||
@dl-horizontal-offset: @component-offset-horizontal;
|
|
||||||
@hr-border: @gray-lighter;
|
|
|
@ -1,41 +0,0 @@
|
||||||
{% extends 'content_base.html' %}
|
|
||||||
{% block pageTitle %}About all the things{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="thumbnail">
|
|
||||||
<p class="center-text mega-icon">
|
|
||||||
<span class="glyphicon glyphicon-hdd" aria-hidden="true"></span>
|
|
||||||
</p>
|
|
||||||
<div class="caption">
|
|
||||||
<h3>About Website</h3>
|
|
||||||
<p>
|
|
||||||
Some info about my website, which clearly works well as you're using it right now!
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="/about/website/" class="btn btn-primary btn-block">More Info</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6">
|
|
||||||
<div class="thumbnail">
|
|
||||||
<p class="center-text mega-icon">
|
|
||||||
<span class="glyphicon glyphicon-user" aria-hidden="true"></span>
|
|
||||||
</p>
|
|
||||||
<div class="caption">
|
|
||||||
<h3>About Me</h3>
|
|
||||||
<p>
|
|
||||||
Some info about me. Although not very much. Because Privacy!
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<a href="/about/me/" class="btn btn-primary btn-block">More Info</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
|
@ -1,19 +0,0 @@
|
||||||
{% extends 'content_base.html' %}
|
|
||||||
|
|
||||||
{% block pageTitle %}About Me{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<p>Stuff</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-4 gh-card">
|
|
||||||
<div data-theme="medium" data-github="RealOrangeOne" class="github-card"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script async defer src="//cdn.jsdelivr.net/github-cards/latest/widget.js"></script>
|
|
||||||
{% endblock %}
|
|
|
@ -1,14 +0,0 @@
|
||||||
# About my website
|
|
||||||
My website is the culmination of all my knowledge, compiled into 1 place. It not only contains all my projects, but it in itself is a project. Making sure this website works properly is a tall order, especially considering it's self hosted.
|
|
||||||
|
|
||||||
## The Website
|
|
||||||
The website itself is written in python, using the Django framework, and a SQLite database. For what I need it's more than overkill, but hey, why not!
|
|
||||||
|
|
||||||
I went with the Django framework because it's what I use with at work, as well as the fact it's simple, clean and easy. It also allows for some server side assets, eg blogging.
|
|
||||||
|
|
||||||
The only reason I have a database is because certain sections require it. For this reason I went with SQLite, because it's really lightweight and simple.
|
|
||||||
|
|
||||||
## The server
|
|
||||||
The website is hosted on my UK VPS. Previous versions have been hosted on 1&1 and MyWindowsHosting.
|
|
||||||
|
|
||||||
The Django application itself is served using waitress. This get's it's port from a custom reverse proxy allowing me to host multiple sites on a single server easily. This is the same one I use for local development. The main web-facing server is nginx, because it's simple to setup, and damn fast!
|
|
|
@ -1,63 +0,0 @@
|
||||||
{% load staticfiles %}
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>{{ html_title }} | TheOrangeOne</title>
|
|
||||||
<meta chatset='utf-8' />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
|
||||||
<script type="text/javascript" src="{% static 'js/jquery.js' %}"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}"/>
|
|
||||||
<link rel="shortcut icon" href=""/>
|
|
||||||
</head>
|
|
||||||
<body class="{{ body_class }}">
|
|
||||||
<div class="content-wrapper">
|
|
||||||
{% block baseContent%}{% endblock %}
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<p class="social">
|
|
||||||
<a href="{{ links.twitter }}"><i class="icon ion-social-twitter"></i></a>
|
|
||||||
<a href="{{ links.instagram }}"><i class="icon ion-social-instagram-outline"></i></a>
|
|
||||||
<a href="{{ links.reddit }}"><i class="icon ion-social-reddit"></i></a>
|
|
||||||
<a href="{{ links.youtube }}"><i class="icon ion-social-youtube"></i></a>
|
|
||||||
<a href="soon"><i class="icon ion-social-codepen"></i></a>
|
|
||||||
<a href="soon"><i class="icon ion-social-twitch-outline"></i></a>
|
|
||||||
<a href="{{ links.github }}"><i class="icon ion-social-octocat"></i></a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<p class="bar-links">
|
|
||||||
<a href="/about/">About</a> |
|
|
||||||
<a href="{{ links.github }}/theorangeone.net" target="_blank">View Source</a>
|
|
||||||
<p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12 powered-by">
|
|
||||||
<p>Powered by <a href="https://www.djangoproject.com/">Django</a>, <a href="https://clients.inceptionhosting.com/aff.php?aff=256">Inception Hosting</a>, and a whole heap of <a href="https://github.com/RealOrangeOne/theorangeone.net">Magic</a>!</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-xs-12">
|
|
||||||
<a href="https://circleci.com/gh/RealOrangeOne/theorangeone.net" target="_blank">
|
|
||||||
<img class="ci-badge" src="https://circleci.com/gh/RealOrangeOne/theorangeone.net.svg?style=svg"/>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static 'js/libs.js' %}"></script>
|
|
||||||
<script type="text/javascript" src="{% static 'js/app.js' %}"></script>
|
|
||||||
{% if js_redirect %}
|
|
||||||
<noscript>
|
|
||||||
<style> html, body { display:none; }</style>
|
|
||||||
<meta http-equiv="refresh" content="0.0;url=/no-js">
|
|
||||||
</noscript>
|
|
||||||
{% endif %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|