1
Fork 0

Merge pull request #7 from RealOrangeOne/contact-page

Add basic contact page
This commit is contained in:
Jake Howard 2017-02-11 21:19:02 +00:00 committed by GitHub
commit 34cab118cf
11 changed files with 124 additions and 39 deletions

View file

@ -49,13 +49,13 @@ accounts:
- fa-trello - fa-trello
freenode: freenode:
- Freenode IRC - Freenode
- TheOrangeOne - TheOrangeOne
- https://webchat.freenode.net/ - https://webchat.freenode.net/
- fa-rss - fa-rss
atomio: atomio:
- AtomIO Slack - Atom Slack
- TheOrangeOne - TheOrangeOne
- https://atomio.slack.com/ - https://atomio.slack.com/
- fa-slack - fa-slack
@ -69,14 +69,14 @@ accounts:
codepen: codepen:
- CodePen - CodePen
- TheOrangeOne - TheOrangeOne
- https://codepen.io/~{0}/ - https://codepen.io/{0}/
- fa-codepen - fa-codepen
npm: npm:
- npm - npm
- TheOrangeOne - TheOrangeOne
- https://www.npmjs.com/~{0}/ - https://www.npmjs.com/~{0}/
- fa-file-code-io - fa-file-code-o
footer_accounts: footer_accounts:
- github - github

View file

@ -1,6 +1,5 @@
<head> <head>
<meta name="slug" content="about" /> <meta name="title" content="About Me" />
<meta name="title" content="About" />
<meta name="no_container" content="true" /> <meta name="no_container" content="true" />
</head> </head>
<body> <body>
@ -14,36 +13,40 @@
</div> </div>
<script async defer src="//cdn.jsdelivr.net/github-cards/latest/widget.js"></script> <script async defer src="//cdn.jsdelivr.net/github-cards/latest/widget.js"></script>
</section> </section>
<section class="bg-primary" id="website"> <section class="bg-primary">
<div class="container"> <div class="container">
<h2>Website</h2> <h2 class="section-heading">Personal Data</h2>
<p> <p>
My website is the culmination of all my knowledge, compiled into 1 place. It not only contains all my projects, but is itself is a project. In the interest of privacy, there's very little personal information here.
</p> </p>
<p> <p>
The site is primarily built with <a href="http://getpelican.com">Pelican</a>, a static site generator. This allows me to write nice clean, <i>DRY</i> content, and have it come out as clean, minified HTML. The information that is here is eitther not personal enough to bother protecting, or has been selectively chosen as nothing bad.
</p>
<p>
The Javascript is built using <a href="http://browserify.org/">Browserify</a>, and the CSS is built using <a href="https://github.com/sass/node-sass">node-SCSS</a>. Both are run as a build step when pelican builds.
</p> </p>
</div> </div>
</section> </section>
<section id="server"> <section>
<div class="container"> <div class="container">
<h2>Server</h2> <h2 class="section-heading">Accounts</h2>
<p> <p>
The website is hosted on part of my dedicated server from <a href="https://www.soyoustart.com/en/">SoYouStart</a>, running an Ubuntu Server VM with <a href="http://dokku.viewdocs.io/dokku/">Dokku</a> installed. These are all the accounts I run, all to do with various things. Take a look!
</p>
<p>
The prebuilt static files are served using a <a href="https://github.com/RealOrangeOne/tstatic">custom Express server</a>, to make the site as fast and effective as possible.
</p> </p>
<div class="row text-center">
{% for key, account in ACCOUNTS.items() %}
<div class="col-sm-3 col-xs-6">
<div class="service-box account">
<a href="{{ account.url }}" class="no-underline">
<i class="fa fa-4x {{ account.icon }}"></i>
<h3>{{ account.site }}</h3>
</a>
</div>
</div>
{% endfor %}
</div>
</div> </div>
</section> </section>
<section class="text-center"> <section class="bg-primary text-center">
<div class="container"> <div class="container">
<div class="btn-group"> <a class="btn btn-primary-dark btn-xl" href="/contact/">Contact Me</a>
<a class="btn btn-github btn-xl" href="https://github.com/RealOrangeOne/theorangeone.net"><i class="fa fa-github fa-lg"></i> View Source</a>
</div>
</div> </div>
</section> </section>
</body> </body>

