diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..0fa2039 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,6 @@ +{ + "extends": "./node_modules/eslint-config/.eslintrc", + "globals": { + $: true + } +} diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..4e32c7b --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +5.10.1 diff --git a/.spelling b/.spelling new file mode 100644 index 0000000..30b8942 --- /dev/null +++ b/.spelling @@ -0,0 +1,38 @@ +# markdown-spellcheck spelling configuration file +# Format - lines beginning # are comments +# global dictionary is at the start, file overrides afterwards +# one word per line, to define a file override use ' - filename' +# where filename is relative to this configuration file +Django +SQLite +eg +MyWindowsHosting +nginx +backends +PyGame +easter +_Enabler +Hipchat +DabApps +JakeSidSmith +facepalm +notsureif +wat +premis +hipchat +plugin +firefox +Jetpack +Javascript +facebook +github +morse +wikipedia +iframe +querystring +javascript +jQuery +gists +Lenovo +Collyer's +til diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 4acaa1b..0000000 --- a/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Jake Howard - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/README.md b/README.md index 02bcfd7..03e4a69 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# My Website +# My Website + +[![Circle CI](https://circleci.com/gh/RealOrangeOne/theorangeone.net.svg?style=svg)](https://circleci.com/gh/RealOrangeOne/theorangeone.net) diff --git a/build b/build index 7374d1a..6437486 100755 --- a/build +++ b/build @@ -1,11 +1,28 @@ #!/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-3.4 env +pyvenv env env/bin/pip install -r requirements.txt --upgrade +scripts/get-private-data.sh + npm install -npm run build +npm run build $@ env/bin/python manage.py collectstatic --noinput + +if [[ $BUILD_PRODUCTION ]] +then + echo ">> Running Migrations..." + env/bin/python manage.py migrate +fi diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..3beddab --- /dev/null +++ b/circle.yml @@ -0,0 +1,20 @@ +machine: + python: + version: 3.4.2 + node: + version: 5.10.1 + environment: + DEBUG: true + DATABASE_URL: sqlite://~/database.db + EMAIL_BACKEND: django.core.mail.backends.console.EmailBackend + BUILD_PRODUCTION: true + +dependencies: + pre: + - ./build + cache_directories: + - env + +test: + override: + - ./runtests diff --git a/data/context.yml b/data/context.yml new file mode 100644 index 0000000..ef3fb5e --- /dev/null +++ b/data/context.yml @@ -0,0 +1,6 @@ +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 diff --git a/data/page_context.yml b/data/page_context.yml new file mode 100644 index 0000000..4c9dcf1 --- /dev/null +++ b/data/page_context.yml @@ -0,0 +1,9 @@ +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 diff --git a/data/path_switch.yml b/data/path_switch.yml new file mode 100644 index 0000000..7a0d12c --- /dev/null +++ b/data/path_switch.yml @@ -0,0 +1 @@ +college/attack-on-blocks: projects/attack-on-blocks diff --git a/etc/environments/deployment/env b/etc/environments/deployment/env index d79c86a..0c8ddc0 100644 --- a/etc/environments/deployment/env +++ b/etc/environments/deployment/env @@ -1,3 +1,3 @@ DEBUG=false -DATABASE_URL=sqlite:///database.db EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend +BUILD_PRODUCTION=true diff --git a/etc/environments/development/env b/etc/environments/development/env index e0bf99a..9204e2e 100644 --- a/etc/environments/development/env +++ b/etc/environments/development/env @@ -1,3 +1,2 @@ DEBUG=true -DATABASE_URL=sqlite:///database.db EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend diff --git a/etc/environments/development/procfile b/etc/environments/development/procfile index dbc4b47..40349a7 100644 --- a/etc/environments/development/procfile +++ b/etc/environments/development/procfile @@ -1,2 +1,2 @@ web: manage.py runserver 0.0.0.0:$PORT -watcher: npm run watch \ No newline at end of file +watcher: npm run watch diff --git a/package.json b/package.json index a7a407d..6580b23 100644 --- a/package.json +++ b/package.json @@ -1,43 +1,50 @@ { "name": "TheOrangeOne-Site", - "version": "0.0.1", - "description": "TheOrangeOne.net site", - "main": "app.js", + "version": "3.0.0", + "description": " Source code for TheOrangeOne.net", "scripts": { - "lint": "echo 'Nothing yet.';", - "build-js": "bash scripts/build-js.sh", - "create-build-dirs": "mkdir -p static/build/js static/build/fonts static/build/css static/build/img private", + "lint": "eslint 'static/src/js/'", + "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": "npm run create-build-dirs && npm run build-fonts && npm run build-images && npm run build-js && npm run build-less", - "build-less": "lessc --silent static/src/less/style.less static/build/css/style.css && cleancss -d --s0 -o static/build/css/style.css static/build/css/style.css", - "build-fonts": "cp -R node_modules/bootstrap/dist/fonts static/build/ && cp -R node_modules/ionicons-pre/fonts static/build/ ", + "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-images": "watch 'npm run build-images' static/src/img/", "watch-less": "watch 'npm run build-less' static/src/less/", - "watch": "npm run watch-less & npm run watch-images", - "clean": "rm -rf static/build collected-static/ node_modules/ env/ private" + "watch": "npm run watch-less", + "clean": "./scripts/clean.js", + "test": "npm run lint", + "spellcheck": "mdspell --en-gb -ranx \"templates/**/*.*\"" }, "repository": { "type": "git", "url": "https://github.com/RealOrangeOne/theorangeone.net" }, "dependencies": { - "bootstrap": "^3.3.5", - "jquery": "^2.1.4", - "less": "^2.5.1", - "markdown": "^0.5.0", - "react": "^0.13.3", - "react-bootstrap": "^0.25.1", - "skrollr": "^0.6.26" + "animate.css": "=3.4.0", + "bootstrap": "=3.3.5", + "ionicons": "=2.0.1", + "jquery": "=2.1.4", + "react": "=0.13.3", + "react-bootstrap": "=0.25.1", + "whatwg-fetch": "=0.10.1" }, "devDependencies": { - "browserify": "^11.0.1", - "clean-css": "^3.4.1", - "eslint": "^1.5.0", - "eslint-plugin-react": "^3.4.2", - "ionicons-pre": "^1.0.0-pre", - "react-tools": "^0.13.2", - "reactify": "^1.1.1", - "uglify-js": "^2.4.24", - "watch": "^0.16.0" + "autoprefixer": "=6.3.3", + "babel-preset-es2015": "=6.1.18", + "babel-preset-react": "=6.1.18", + "babelify": "=7.2.0", + "browserify": "=11.0.1", + "clean-css": "=3.4.1", + "eslint": "=1.5.0", + "eslint-config": "git://github.com/dabapps/eslint-config.git", + "eslint-plugin-react": "=3.4.2", + "less": "=2.5.1", + "less-mixins": "git://github.com/RealOrangeOne/less-mixins.git", + "markdown-spellcheck": "=0.10.0", + "postcss-cli": "=2.5.1", + "react-tools": "=0.13.2", + "uglify-js": "=2.4.24", + "watch": "=0.16.0" } } diff --git a/project/college/__init__.py b/project/blog/__init__.py similarity index 100% rename from project/college/__init__.py rename to project/blog/__init__.py diff --git a/project/blog/tests.py b/project/blog/tests.py new file mode 100644 index 0000000..fc705e3 --- /dev/null +++ b/project/blog/tests.py @@ -0,0 +1,67 @@ +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": "

