diff options
-rw-r--r-- | apps/pages/views.py | 7 | ||||
-rw-r--r-- | config/base_urls.py | 7 | ||||
-rw-r--r-- | config/requirements.txt | 31 | ||||
-rw-r--r-- | config/settings.py | 3 | ||||
-rw-r--r-- | design/sass/_forms.scss | 13 | ||||
-rw-r--r-- | design/sass/_global.scss | 7 | ||||
-rw-r--r-- | design/sass/screenv1.scss | 1 | ||||
-rw-r--r-- | design/templates/base.html | 28 | ||||
-rw-r--r-- | design/templates/django_registration/logged_out.html | 9 | ||||
-rw-r--r-- | design/templates/django_registration/registration_form.html | 7 | ||||
-rw-r--r-- | design/templates/lib/login.html | 34 | ||||
-rw-r--r-- | design/templates/notes/note_list.html | 9 | ||||
-rw-r--r-- | design/templates/pages/page.html | 8 | ||||
-rw-r--r-- | design/templates/registration/login.html | 27 | ||||
-rwxr-xr-x | minify.py | 10 | ||||
-rw-r--r-- | scripts/overlay.js | 253 | ||||
-rw-r--r-- | scripts/util.js | 20 |
17 files changed, 442 insertions, 32 deletions
diff --git a/apps/pages/views.py b/apps/pages/views.py index 617ce04..4ace41b 100644 --- a/apps/pages/views.py +++ b/apps/pages/views.py @@ -1,4 +1,5 @@ from django.views.generic.detail import DetailView +from django.contrib.auth.forms import AuthenticationForm from pages.models import Page @@ -9,3 +10,9 @@ class PageDetailView(DetailView): def get_template_names(self): obj = self.get_object() return ["pages/%s.html" % obj.slug, 'pages/page.html'] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + if self.request.user.is_anonymous: + context['login_form'] = AuthenticationForm() + return context diff --git a/config/base_urls.py b/config/base_urls.py index bb2fa56..84dd56d 100644 --- a/config/base_urls.py +++ b/config/base_urls.py @@ -9,17 +9,16 @@ from django_registration.backends.activation.views import RegistrationView from rest_framework import routers from pages.views import PageDetailView -from notes.views import NoteViewSet +from notes.views import NoteViewSet, FolderViewSet, NoteListView from accounts.forms import UserForm router = routers.DefaultRouter() -router.register(r'notes', NoteViewSet, basename="notes") +router.register(r'<str:user>/notes/folder/', FolderViewSet, basename="folder-api") +router.register(r'<str:user>/notes/', NoteViewSet, basename="notes-api") urlpatterns = static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += [ path('admin/', admin.site.urls), - # path(r'profile/', include('accounts.urls')), - path(r'notes/', include('notes.urls')), path(r'accounts/', include('django_registration.backends.activation.urls')), path(r'accounts/', include('django.contrib.auth.urls')), path(r'register/', RegistrationView.as_view(form_class=UserForm), name='django_registration_register',), diff --git a/config/requirements.txt b/config/requirements.txt index f174273..fb66a23 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -1,12 +1,43 @@ +backcall==0.1.0 +beautifulsoup4==4.6.3 +bs4==0.0.1 +certifi==2018.10.15 +chardet==3.0.4 +confusable-homoglyphs==3.2.0 coverage==4.5.1 +decorator==4.3.0 Django==2.1.2 django-extensions==2.1.3 django-registration==3.0 django-storages==1.7.1 django-taggit==0.23.0 +django-taggit-serializer==0.1.7 +djangorestframework==3.9.0 +Faker==0.9.1 +idna==2.7 ipython==7.1.1 ipython-genutils==0.2.0 +jedi==0.13.1 +jsmin==2.2.2 +Markdown==3.0.1 mixer==6.1.3 +parso==0.3.1 +pexpect==4.6.0 +pickleshare==0.7.5 Pillow==5.3.0 +pkg-resources==0.0.0 +prompt-toolkit==2.0.7 psycopg2==2.7.5 +psycopg2-binary==2.7.6.1 +ptyprocess==0.6.0 pwned-passwords-django==1.3.1 +Pygments==2.2.0 +python-dateutil==2.7.5 +python-decouple==3.1 +pytz==2018.7 +requests==2.20.1 +six==1.11.0 +text-unidecode==1.2 +traitlets==4.3.2 +urllib3==1.24.1 +wcwidth==0.1.7 diff --git a/config/settings.py b/config/settings.py index ade8a84..52d2314 100644 --- a/config/settings.py +++ b/config/settings.py @@ -87,7 +87,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [ - os.path.join(BASE_DIR, 'templates'), + os.path.join(BASE_DIR, 'design/templates'), ], 'APP_DIRS': True, 'OPTIONS': { @@ -96,6 +96,7 @@ TEMPLATES = [ 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + #'notes.context_processors.add_login_form', ], }, }, diff --git a/design/sass/_forms.scss b/design/sass/_forms.scss index 3dc8ee4..cac2442 100644 --- a/design/sass/_forms.scss +++ b/design/sass/_forms.scss @@ -1,3 +1,6 @@ +form { + +} form fieldset { margin: 1rem 0; padding: 0; @@ -76,10 +79,18 @@ table { background: $link_color; color: #fff !important; border: 1px solid $link_color; - padding: 5px 9px; + padding: 7px 9px; white-space: nowrap; &:hover { background: $link_hover_color; border: 1px solid $link_hover_color; } } +.form-narrow { + margin: 0 auto; + max-width: 60%; +} +.fancy-legend { + @include fontsize(24); + @include fancy-serif; +} diff --git a/design/sass/_global.scss b/design/sass/_global.scss index 9a01c61..e852855 100644 --- a/design/sass/_global.scss +++ b/design/sass/_global.scss @@ -153,6 +153,7 @@ h3 { } .wrapper { @include constrain_wide; + margin-top: 5rem; } //************** Universals ************************ .hide { @@ -176,6 +177,12 @@ h3 { .alert, .error { color: red !important; } +.text-center { + text-align: center; +} +.sm { + max-width: 80px; +} //************** other global classes ************************ .sans { @include generic_sans; diff --git a/design/sass/screenv1.scss b/design/sass/screenv1.scss index a9e582e..a818f08 100644 --- a/design/sass/screenv1.scss +++ b/design/sass/screenv1.scss @@ -5,3 +5,4 @@ @import "_header.scss"; @import "_footer.scss"; @import "_forms.scss"; +@import "_model.scss"; diff --git a/design/templates/base.html b/design/templates/base.html index 45291d0..6b6ef8c 100644 --- a/design/templates/base.html +++ b/design/templates/base.html @@ -27,10 +27,10 @@ <nav class="right"> <ul> <li><a href="{% url 'pages' slug='tour' %}" title="">Tour</a></li> - <li><a href="{% url 'pages' slug='howto' %}" title="">How to</a></li> - <li><a href="{% url 'logout' %}" title="">Log out</a></li>{% if request.user %} - <li><a href="{% url 'settings' %}" title="">Account</a></li>{% else %} - <li><a href="{% url 'login' %}" title="">Login</a></li> + <li><a href="{% url 'pages' slug='howto' %}" title="">How to</a></li>{% if not request.user.is_anonymous %} + <li><a href="{% url 'settings' %}" title="">Account</a></li> + <li><a href="{% url 'logout' %}" title="">Log out</a></li>{% else %} + <li><a href="{% url 'login' %}" title="" id="overlay-trigger" data-element="#js-overlay-content">Login</a></li> <li><a href="{% url 'django_registration_register' %}" title="" class="btn">Get Started</a></li>{% endif %} </ul> </nav> @@ -53,4 +53,24 @@ </ul> </nav> </footer> + {% block extra %} + {%endblock%} +<script async src="/media/js/package.min.js"></script> +<script> +// Waiting for the DOM to load +document.addEventListener("DOMContentLoaded", function () { + // Select your overlay trigger + var trigger = document.querySelector('#overlay-trigger'); + trigger.addEventListener('click', function(e){ + e.preventDefault(); + novicell.overlay.create({ + 'selector': trigger.getAttribute('data-element'), + 'class': 'selector-overlay', + "onCreate": function() { console.log('created'); }, + "onLoaded": function() { console.log('loaded'); }, + "onDestroy": function() { console.log('Destroyed'); } + }); + }); +}); +</script> </body> diff --git a/design/templates/django_registration/logged_out.html b/design/templates/django_registration/logged_out.html new file mode 100644 index 0000000..cccb58b --- /dev/null +++ b/design/templates/django_registration/logged_out.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} + +{% block title %}Logged Out{% endblock %} + +{% block primary %} +<h2>You're logged out</h2> +<h4>Thanks for visiting the site today</h4> +<a href="{%url 'login'%}">Login again</a> +{% endblock %} diff --git a/design/templates/django_registration/registration_form.html b/design/templates/django_registration/registration_form.html index 54e01c5..8573918 100644 --- a/design/templates/django_registration/registration_form.html +++ b/design/templates/django_registration/registration_form.html @@ -15,3 +15,10 @@ </form> <p class="text-muted">Already have an account? <a href="{% url 'login' %}">Log in</a>.</p> {% endblock %} +{% block extra %} +<div class="overlay-content" id="js-overlay-content" style="display: none;"> + {% include 'lib/login.html' with form=login_form site=site %} + {{login_form}} +</div> +{{site.name}} +{%endblock%} diff --git a/design/templates/lib/login.html b/design/templates/lib/login.html new file mode 100644 index 0000000..de2300d --- /dev/null +++ b/design/templates/lib/login.html @@ -0,0 +1,34 @@ +<h1>Login to {{site.name}}</h1> +{% if form.errors %} +<p>Your username and password didn't match. Please try again.</p> +{% endif %} + +{% if next %} + {% if user.is_authenticated %} + <p>Your account doesn't have access to this page. To proceed, + please login with an account that has access.</p> + {% else %} + <p>Please login to see this page.</p> + {% endif %} +{% endif %} + +<form method="post" action="{% url 'login' %}"> +{% csrf_token %} +<fieldset> + {{ form.username.label_tag }} + {{ form.username }} +</fieldset> +<fieldset> + {{ form.password.label_tag }} + {{ form.password }} +</fieldset> + +<input type="submit" class="btn sm" value="login"> +<input type="hidden" name="next" value="{{ next }}"> +<a href="" >Cancel</a> +</form> + +{# Assumes you setup the password_reset view in your URLconf #} +<hr /> +<p class="text-center">No account yet? <a href="/register/">Sign up</a>.<br> +<a href="{% url 'password_reset' %}">Forgot your password?</a></p> diff --git a/design/templates/notes/note_list.html b/design/templates/notes/note_list.html deleted file mode 100644 index 762d05a..0000000 --- a/design/templates/notes/note_list.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends 'base.html' %} -{% block content %} -<main> - <h1> Notes</h1> - <ul>{% for obj in object_list %} - <li>{{obj}}</li> - {% endfor %}</ul> -</main> -{% endblock %} diff --git a/design/templates/pages/page.html b/design/templates/pages/page.html index 3ac5795..91d4732 100644 --- a/design/templates/pages/page.html +++ b/design/templates/pages/page.html @@ -5,3 +5,11 @@ {{page.body_html|safe}} </main> {% endblock %} + +{% block extra %} +{% if login_form %} +<div class="overlay-content" id="js-overlay-content" style="display: none;"> + {% include 'lib/login.html' with form=login_form %} +</div> +{% endif %} +{%endblock%} diff --git a/design/templates/registration/login.html b/design/templates/registration/login.html index 13b1c6c..a481556 100644 --- a/design/templates/registration/login.html +++ b/design/templates/registration/login.html @@ -1,5 +1,7 @@ {% extends 'base.html' %} {% block content %} +<form class="form-narrow" method="post" action="{% url 'login' %}"> +<legend class="fancy-legend">Login to {{site.name}}</legend> {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} @@ -13,23 +15,22 @@ {% endif %} {% endif %} -<form method="post" action="{% url 'login' %}"> {% csrf_token %} -<table> -<tr> - <td>{{ form.username.label_tag }}</td> - <td>{{ form.username }}</td> -</tr> -<tr> - <td>{{ form.password.label_tag }}</td> - <td>{{ form.password }}</td> -</tr> -</table> +<fieldset> + {{ form.username.label_tag }} + {{ form.username }} +</fieldset> +<fieldset> + {{ form.password.label_tag }} + {{ form.password }} +</fieldset> -<input type="submit" value="login"> +<input type="submit" class="btn sm" value="login"> <input type="hidden" name="next" value="{{ next }}"> </form> {# Assumes you setup the password_reset view in your URLconf #} -<p><a href="{% url 'password_reset' %}">Lost password?</a></p> +<hr /> +<p class="text-center">No account yet? <a href="/register/">Sign up</a>.<br> +<a href="{% url 'password_reset' %}">Forgot your password?</a></p> {% endblock %} diff --git a/minify.py b/minify.py new file mode 100755 index 0000000..6ce2775 --- /dev/null +++ b/minify.py @@ -0,0 +1,10 @@ +from jsmin import jsmin +import os +minified = "" + +for filename in os.listdir('scripts'): + with open(os.path.join('scripts', filename)) as js_file: + minified += jsmin(js_file.read()) +with open('media/js/package.min.js', 'w') as jscompressed: + jscompressed.write(minified) + jscompressed.close() diff --git a/scripts/overlay.js b/scripts/overlay.js new file mode 100644 index 0000000..b40d911 --- /dev/null +++ b/scripts/overlay.js @@ -0,0 +1,253 @@ +'use strict'; +/** + * @name Novicell overlay + * @desc Simple script that opens an overlay / modal with some content form either a selector or an URL + * @author Danni Larsen (DLA), Michael Sølvsteen (MSL), Signe Helbo Poulsen (SHP), Emil Skytte Ankersen (EAN) + * @example novicell.overlay.create({ 'selector': SELECTOR, 'url': URL, 'class':'CLASSNAME', 'onCreate': FUNCTIONNAME, 'onLoaded': FUNCTIONNAME, 'onDestroy': FUNCTIONNAME }); + * @requires none + */ + +var novicell = novicell || {}; + +novicell.overlay = novicell.overlay || new function () { + var self = this; + var options = {}; + var overlayElem; + var overlayContainer; + var overlayContent; + var backdrop; + var content; + var onCreate; + var onLoaded; + var onDestroy; + var isVideo = false; + + this.create = function (opts) { + var self = this; + // Set global options + options = opts; + + // Call onCreate callback + if (typeof options.onCreate === 'function') { + options.onCreate(); + } + + // Remove existing overlays + self.destroy(); + + // Check if content comes from a DOM selector + if (options.hasOwnProperty('selector') && options.selector !== null) { + var element = document.querySelector(options.selector); + + if (element) { + content = element.innerHTML; + constructOverlay(); + } else { + console.warn('novicell.overlay: element does not exist. Please provide a valid selector for use in document.querySelector.'); + return; + } + } + + // Check if content comes from a HTML element + else if (options.hasOwnProperty('element') && options.element !== null) { + var element = options.element; + + if (element) { + content = element.innerHTML; + constructOverlay(); + } else { + console.warn('novicell.overlay: element does not exist. Please provide a valid DOM element.'); + return; + } + } + + // Or if content comes from an ID + else if (options.hasOwnProperty('videoId')) { + if (options.videoId !== null) { + var src = ''; + isVideo = true; + + if(options.type == 'vimeo') { + src = 'https://player.vimeo.com/video/' + options.videoId + '?autoplay=' + options.autoplay; + } + else if(options.type == 'youtube') { + src = 'https://www.youtube.com/embed/' + options.videoId + '?autoplay=' + options.autoplay + '&rel=0'; + } + else { + return; + } + + var iframe = document.createElement('iframe'); + iframe.setAttribute('src', src); + iframe.setAttribute('frameborder', 0); + iframe.setAttribute('allowfullscreen', ''); + iframe.setAttribute('width', '100%'); + iframe.setAttribute('height', '100%'); + + content = iframe.outerHTML; + + constructOverlay(); + } else { + console.warn('novicell.overlay: video-id is empty. Please provide a video-id for use in video embed code (we support only Vimeo and YouTube).'); + return; + } + } + // If nothing is working, send error to los consolé + else { + console.error('novicell.overlay: no content to display! Please set a selector or a url to load.') + return; + } + }; + + this.destroy = function () { + if(document.querySelector('#js-novi-overlay')) { + // Remove elements + overlayElem.parentElement.removeChild(overlayElem); + backdrop.parentElement.removeChild(backdrop); + + // Stop listening for close overlay events + document.removeEventListener('keyup', self.destroy); + + // Remove class on body + document.documentElement.classList.remove('no-scroll', 'novi-overlay--open'); + + // Reset video variable + isVideo = false; + + // Call onDestroy callback + if (typeof options.onDestroy === 'function') { + options.onDestroy(); + } + } + }; + + function constructOverlay() { + // Create backdrop + setupBackdrop(); + + // Create the overlay + setupOverlay(); + + // Create content for overlay + setupOverlayContainer(); + + // Create close button + setupCloseButton(); + + // Add class to body-element + document.documentElement.classList.add('no-scroll'); + + // Call onLoaded callback + if (typeof options.onLoaded === 'function') { + options.onLoaded(); + } + }; + + function setupBackdrop() { + // Create the backdrop + backdrop = document.createElement('div'); + backdrop.classList.add('novi-backdrop'); + backdrop.id = 'js-novi-backdrop'; + + backdrop.addEventListener('click', function(e){ + if(e.target.classList.contains('novi-overlay') || e.target.classList.contains('novi-overlay__container')) { + self.destroy(); + } + }); + + // Add backdrop to overlay element + document.querySelector('body').appendChild(backdrop); + }; + + /* + * Helper functions for HTML elements + */ + function setupOverlay() { + // Create the overlay + overlayElem = document.createElement('div'); + overlayElem.classList.add('novi-overlay'); + overlayElem.id = 'js-novi-overlay'; + + // Set class for the overlay, if set in options + if (options.hasOwnProperty('class')) { + overlayElem.classList.add(options.class); + } + + // Add overlay to overlay element + // document.querySelector('body').appendChild(overlayElem); + backdrop.appendChild(overlayElem); + }; + + function setupOverlayContainer() { + // Create content for overlay + overlayContainer = document.createElement('div'); + overlayContainer.classList.add('novi-overlay__container'); + + // Create scroll element + overlayContent = document.createElement('div'); + overlayContent.classList.add('novi-overlay__content'); + + if(isVideo) { + overlayContent.classList.add('novi-overlay__content--video') + } + + // Set content + overlayContent.innerHTML = content; + overlayContainer.appendChild(overlayContent); + + // Add overlayContainer to overlay element + overlayElem.appendChild(overlayContainer); + }; + + function setupCloseButton() { + // Create the button + var btnClose = document.createElement('button'); + btnClose.classList.add('novi-overlay-close', 'button--close'); + btnClose.type = 'button'; + btnClose.id = 'js-novi-overlay-close'; + + // Add eventlistener for button click + btnClose.addEventListener('click', self.destroy); + + // Add eventlistener for esc key + document.addEventListener('keydown', function (e) { + if (e.keyCode === 27) { + self.destroy(); + } + }); + + // Add close button to overlay element + overlayContent.appendChild(btnClose); + }; + + /* + * Helper functions for getting content + */ + function get(url) { + // Return a new promise. + return new Promise(function (resolve, reject) { + // Do the usual XHR stuff + var req = new XMLHttpRequest(); + req.open('GET', url); + + req.onload = function () { + if (req.status >= 200 && req.status < 400) { + // Success!! + resolve(req.response); + } else { + // Error!! + reject(Error(req.statusText)); + } + }; + + // Handle network errors + req.onerror = function () { + reject(Error("Network Error")); + }; + + // Make the request + req.send(); + }); + }; + +}(); diff --git a/scripts/util.js b/scripts/util.js new file mode 100644 index 0000000..d70c012 --- /dev/null +++ b/scripts/util.js @@ -0,0 +1,20 @@ +function get_login_form() { + var request = new XMLHttpRequest(); + request.open('GET', '/login/', true); + request.onload = function() { + if (request.status >= 200 && request.status < 400) { + var data = + for(var i in data) { + var u = data[i]['fields']['part_number'] + ' - ' + data[i]['fields']['part_name'] + choices.push({ value: String(data[i].pk), label: u, }); + } + populateParts(choices); + } else { + console.log("server error"); + } + }; + request.onerror = function() { + console.log("error on request"); + }; + request.send(); +} |