aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/pages/views.py7
-rw-r--r--config/base_urls.py7
-rw-r--r--config/requirements.txt31
-rw-r--r--config/settings.py3
-rw-r--r--design/sass/_forms.scss13
-rw-r--r--design/sass/_global.scss7
-rw-r--r--design/sass/screenv1.scss1
-rw-r--r--design/templates/base.html28
-rw-r--r--design/templates/django_registration/logged_out.html9
-rw-r--r--design/templates/django_registration/registration_form.html7
-rw-r--r--design/templates/lib/login.html34
-rw-r--r--design/templates/notes/note_list.html9
-rw-r--r--design/templates/pages/page.html8
-rw-r--r--design/templates/registration/login.html27
-rwxr-xr-xminify.py10
-rw-r--r--scripts/overlay.js253
-rw-r--r--scripts/util.js20
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();
+}