Test blog post content

", + "slug": "test-post" + } + self.invalid_blog_data = { + "title": "Invalid blog post", + "content": "

", + "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": "

Test blog post content

", + "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']) diff --git a/project/blog/urls.py b/project/blog/urls.py new file mode 100644 index 0000000..51d5239 --- /dev/null +++ b/project/blog/urls.py @@ -0,0 +1,6 @@ +from django.conf.urls import url +from . import views + +urlpatterns = [ + url(r'^(?P.+)/?$', views.BlogView.as_view(), name='blog-post'), +] diff --git a/project/blog/utils.py b/project/blog/utils.py new file mode 100644 index 0000000..a2af234 --- /dev/null +++ b/project/blog/utils.py @@ -0,0 +1,25 @@ +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") diff --git a/project/blog/views.py b/project/blog/views.py new file mode 100644 index 0000000..6ceed9d --- /dev/null +++ b/project/blog/views.py @@ -0,0 +1,22 @@ +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) diff --git a/project/college/admin.py b/project/college/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/project/college/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/project/college/migrations/__init__.py b/project/college/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/college/models.py b/project/college/models.py deleted file mode 100644 index 71a8362..0000000 --- a/project/college/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/project/college/tests.py b/project/college/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/college/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/college/urls.py b/project/college/urls.py deleted file mode 100644 index e4a01a4..0000000 --- a/project/college/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls import url -from .views import StudentServerView - - -urlpatterns = [ - url(r'^student-server', StudentServerView.as_view(), name='student-server') -] diff --git a/project/college/views.py b/project/college/views.py deleted file mode 100644 index 067dacc..0000000 --- a/project/college/views.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG - -class StudentServerView(CustomHeaderBG.Template): - template_name = 'college/student-server.html' \ No newline at end of file diff --git a/project/common/data.py b/project/common/data.py new file mode 100644 index 0000000..1794cd2 --- /dev/null +++ b/project/common/data.py @@ -0,0 +1,46 @@ +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 diff --git a/project/common/migrations/__init__.py b/project/common/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/common/models.py b/project/common/models.py deleted file mode 100644 index 71a8362..0000000 --- a/project/common/models.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.db import models - -# Create your models here. diff --git a/project/common/tests.py b/project/common/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/common/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/common/views.py b/project/common/views.py deleted file mode 100644 index be82893..0000000 --- a/project/common/views.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.views.generic import TemplateView - - -class CustomHeaderBG(): - """Allow custom header background""" - - class Template(TemplateView): - header_BG = "" - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['header_BG'] = self.header_BG - return context \ No newline at end of file diff --git a/project/media/__init__.py b/project/media/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/media/admin.py b/project/media/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/project/media/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/project/media/migrations/__init__.py b/project/media/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/media/tests.py b/project/media/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/media/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/media/urls.py b/project/media/urls.py deleted file mode 100644 index 0bacbc0..0000000 --- a/project/media/urls.py +++ /dev/null @@ -1,8 +0,0 @@ -from django.conf.urls import url -from .views import GalleryView, YoutubeView - - -urlpatterns = [ - url(r'^gallery', GalleryView.as_view(), name='gallery'), - url(r'^youtube', YoutubeView.as_view(), name='youtube') -] diff --git a/project/media/views.py b/project/media/views.py deleted file mode 100644 index 516f356..0000000 --- a/project/media/views.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG - - -class YoutubeView(CustomHeaderBG.Template): - template_name = "media/youtube.html" - - -class GalleryView(TemplateView): - template_name = "media/gallery.html" diff --git a/project/pages/tests.py b/project/pages/tests.py index a79ca8b..7f2554e 100644 --- a/project/pages/tests.py +++ b/project/pages/tests.py @@ -1,3 +1,19 @@ -# from django.test import TestCase +from django.test import TestCase +from django.conf import settings +import os.path +from glob import glob -# Create your tests here. + +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) diff --git a/project/pages/urls.py b/project/pages/urls.py index 6dbaf72..b597e68 100644 --- a/project/pages/urls.py +++ b/project/pages/urls.py @@ -1,11 +1,7 @@ from django.conf.urls import url -from . import views +from .views import page_view urlpatterns = [ - url(r'^about/website/$', views.AboutWebsiteView.as_view(), name='about-website'), - url(r'^about/$', views.AboutIndexView.as_view(), name='about'), - url(r'^no-js/$', views.NoJavascriptView.as_view(), name='no-js'), - url(r'^404/$', views.Custom404View.as_view(), name='404'), - url(r'^$', views.IndexView.as_view(), name='index') + url(r'^(?P.*)', page_view, name='page'), ] diff --git a/project/pages/utils.py b/project/pages/utils.py new file mode 100644 index 0000000..c14cf37 --- /dev/null +++ b/project/pages/utils.py @@ -0,0 +1,29 @@ +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 diff --git a/project/pages/views.py b/project/pages/views.py index ca5457e..f5ce62a 100644 --- a/project/pages/views.py +++ b/project/pages/views.py @@ -1,34 +1,33 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG -from django.contrib.staticfiles.templatetags.staticfiles import static +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 -class IndexView(TemplateView): - template_name = 'index.html' +def page_view(request, path): + template = None + if path.endswith('/'): # Strip trailing slash + path = path[:-1] + path = swap_page(path) -class NoJavascriptView(TemplateView): - template_name = 'core/no-js.html' - - -class Custom404View(CustomHeaderBG.Template): - template_name = 'core/404.html' - header_BG = static('img/ninjas.png') - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - context['no_footer'] = True - return context - - def get(self, request, *args, **kwargs): - context = self.get_context_data(**kwargs) - return self.render_to_response(context, status=404) - - -class AboutWebsiteView(CustomHeaderBG.Template): - template_name = 'about/website.html' - header_BG = '' - - -class AboutIndexView(TemplateView): - template_name = 'about/index.html' \ No newline at end of file + 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) diff --git a/project/projects/__init__.py b/project/projects/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/projects/migrations/__init__.py b/project/projects/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/projects/tests.py b/project/projects/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/projects/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/projects/urls.py b/project/projects/urls.py deleted file mode 100644 index 551c48f..0000000 --- a/project/projects/urls.py +++ /dev/null @@ -1,16 +0,0 @@ -from django.conf.urls import url -from . import views - -urlpatterns = [ - # Project URLs - url(r'^attack-on-blocks', views.AttackOnBlocksView.as_view(), name="attack-on-blocks"), - url(r'^bsod-enabler', views.BSODEnablerView.as_view(), name='bsod-enabler'), - url(r'^hipchat-emoticons-for-all', views.HipChatEmoticonsForAllView.as_view(), name='hipchat-emoticons'), - url(r'^pithos', views.PithosView.as_view(), name='pithos'), - - # Code URLs - url(r'^morse-code-decoder', views.MorseCodeDecoderView.as_view(), name='morse-code-decoder'), - url(r'^wiki-game-solver', views.WikiGameSolverView.as_view(), name='wiki-game-solver'), - - url(r'^', views.AllProjectsView.as_view(), name="index") -] diff --git a/project/projects/views.py b/project/projects/views.py deleted file mode 100644 index 899ed06..0000000 --- a/project/projects/views.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG - - -# Project Views -class AllProjectsView(TemplateView): - template_name = "projects/all_projects.html" - - -class AttackOnBlocksView(CustomHeaderBG.Template): - template_name = "college/attack-on-blocks.html" - - -class BSODEnablerView(CustomHeaderBG.Template): - template_name = 'projects/BSOD-Enabler.html' - header_BG = 'http://cdn9.howtogeek.com/wp-content/uploads/2013/05/xwindows-8-blue-screen-error.png.pagespeed.ic.yOWUS_rYGn.png' - - -class HipChatEmoticonsForAllView(CustomHeaderBG.Template): - template_name = 'projects/hipchat-emoticons-for-all.html' - header_BG = "https://info.seibert-media.net/plugins/servlet/pptslide?attachment=HipChat+Server+Sales+Pitch+Final+PPT.pptx&attachmentId=7536966&attachmentVer=1&pageId=3179011&slide=0" - - -class PithosView(CustomHeaderBG.Template): - template_name = 'projects/pithos.html' - - -# Code Views -class MorseCodeDecoderView(CustomHeaderBG.Template): - template_name = 'projects/morse-code-decoder.html' - - -class WikiGameSolverView(CustomHeaderBG.Template): - template_name = 'projects/wiki-game-solver.html' - header_BG = "http://is1.mzstatic.com/image/pf/us/r30/Purple/v4/a3/3f/31/a33f31dd-3ace-ffe4-5531-89d3f3a1106f/mzl.ihqwkbih.png" \ No newline at end of file diff --git a/project/robotics/__init__.py b/project/robotics/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/robotics/migrations/__init__.py b/project/robotics/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/robotics/tests.py b/project/robotics/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/robotics/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/robotics/urls.py b/project/robotics/urls.py deleted file mode 100644 index 731879e..0000000 --- a/project/robotics/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.conf.urls import url -from .views import Index2015View, Index2014View, RoboticsIndexView, Robot2015View - - -urlpatterns = [ - url(r'^$', RoboticsIndexView.as_view(), name='index' ), - url(r'^2015', Index2015View.as_view(), name='2015-index'), - url(r'^2015/robot', Robot2015View.as_view(), name='2015-robot'), - url(r'^2014', Index2014View.as_view(), name='2014-index') -] diff --git a/project/robotics/views.py b/project/robotics/views.py deleted file mode 100644 index 0a760f6..0000000 --- a/project/robotics/views.py +++ /dev/null @@ -1,22 +0,0 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG - - -class RoboticsIndexView(TemplateView): - template_name = 'robotics/index.html' - -# 2015 -class Index2015View(CustomHeaderBG.Template): - template_name = 'robotics/2015-index.html' - header_BG = "https://farm8.staticflickr.com/7711/17122633430_7d1bde923a_k.jpg" - - -class Robot2015View(CustomHeaderBG.Template): - template_name = 'robotics/2015-robot.html' - header_BG = "" - - -# 2014 -class Index2014View(CustomHeaderBG.Template): - template_name = 'robotics/2014-index.html' - header_BG = "nothing" \ No newline at end of file diff --git a/project/settings.py b/project/settings.py index e4fdcd5..f7933f5 100644 --- a/project/settings.py +++ b/project/settings.py @@ -1,19 +1,6 @@ -""" -Django settings for project. - -Generated by 'django-admin startproject' using Django 1.8.3. - -For more information on this file, see -https://docs.djangoproject.com/en/1.8/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.8/ref/settings/ -""" # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -import dj_database_url -# import logging -import os -import sys +import dj_database_url, os +from private import export BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -22,7 +9,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'q&^k#n_lkd5l9&g^k7$q1rhj_=%9yqy-)ln78ik414-bcowedy' +SECRET_KEY = export('SECRET_KEY') # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -31,23 +18,13 @@ ALLOWED_HOSTS = ['too.ctf.sh', 'theorangeone.net'] # Application definition INSTALLED_APPS = ( - 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', 'django.contrib.staticfiles', - 'rest_framework', - 'bootstrapform', - 'project.pages', 'project.common', - 'project.projects', - 'project.robotics', - 'project.setup', - 'project.media', - 'project.college' + 'project.blog' ) MIDDLEWARE_CLASSES = ( @@ -85,7 +62,7 @@ WSGI_APPLICATION = 'project.wsgi.application' # Database # https://docs.djangoproject.com/en/1.8/ref/settings/#databases DATABASES = { - 'default': dj_database_url.config(default=os.environ['DATABASE_URL']) + 'default': dj_database_url.config(default='sqlite://memory') } EMAIL_BACKEND = os.environ['EMAIL_BACKEND'] @@ -105,4 +82,9 @@ STATIC_ROOT = os.path.join(BASE_DIR, 'collected-static') STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static', 'build'), ) -STATICFILES_STORAGE = 'whitenoise.django.GzipManifestStaticFilesStorage' + +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) diff --git a/project/setup/__init__.py b/project/setup/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/setup/admin.py b/project/setup/admin.py deleted file mode 100644 index 8c38f3f..0000000 --- a/project/setup/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/project/setup/migrations/__init__.py b/project/setup/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/project/setup/models.py b/project/setup/models.py deleted file mode 100644 index b2e2c23..0000000 --- a/project/setup/models.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.db import models -import uuid - - -class Device(models.Model): - device_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - device_name = models.CharField(max_length=50, blank=False, null=True) - friendly_name = models.CharField(max_length=50, blank=False, null=True, unique=True) - model_name = models.CharField(max_length=50, blank=False, null=True, unique=True) - description = models.TextField(blank=True, null=True) - computer = models.ForeignKey('setup.Computer') - - - -class Computer(models.Model): - computer_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - CPU = models.TextField(max_length=30, blank=False, null=True, verbose_name="Processor") - GPU = models.TextField(max_length=40, blank=True, null=True, verbose_name="Graphics Card") - RAM = models.IntegerField(blank=False, null=True) - - - -class Peripheral(models.Model): - peripheral_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - name = models.CharField(max_length=40, blank=False, null=True) - model_name = models.CharField(max_length=100, blank=False, null=True) - device = models.ForeignKey('setup.Device') diff --git a/project/setup/tests.py b/project/setup/tests.py deleted file mode 100644 index 7ce503c..0000000 --- a/project/setup/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/project/setup/urls.py b/project/setup/urls.py deleted file mode 100644 index 234e3b2..0000000 --- a/project/setup/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.conf.urls import url -from . import views - -urlpatterns = [ - url(r'^desk', views.DeskView.as_view(), name="desk"), - url(r'^', views.SetupIndexView.as_view(), name="index") -] diff --git a/project/setup/views.py b/project/setup/views.py deleted file mode 100644 index bc04113..0000000 --- a/project/setup/views.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.views.generic import TemplateView -from project.common.views import CustomHeaderBG - - -class SetupIndexView(TemplateView): - template_name = "setup/index.html" - - -class DeskView(CustomHeaderBG.Template): - template_name = "setup/my-rig.html" - header_BG = "https://c1.staticflickr.com/1/557/18312934624_b51a541594_h.jpg" diff --git a/project/urls.py b/project/urls.py index 267eb33..47926c7 100644 --- a/project/urls.py +++ b/project/urls.py @@ -1,15 +1,7 @@ from django.conf.urls import include, url -from django.contrib import admin -from project.pages.views import Custom404View + urlpatterns = [ - url(r'^site-admin/', include(admin.site.urls)), - url(r'^student-robotics/', include('project.robotics.urls', namespace='robotics')), - url(r'^projects/', include('project.projects.urls', namespace='projects')), - url(r'^media/', include('project.media.urls', namespace='media')), - url(r'^setup/', include('project.setup.urls', namespace='setup')), - url(r'^college/', include('project.college.urls', namespace='college')), + url(r'^blog/', include('project.blog.urls', namespace='blog')), url(r'', include('project.pages.urls', namespace='pages')) ] - -handler404 = Custom404View \ No newline at end of file diff --git a/project/wsgi.py b/project/wsgi.py index 927d764..81b9179 100644 --- a/project/wsgi.py +++ b/project/wsgi.py @@ -1,11 +1,3 @@ -""" -WSGI config for twm project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ -""" import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") diff --git a/requirements.txt b/requirements.txt index 0d89d27..09e7b03 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,13 @@ -Django==1.8.3 +beautifulsoup4==4.4.1 +coverage==4.0.3 +colorama==0.3.6 +Django==1.8.11 dj-database-url==0.3.0 -django-bootstrap-form==3.2 -djangorestframework==3.2.0 -djorm-ext-pgfulltext==0.10 -flake8==2.4.1 -pep8==1.5.7 -pycrypto==2.6.1 -pyflakes==0.8.1 -six==1.9.0 -waitress==0.8.9 -whitenoise==2.0.2 +flake8==2.5.0 +iso8601==0.1.11 +markdown2==2.3.0 +PyYAML==3.11 +requests==2.9.1 +requests-mock==0.7.0 +whitenoise==2.0.6 +waitress==0.8.10 diff --git a/runtests b/runtests index 5e6c7dd..ac34df1 100755 --- a/runtests +++ b/runtests @@ -1,7 +1,67 @@ -#!/usr/bin/env bash +#!/usr/bin/env python3 +import coverage +import os, sys +import subprocess +from colorama import Fore, init -set -e -export PATH=env/bin:${PATH} +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") +from django.core.management import execute_from_command_line +init(autoreset=True) -flake8 project --ignore=E128,E501 --exclude="migrations,settings,*/wsgi.py" + +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)) diff --git a/scripts/build-js.sh b/scripts/build-js.sh old mode 100644 new mode 100755 index 6d6d64f..b4fdede --- a/scripts/build-js.sh +++ b/scripts/build-js.sh @@ -1,18 +1,44 @@ -#!/usr/bin/bash +#!/usr/bin/env bash set -e -cp node_modules/bootstrap/dist/js/bootstrap.js static/src/js/lib/ -cp node_modules/skrollr/src/skrollr.js static/src/js/lib/ +if [[ $BUILD_PRODUCTION ]] +then + echo ">>> WARNING: Building in Production Mode!" +fi + +mkdir -p static/build/js/lib + +if [[ $BUILD_PRODUCTION ]] +then + 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 static/build/js/lib/* --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/libs.js +else + echo ">> Building Libraries..." + cp node_modules/bootstrap/dist/js/bootstrap.js static/build/js/lib/bootstrap.js + uglifyjs static/build/js/lib/* --screw-ie8 --stats --keep-fnames -o static/build/js/libs.js +fi + +rm -rf static/build/js/lib + +if [[ $BUILD_PRODUCTION ]] +then + echo ">> Compressing jQuery..." + uglifyjs node_modules/jquery/dist/jquery.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/jquery.js +else + echo ">> Building jQuery..." + uglifyjs node_modules/jquery/dist/jquery.js --screw-ie8 --stats --keep-fnames -o static/build/js/jquery.js +fi -echo ">> Building Libraries..." -uglifyjs node_modules/jquery/dist/jquery.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/jquery.js -uglifyjs node_modules/markdown/lib/markdown.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/markdown.js -uglifyjs static/src/js/lib/* --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/libs.js echo ">> Building Application JS..." -browserify -t reactify static/src/js/app.js -o static/build/js/app.js -# uglifyjs static/build/js/app.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/app.js +browserify -t [ babelify --presets [ es2015 react ] ] static/src/js/app.js -o static/build/js/app.js -echo ">> Building Global Utilities..." -uglifyjs static/src/js/utils.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/utils.js +if [[ $BUILD_PRODUCTION ]] +then + echo ">> Compressing Application..." + uglifyjs static/build/js/app.js --compress --screw-ie8 --define --stats --keep-fnames -o static/build/js/app.js +fi + +echo "> JS Built!" diff --git a/scripts/build-less.sh b/scripts/build-less.sh new file mode 100755 index 0000000..d9f27fa --- /dev/null +++ b/scripts/build-less.sh @@ -0,0 +1,22 @@ +#!/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!" diff --git a/scripts/clean.sh b/scripts/clean.sh new file mode 100755 index 0000000..0064077 --- /dev/null +++ b/scripts/clean.sh @@ -0,0 +1,15 @@ +#!/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." diff --git a/scripts/fresh.sh b/scripts/fresh.sh new file mode 100755 index 0000000..4338659 --- /dev/null +++ b/scripts/fresh.sh @@ -0,0 +1,24 @@ +#!/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!" diff --git a/scripts/get-private-data.sh b/scripts/get-private-data.sh new file mode 100755 index 0000000..c4b6d0e --- /dev/null +++ b/scripts/get-private-data.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +echo ">> Removing old Private Data..." +rm -rf private/ + +echo ">> Getting Private Data..." +git clone git@bitbucket.org:TheOrangeOne/theorangeone.net-site-private-data.git --branch master --single-branch private/ diff --git a/scripts/get_private_data.sh b/scripts/get_private_data.sh deleted file mode 100644 index 92119cb..0000000 --- a/scripts/get_private_data.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/bash - -set -e - -echo ">> Removing old Private data..." -rm -rf private/* -mkdir private/ -cd private/ - -echo ">> Cloning Private Repo..." -git clone git@bitbucket.org:TheOrangeOne/theorangeone.net-site-private-data.git - - -echo ">> That's it for now..." diff --git a/static/src/img/SR-logo-banner.png b/static/src/img/SR-logo-banner.png new file mode 100644 index 0000000..a4d9434 Binary files /dev/null and b/static/src/img/SR-logo-banner.png differ diff --git a/static/src/img/logo-transparent.png b/static/src/img/logo-transparent.png new file mode 100644 index 0000000..602aded Binary files /dev/null and b/static/src/img/logo-transparent.png differ diff --git a/static/src/img/BSOD_Enabler_Screenshot.png b/static/src/img/projects/BSOD_Enabler_Screenshot.png similarity index 100% rename from static/src/img/BSOD_Enabler_Screenshot.png rename to static/src/img/projects/BSOD_Enabler_Screenshot.png diff --git a/static/src/img/projects/collyers-logo.gif b/static/src/img/projects/collyers-logo.gif new file mode 100644 index 0000000..ca69f3b Binary files /dev/null and b/static/src/img/projects/collyers-logo.gif differ diff --git a/static/src/img/projects/kermit-typing.gif b/static/src/img/projects/kermit-typing.gif new file mode 100644 index 0000000..bb1b552 Binary files /dev/null and b/static/src/img/projects/kermit-typing.gif differ diff --git a/static/src/js/app.js b/static/src/js/app.js index d1ace96..47090a0 100644 --- a/static/src/js/app.js +++ b/static/src/js/app.js @@ -1,14 +1,24 @@ -var React = require('react'); -var indexCarousel = require('./components/indexCarousel.js'); +import './events.js'; +import './globals.js'; -require('./events.js'); +import 'whatwg-fetch'; +import React from 'react'; -if ($('#index-carousel-container').length == 1) { - React.render(indexCarousel, $("#index-carousel-container")[0]); +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')[0]); } -$(window).load(function(){ - $(window).trigger('scroll').trigger('resize'); - var s = skrollr.init(); -}); \ No newline at end of file +if ($('#breadcrumbs').length > 0) { + React.render(, $('#breadcrumbs')[0]); +} + +if ($('.header h1').text()) { + $('.markdown-content h1').eq(0).hide(); +} diff --git a/static/src/js/components/breadcrumbs.js b/static/src/js/components/breadcrumbs.js new file mode 100644 index 0000000..bdbaa51 --- /dev/null +++ b/static/src/js/components/breadcrumbs.js @@ -0,0 +1,33 @@ +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( +
  • {urlParts[i]}
  • + ); + } else { + var url = '/' + dirs.join('/') + '/'; + elements.push( +
  • {urlParts[i]}
  • + ); + } + } + return ( +
      + { elements } +
    + ); + } +} diff --git a/static/src/js/components/image-panel.js b/static/src/js/components/image-panel.js new file mode 100644 index 0000000..085df6c --- /dev/null +++ b/static/src/js/components/image-panel.js @@ -0,0 +1,24 @@ +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`); + }); +} diff --git a/static/src/js/components/indexCarousel.js b/static/src/js/components/indexCarousel.js deleted file mode 100644 index 0a56fa3..0000000 --- a/static/src/js/components/indexCarousel.js +++ /dev/null @@ -1,27 +0,0 @@ -var React = require('react'); -var Bootstrap = require('react-bootstrap'); - -var Carousel = Bootstrap.Carousel; -var CarouselItem = Bootstrap.CarouselItem; - -const indexCarousel = ( - - -
    -
    -

    Setup

    -

    Nulla vitae elit libero, a pharetra augue mollis interdum.

    -
    -
    - - -
    -
    -

    Student Robotics

    -

    Lorem ipsum dolor sit amet, consectetur adipiscing elit.

    -
    -
    -
    -); - -module.exports = indexCarousel; \ No newline at end of file diff --git a/static/src/js/components/navbar/dropdown.js b/static/src/js/components/navbar/dropdown.js new file mode 100644 index 0000000..728a505 --- /dev/null +++ b/static/src/js/components/navbar/dropdown.js @@ -0,0 +1,20 @@ +import React from 'react'; + +export default class Dropdown extends React.Component { + render() { + return ( +
  • + +
      + { this.props.children } +
    +
  • + ); + } +} diff --git a/static/src/js/components/navbar/header.js b/static/src/js/components/navbar/header.js new file mode 100644 index 0000000..5b2137c --- /dev/null +++ b/static/src/js/components/navbar/header.js @@ -0,0 +1,28 @@ +import React from 'react'; + +export default class Header extends React.Component { + render() { + const items = [0, 1, 2]; + var iconBars = items.map(function (item) { + return ( + + ); + }); + return ( +
    + + + + Navigation + +
    + ); + } +} diff --git a/static/src/js/components/navbar/item.js b/static/src/js/components/navbar/item.js new file mode 100644 index 0000000..03e2984 --- /dev/null +++ b/static/src/js/components/navbar/item.js @@ -0,0 +1,23 @@ +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 = ( + + ); + } else if (this.props.icon.startsWith('glyphicon')) { + icon = ( +