View file

@ -0,0 +1,27 @@
<head>
<meta name="title" content="Contact Me" />
<meta name="no_container" content="true" />
</head>
<body>
<section>
<div class="container">
<p>
The fastest way to contact me is through twitter. Just tag me or send me a message and I'll respond as soon as possible!
</p>
</div>
</section>
<section class="bg-primary">
<div class="container">
<h2 class="section-heading">Need something more formal?</h2>
<p>
If you need to contact me in a more formal capacity, send me an email! I aim to respond to all emails within 3 days.
</p>
</div>
</section>
<section class="text-center">
<div class="container">
<a class="btn btn-primary btn-xl margin protected-mailto" data-value="{{ CONTACT_EMAIL|encode_text }}">Send me an email!</a>
<a class="btn btn-primary btn-xl margin" href="{{ ACCOUNTS.twitter.url }}">View Twitter</a>
</div>
</section>
</body>

View file

@ -7,6 +7,7 @@ sys.path.insert(0, os.path.realpath('./'))
AUTHOR = "Jake Howard" AUTHOR = "Jake Howard"
SITENAME = "TheOrangeOne" SITENAME = "TheOrangeOne"
SITEURL = "https://theorangeone.net" SITEURL = "https://theorangeone.net"
CONTACT_EMAIL = "info@theorangeone.net"
PATH = 'content' PATH = 'content'
TIMEZONE = "Europe/London" TIMEZONE = "Europe/London"
DEFAULT_LANG = "en" DEFAULT_LANG = "en"
@ -111,7 +112,8 @@ JINJA_FILTERS = {
"limit": filters.limit, "limit": filters.limit,
"get_title": filters.get_title, "get_title": filters.get_title,
"get_html_title": filters.get_html_title, "get_html_title": filters.get_html_title,
"get_image": filters.get_image "get_image": filters.get_image,
"encode_text": filters.encode_text
} }
JINJA_ENVIRONMENT = { JINJA_ENVIRONMENT = {

View file

@ -39,3 +39,7 @@ def get_html_title(instance):
def get_image(instance): def get_image(instance):
return get_attribute(instance, 'image') or (hasattr(instance, 'page') and get_attribute(instance.page, 'name')) or '' return get_attribute(instance, 'image') or (hasattr(instance, 'page') and get_attribute(instance.page, 'name')) or ''
def encode_text(text):
return " ".join([str(ord(c)) for c in text])

View file

@ -9,7 +9,7 @@ class TestClient:
def get(self, path, JS=True): def get(self, path, JS=True):
file_path = self.build_path(path) file_path = self.build_path(path)
content = "".join(open(file_path).readlines()) content = "".join(open(file_path).readlines())
if path.endswith('html'): if file_path.endswith('html'):
content = BeautifulSoup(content, 'html.parser') content = BeautifulSoup(content, 'html.parser')
if JS: if JS:
for script in content(["noscript"]): # Remove noscript tags for script in content(["noscript"]): # Remove noscript tags
@ -19,6 +19,8 @@ class TestClient:
def build_path(self, path): def build_path(self, path):
if path.startswith('/'): if path.startswith('/'):
path = path[1:] path = path[1:]
if path.endswith('/'):
path += 'index.html'
return os.path.join(self.output_path, path) return os.path.join(self.output_path, path)
def exists(self, path): def exists(self, path):

View file

@ -96,5 +96,11 @@ class TestClientTestCase(TestCase):
def test_file_exists(self): def test_file_exists(self):
self.assertTrue(self.client.exists('index.html')) self.assertTrue(self.client.exists('index.html'))
def test_build_path_without_index(self):
self.assertEqual(
self.client.build_path('foo/'),
self.client.build_path('foo/index.html')
)
def test_file_doesnt_exist(self): def test_file_doesnt_exist(self):
self.assertFalse(self.client.exists('foo.bar')) self.assertFalse(self.client.exists('foo.bar'))

View file

@ -1,5 +1,6 @@
from tests import TestCase from tests import TestCase
from config import social as social_settings from config import social as social_settings
import pelicanconf as settings
import os.path import os.path
@ -47,18 +48,6 @@ class AboutPageTestCase(TestCase):
self.assertHeaderTitle(content, 'About') self.assertHeaderTitle(content, 'About')
self.assertTitle(content, 'About') self.assertTitle(content, 'About')
def test_website_section(self):
content = self.client.get('about/index.html')
section = content.find('section', id='website')
subtitle = section.find('h2')
self.assertEqual('Website', self.get_children(subtitle))
def test_server_section(self):
content = self.client.get('about/index.html')
section = content.find('section', id='server')
subtitle = section.find('h2')
self.assertEqual('Server', self.get_children(subtitle))
def test_github_card(self): def test_github_card(self):
content = self.client.get('about/index.html') content = self.client.get('about/index.html')
tags = content.find_all('div', class_='github-card') tags = content.find_all('div', class_='github-card')
@ -67,6 +56,33 @@ class AboutPageTestCase(TestCase):
self.assertEqual('medium', tag.attrs['data-theme']) self.assertEqual('medium', tag.attrs['data-theme'])
self.assertEqual(social_settings['accounts']['github'][1], tag.attrs['data-github']) self.assertEqual(social_settings['accounts']['github'][1], tag.attrs['data-github'])
def test_accounts(self):
content = self.client.get('about/index.html')
accounts = content.find_all('div', class_='account')
defined_accounts = [s for k, s in settings.ACCOUNTS.items()]
self.assertEqual(len(accounts), len(defined_accounts))
site_names = [s['site'] for s in defined_accounts]
urls = [s['url'] for s in defined_accounts]
icons = [s['icon'] for s in defined_accounts]
for account in accounts:
self.assertIn(account.find('a').attrs['href'], urls)
self.assertIn(account.find('i').attrs['class'][-1], icons)
self.assertIn(self.get_children(account.find('h3')), site_names)
class ContactPageTestCase(TestCase):
def test_title(self):
content = self.client.get('contact/')
self.assertHeaderTitle(content, 'Contact Me')
self.assertTitle(content, 'Contact Me')
def test_contact_links(self):
content = self.client.get('contact/')
links = content.find_all('section')[2].find_all('a')
self.assertEqual(links[1].attrs['href'], settings.ACCOUNTS['twitter']['url'])
decoded_value = ''.join([chr(int(c)) for c in links[0].attrs['data-value'].split(' ')])
self.assertEqual(decoded_value, settings.CONTACT_EMAIL)
class Page404TestCase(TestCase): class Page404TestCase(TestCase):
def test_title(self): def test_title(self):

View file

@ -30,3 +30,14 @@ $('.navbar-brand').bind('click', function (event) {
} }
event.preventDefault(); event.preventDefault();
}); });
$('.protected-mailto').bind('click', function (evt) {
evt.preventDefault();
var char_codes = $(this).data('value').split(' ');
var plain_text = [];
for (var i = 0; i < char_codes.length; i++) {
plain_text.push(String.fromCharCode(parseInt(char_codes[i], 10)));
}
window.location = 'mailto:' + plain_text.join('');
});

View file

@ -16,3 +16,7 @@
padding: 0 $grid-gutter-width / 2; padding: 0 $grid-gutter-width / 2;
} }
a.no-underline:hover {
text-decoration: inherit;
}

View file

@ -110,3 +110,13 @@ header#header {
.github-card-container > iframe { .github-card-container > iframe {
max-width: 100%; max-width: 100%;
} }
.protected-mailto {
cursor: pointer;
}
.service-box.account {
a:hover {
color: $brand-orange-dark;
}
}