Commit a054a318 authored by Sergio Alonso's avatar Sergio Alonso

Merged feature/authentication

Squashed commit of the following:

commit 5fbe6a4ac2f10820a8377224fe70323473dcf0dd
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 17 13:01:40 2016 +0200

    git: ignore account sql info

commit 57328d311613d97c92a5c3ebe8f069f3f1f075cd
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 17 12:54:26 2016 +0200

    account: stuff

commit 5db50f436f5ece313be835c1cbe875f8b1580f98
Author: Sergio Alonso <[email protected]>
Date:   Thu Oct 13 20:05:35 2016 +0200

    account: move tests files

commit ccb3243ae1fcd1f4157a518e2b1cc0086073228f
Author: Sergio Alonso <[email protected]>
Date:   Thu Oct 13 20:04:33 2016 +0200

    account: remove unused data in profile template

commit 6a8d0f36aaa59b1ad767a2c9d97949c105509d7d
Author: Sergio Alonso <[email protected]>
Date:   Thu Oct 13 12:37:17 2016 +0200

    account: remove bootstrap messages

commit 5a96fae319a0cf2eefd773a3c75cb821805955b5
Author: Sergio Alonso <[email protected]>
Date:   Thu Oct 13 12:34:04 2016 +0200

    account: beautify navbar

commit 9c80f74ca3f8cc6d1a6fe8698d31825c2feb8161
Author: Sergio Alonso <[email protected]>
Date:   Thu Oct 13 11:08:25 2016 +0200

    account: login links

commit dc386baca49fdd0f441ab89bf76cab388a577f8c
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 21:45:14 2016 +0200

    account: urls after rename app

commit 35b81b1596775fe75d42f3be2d7efc7efccf2fec
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 21:33:19 2016 +0200

    settings: django-allauth

commit e384cd9ba692fcdbed0b65c9ab3ae6ca04ec0926
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 12:03:51 2016 +0200

    account: rename app

commit c04ba4862f25b9fb9d6e08cccadd85f6ede9ec9e
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 01:42:13 2016 +0200

    flake8: allow long lines

commit 69503f91f2e29e608d1e0868b33827382abb2dfb
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 01:41:48 2016 +0200

    accounts: display the user’s gravatar icon

commit a1ef9f21abe08978642666299ec912f89d5edcc1
Author: Sergio Alonso <[email protected]>
Date:   Wed Oct 12 01:29:00 2016 +0200

    accounts: remove the logout-confirmation step

commit fb25b5e643dd1a54e605363521f3d4a695adc212
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 19:46:32 2016 +0200

    accounts: add login and logout links

commit ce5ded0d880f788128573cdafb071d5ed71fa515
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 19:45:23 2016 +0200

    accounts: UserProfile model migration

commit d06b418d3ed3f543827c1a4170aaa47e60da9c5a
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 19:44:46 2016 +0200

    accounts: make the user’s email-verification status available

commit ebfb5cf7ce9d63ee736d95d0d0435a6d58e73932
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 19:00:38 2016 +0200

    notes: use https with runserver

commit 3716de293f860c6249c51fa4055f96f004113e79
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 18:54:08 2016 +0200

    settings: django-allauth settings

commit d69953a150d153aaca265f7abf9aeb54868fb76b
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 18:44:58 2016 +0200

    settings: install django_extensions app

commit 6207d37981a87669c6da5aea2d4f3d62089b4978
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 18:44:04 2016 +0200

    accounts: install app in settings

commit 41053fec900b13039d990a2de726e119c86a4757
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 18:42:44 2016 +0200

    accounts: profile template

commit 3eb5179e03e9f0edda5fa1e7fb4e9901b3b296f0
Author: Sergio Alonso <[email protected]>
Date:   Tue Oct 11 18:39:04 2016 +0200

    require django extensions to runserver with https

commit 23c6826442d8c63165de5e545756e84248613df5
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 10 16:33:37 2016 +0200

    django-allauth: install

commit 044db49e07600e2397762278cb7d658f9362cba7
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 10 15:38:48 2016 +0200

    Require django-allauth

commit f807120006ba8e405a9dd37d11bb895aeb302f40
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 10 15:36:38 2016 +0200

    accounts: start app

commit 5dda1b951ebd0f128061dcd03e4a5a37c54eede7
Author: Sergio Alonso <[email protected]>
Date:   Mon Oct 10 12:40:06 2016 +0200

    gitlab-ci: run tests with code coverage
parent 4f9eab9c
......@@ -93,3 +93,5 @@ ENV/
# backup files
*backup
account/sql/create-dropbox.sql
......@@ -106,6 +106,10 @@ Python 3.4.3
python3.4 ./manage.py runserver --settings=educational_heritage.settings.local
## Using HTTPS
python3.4 manage.py runserver_plus --cert /tmp/cert --settings=educational_heritage.settings.local 0.0.0.0:8000
# sqlite3
sqlite3 educational_heritage/db.sqlite3
......
"""Account package."""
"""Allows you to work with the models in the admin page."""
# from django.contrib import admin
# Register your models here.
"""include any application configuration for the app."""
from django.apps import AppConfig
class AccountConfig(AppConfig):
"""Account app configuration."""
name = 'account'
label = 'org.educationalheritage.account'
[
{
"model": "landing.subscriber",
"pk": "cad6cc1b-fb48-4c6b-a42a-403763113526",
"fields": {
"email": "[email protected]",
"date": "2016-10-13T18:18:51.394Z"
}
},
{
"model": "repository.item",
"pk": 1,
"fields": {
"title": "Libro de ejercicios de multiplicar",
"description": "Practica las tablas de multiplicar",
"image": "multiplication.png",
"url": "multiplication.pdf"
}
},
{
"model": "repository.item",
"pk": 2,
"fields": {
"title": "Libro de ejercicios de caligrafia",
"description": "Practica la caligrafia del abecedario",
"image": "calligraphy.png",
"url": "calligraphy.pdf"
}
},
{
"model": "auth.user",
"fields": {
"password": "pbkdf2_sha256$30000$4gfREH0varaK$TaqzR/y8KbNNB3iykGEnIFxMXx2CA/tTcYxtmGKeSXc=",
"last_login": null,
"is_superuser": true,
"username": "admin",
"first_name": "",
"last_name": "",
"email": "",
"is_staff": true,
"is_active": true,
"date_joined": "2016-10-12T19:49:15.551Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "auth.user",
"fields": {
"password": "!JWSe913cQOTrZ5Q9pAY6FPam7ykiJboGm1DROV1T",
"last_login": "2016-10-13T10:24:26.939Z",
"is_superuser": false,
"username": "sergio",
"first_name": "Sergio",
"last_name": "Alonso",
"email": "[email protected]",
"is_staff": false,
"is_active": true,
"date_joined": "2016-10-12T19:49:32.638Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "auth.user",
"fields": {
"password": "!lUwPoEiaseLWvPCwzxAMqlk9hz2ENEjrgjbXs87U",
"last_login": "2016-10-14T09:21:13.806Z",
"is_superuser": false,
"username": "sergio2",
"first_name": "Sergio",
"last_name": "Alonso",
"email": "[email protected]",
"is_staff": false,
"is_active": true,
"date_joined": "2016-10-13T10:30:04.699Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "sessions.session",
"pk": "21zxzz23mu0v3hlvv0aylrrtmxir2evd",
"fields": {
"session_data": "ZmUxYjhmMjYxNGRmMDc2YjJjY2I5Mzc1ZTQxMzEzMjI2YTE3ZDQ0Mjp7Il9hdXRoX3VzZXJfYmFja2VuZCI6ImFsbGF1dGguYWNjb3VudC5hdXRoX2JhY2tlbmRzLkF1dGhlbnRpY2F0aW9uQmFja2VuZCIsIl9hdXRoX3VzZXJfaGFzaCI6IjNkNDI0YzQ4ZTExZWIxZTJkOGExMGZjZTU2ZWY3OWU2MzVkY2RiMzAiLCJfYXV0aF91c2VyX2lkIjoiMyJ9",
"expire_date": "2016-10-28T09:21:14.103Z"
}
},
{
"model": "sessions.session",
"pk": "bz5u9odor22azj1aqzie03dwei7hceea",
"fields": {
"session_data": "YTA5MTQzYmJkNWI0YTAwMDgyN2RmODM0ODkzYWJmMDNiMGE2MThiODp7ImFjY291bnRfdXNlciI6IjMiLCJfYXV0aF91c2VyX2hhc2giOiIzZDQyNGM0OGUxMWViMWUyZDhhMTBmY2U1NmVmNzllNjM1ZGNkYjMwIiwiX2F1dGhfdXNlcl9pZCI6IjMiLCJfYXV0aF91c2VyX2JhY2tlbmQiOiJhbGxhdXRoLmFjY291bnQuYXV0aF9iYWNrZW5kcy5BdXRoZW50aWNhdGlvbkJhY2tlbmQiLCJhY2NvdW50X3ZlcmlmaWVkX2VtYWlsIjpudWxsfQ==",
"expire_date": "2016-10-27T10:31:31.261Z"
}
},
{
"model": "sessions.session",
"pk": "n7v5blfbg85ej777b2jpixdb4h4d53wk",
"fields": {
"session_data": "YjczNDI4ZDcyYjNjNGFhODgzYWIzYzk5OWE5ZDg5NDYyNjA1NzQ4YTp7InNvY2lhbGFjY291bnRfc3RhdGUiOlt7ImF1dGhfcGFyYW1zIjoiIiwic2NvcGUiOiIiLCJwcm9jZXNzIjoibG9naW4ifSwiUXJiM2hrbXFWN1lpIl19",
"expire_date": "2016-10-27T09:50:23.558Z"
}
},
{
"model": "sessions.session",
"pk": "p9gu83ulopccngkio0v29jabbflvyjml",
"fields": {
"session_data": "MDA5MzcyMmQ5NmQwZjBhNjdjM2MwMzBiMGIxMDc2NWNhNjZjZTMxNTp7InNvY2lhbGFjY291bnRfc3RhdGUiOlt7ImF1dGhfcGFyYW1zIjoiIiwic2NvcGUiOiIiLCJwcm9jZXNzIjoibG9naW4ifSwibjl3U1NBODVWR2JDIl19",
"expire_date": "2016-10-28T09:30:22.346Z"
}
},
{
"model": "sessions.session",
"pk": "tskahkz310fxq0vv6juux41g5d5jp68q",
"fields": {
"session_data": "ZTRiYjJmN2Q2OGMxZGNiYTEyYTY0ODM1Mzg3YjAyYTQ1NTA3NmYxODp7InNvY2lhbGFjY291bnRfc3RhdGUiOlt7ImF1dGhfcGFyYW1zIjoiIiwic2NvcGUiOiIiLCJwcm9jZXNzIjoibG9naW4ifSwiaFBXQXVFdlpwbnZYIl19",
"expire_date": "2016-10-28T09:31:58.280Z"
}
},
{
"model": "sites.site",
"fields": {
"domain": "127.0.0.1:8000",
"name": "educational_heritage"
}
},
{
"model": "account.emailaddress",
"pk": 1,
"fields": {
"user": 2,
"email": "[email protected]",
"verified": true,
"primary": true
}
},
{
"model": "account.emailaddress",
"pk": 2,
"fields": {
"user": 3,
"email": "[email protected]",
"verified": true,
"primary": true
}
},
{
"model": "socialaccount.socialapp",
"pk": 1,
"fields": {
"provider": "dropbox_oauth2",
"name": "Dropbox",
"client_id": "6x2otlvghnb6pwn",
"secret": "ct8s7hci73s09ih",
"key": "",
"sites": [
1
]
}
},
{
"model": "socialaccount.socialaccount",
"pk": 1,
"fields": {
"user": 2,
"provider": "dropbox_oauth2",
"uid": "613346083",
"last_login": "2016-10-13T10:24:26.334Z",
"date_joined": "2016-10-12T19:49:32.802Z",
"extra_data": "{\"email_verified\": true, \"name_details\": {\"surname\": \"Alonso\", \"familiar_name\": \"Sergio\", \"given_name\": \"Sergio\"}, \"email\": \"[email protected]\", \"country\": \"ES\", \"uid\": 613346083, \"team\": null, \"referral_link\": \"https://db.tt/l21wPRaN\", \"locale\": \"es_ES\", \"is_paired\": false, \"quota_info\": {\"quota\": 2147483648, \"normal\": 1100350, \"shared\": 0, \"datastores\": 0}, \"display_name\": \"Sergio Alonso\"}"
}
},
{
"model": "socialaccount.socialaccount",
"pk": 2,
"fields": {
"user": 3,
"provider": "dropbox_oauth2",
"uid": "474996168",
"last_login": "2016-10-14T09:21:12.161Z",
"date_joined": "2016-10-13T10:30:04.876Z",
"extra_data": "{\"email_verified\": true, \"name_details\": {\"surname\": \"Alonso\", \"familiar_name\": \"Sergio\", \"given_name\": \"Sergio\"}, \"email\": \"[email protected]\", \"country\": \"ES\", \"uid\": 474996168, \"team\": null, \"referral_link\": \"https://db.tt/0eBe5RDR\", \"locale\": \"es_ES\", \"is_paired\": false, \"quota_info\": {\"quota\": 2147483648, \"normal\": 860090, \"shared\": 743159, \"datastores\": 0}, \"display_name\": \"Sergio Alonso\"}"
}
},
{
"model": "socialaccount.socialtoken",
"pk": 1,
"fields": {
"app": 1,
"account": 1,
"token": "gH-_hf_P6HAAAAAAAAAAK42sjEaOhbuLzf201cgU4cM98JWegFpgtPmRH9z0M28u",
"token_secret": "",
"expires_at": null
}
},
{
"model": "socialaccount.socialtoken",
"pk": 2,
"fields": {
"app": 1,
"account": 2,
"token": "WKpWLbHxcCAAAAAAAAAEjksD5YhcApjkz84DoYyjmlmQRy9CepVw4eG5wmfixIRM",
"token_secret": "",
"expires_at": null
}
},
{
"model": "org.educationalheritage.account.userprofile",
"pk": 1,
"fields": {
"user": 2
}
},
{
"model": "org.educationalheritage.account.userprofile",
"pk": 2,
"fields": {
"user": 3
}
}
]
{"Email": "[email protected]", "Passwd": "novalepana"}
# -*- coding: utf-8 -*-
# Generated by Django 1.10 on 2016-10-11 17:31
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
]
"""Account models."""
import hashlib
from allauth.account.models import EmailAddress
from django.contrib.auth.models import User
from django.db import models
class UserProfile(models.Model):
"""UserProfile model."""
user = models.OneToOneField(User, related_name='profile')
def __unicode__(self):
"""Render a string representation."""
return "{}'s profile".format(self.user.username)
class Meta:
"""Model metadata."""
db_table = 'user_profile'
def account_verified(self):
"""Detect the user’s email-verification status."""
if self.user.is_authenticated:
result = EmailAddress.objects.filter(email=self.user.email)
if len(result):
return result[0].verified
return False
def profile_image_url(self):
"""Display the user's gravatar icon."""
return "http://www.gravatar.com/avatar/{}?s=40".format(hashlib.md5(self.user.email.encode('utf-8')).hexdigest())
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
{% extends "base.html" %}
{% load bootstrap3 %}
{% block head_title %}Welcome{% endblock %}
{% block content %}
<div class="container">
<h3>Profile</h3>
<ul class="list-unstyled">
<li><p class="picture-frame">
{% if request.user.profile.profile_image_url %}<img alt="" src="{{ request.user.profile.profile_image_url }}" style="width: 40px; height: 40px">
{% else %}No picture available yet.{% endif %}
</p></li>
<li>First name: <strong>{{user.first_name}}</strong></li>
<li>Last name: <strong>{{user.last_name}}</strong></li>
<li>Email: <strong>{{user.email}}</strong></li>
<li>Account: <strong>{% if request.user.profile.account_verified %}Verified{% else %}Unverified{% endif %}</strong></li>
</ul>
</div> <!-- container -->
{% endblock %}
"""Account tests."""
"""Account tests."""
# from django.test import TestCase
# Create your tests here.
"""Account funtional tests suite."""
import json
import unittest
from pyvirtualdisplay import Display
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
class AccountTest(unittest.TestCase):
"""Account Test Suite."""
fixtures = ['allauth_fixture']
def setUp(self):
"""Test Case set up."""
self.display = Display(visible=0, size=(1024, 768))
self.display.start()
self.browser = webdriver.Chrome()
self.browser.wait = WebDriverWait(self.browser, 10)
def tearDown(self):
"""Test Case tear down."""
self.browser.close()
self.display.stop()
def get_element_by_id(self, element_id):
"""Get element by id."""
return self.browser.wait.until(EC.presence_of_element_located((By.ID, element_id)))
def get_element_by_name(self, element_name):
"""Get element by xpath."""
return self.browser.wait.until(EC.presence_of_element_located((By.XPATH, element_name)))
def user_login(self):
"""Login user into dropbox."""
with open("account/fixtures/dropbox_user.json") as f:
credentials = json.loads(f.read())
self.get_element_by_name("//input[@name='login_email']").send_keys(credentials["Email"])
self.get_element_by_name("//input[@name='login_password']").send_keys(credentials["Passwd"])
self.get_element_by_name("//button[@type='submit']").click()
return
def test_login_with_dropbox(self):
"""Test Case: login with dropbox."""
# User goes to checkout the Open Educational Resources account
self.browser.get('https://127.0.0.1:8000/')
# Check that the login button is present
dropbox_login = self.get_element_by_id("dropbox_login")
# Check that login button points to correct url
self.assertEqual(
dropbox_login.get_attribute("href"),
"https://127.0.0.1:8000/accounts/dropbox_oauth2/login/")
# checks that after clicking on the login button
dropbox_login.click()
# could enter dropbox user credentials
self.user_login()
# the user gets logged in and it sees the logout button instead
dropbox_logout = self.get_element_by_id("logout")
# click on the logout button
dropbox_logout.click()
# should make the user see the login button again.
dropbox_login = self.get_element_by_id("dropbox_login")
"""Account views."""
# from django.shortcuts import render
# Create your views here.
......@@ -27,6 +27,7 @@ SECRET_KEY = os.environ["SECRET_KEY"]
INSTALLED_APPS = [
'landing.apps.LandingConfig',
'repository.apps.RepositoryConfig',
'account.apps.AccountConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
......@@ -34,6 +35,12 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'bootstrap3',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.dropbox_oauth2',
'django_extensions',
]
MIDDLEWARE = [
......@@ -120,3 +127,35 @@ USE_TZ = True
# https://docs.djangoproject.com/en/1.10/howto/static-files/
STATIC_URL = '/static/'
# auth and allauth settings
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)
SOCIALACCOUNT_PROVIDERS = {'dropbox_oath2': {'SCOPE': ['email']}}
SITE_ID = 1
LOGIN_REDIRECT_URL = '/account/profile/'
SOCIALACCOUNT_QUERY_EMAIL = True
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
# Use email as the primary identifier
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
# Make email verification mandatory to avoid junk email accounts
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
# Eliminate need to provide username, as it's a very old practice
ACCOUNT_USERNAME_REQUIRED = False
# Avoid two steps logout
ACCOUNT_LOGOUT_ON_GET = True
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
ACCOUNT_UNIQUE_EMAIL = True
SOCIALACCOUNT_AUTO_SIGNUP = True
......@@ -15,10 +15,13 @@ Including another URLconf
"""
from django.conf.urls import include, url
from django.contrib import admin
from django.views.generic import TemplateView
urlpatterns = [
url(r'^repository/', include('repository.urls')),
url(r'^landing/', include('landing.urls')),
url(r'^accounts/', include('allauth.urls')),
url(r'^account/profile/', TemplateView.as_view(template_name='account/profile.html')),
url(r'^admin/', admin.site.urls),
url(r'^', include('landing.urls')),
]
body {
body {
font-family: Hack, monospace;
height: 100%;
background-color: #fff;
......@@ -48,7 +48,7 @@ body {
color: #666;
}
.social-proof h3 {
.social-proof h3 {
padding: 5%;
padding-bottom: 1%;
}
......@@ -61,3 +61,9 @@ body {
text-justify: inter-word;
}
.navbar .navbar-header .navbar-toggle .icon-bar {
background-color: #666;
}
.navbar .navbar-header .navbar-toggle {
border: 1px solid #666;
}
......@@ -25,12 +25,12 @@ class OpenEducationalResourcesTest(unittest.TestCase):
def test_subscribe_into_landing_page(self):
"""Test Case: home page."""
# User goes to check out homepage
self.browser.get('http://localhost:8000')
self.browser.get('https://127.0.0.1:8000')
# She notices the page title and header mention Educational Resources
self.assertIn('Recursos Educativos', self.browser.title)
header_text = self.browser.find_element_by_tag_name('h1').text
self.assertIn('Recursos Educativos', header_text)
self.assertIn('Personas mejorando la educación', header_text)
# She is invited to enter an email address
inputbox = self.browser.find_element_by_id('id_email')
......@@ -45,7 +45,7 @@ class OpenEducationalResourcesTest(unittest.TestCase):
# When she hits enter, the page is redirected to a thanks page
inputbox.send_keys(Keys.ENTER)
self.assertIn('http://localhost:8000/landing/', self.browser.current_url)
self.assertIn('https://127.0.0.1:8000/landing/', self.browser.current_url)
rows = self.browser.find_elements_by_tag_name('p')
self.assertTrue(
......
{% extends "base.html" %}
{% extends "base.html" %}
{% load bootstrap3 %}
{% block content %}
<div class="page-header">
<h2>Recursos Educativos Abiertos</h2>
<h1>Recursos Educativos Abiertos</h1>
</div>
......@@ -26,4 +26,4 @@
</div> <!-- /row -->
{% endblock %}
{% endblock %}
......@@ -24,14 +24,12 @@ class RepositoryTest(unittest.TestCase):
def test_list_all_repository_items(self):
"""Test Case: visit repository home page."""
# User goes to checkout the Open Educational Resources repository
self.browser.get('http://localhost:8000/repository/')
self.browser.get('https://localhost:8000/repository/')
# print(self.browser.page_source)
# User notices the page title and header mention resources lists
self.assertIn('Recursos Educativos', self.browser.title)
header_text = self.browser.find_element_by_tag_name('h1').text
self.assertIn('Recursos Educativos', header_text)
header_text = self.browser.find_element_by_tag_name('h2').text
self.assertIn('Recursos Educativos Abiertos', header_text)
# Now the page lists:
......
Django==1.10
django-bootstrap3==7.0.1
django-allauth==0.27.0
# code style
......@@ -10,6 +11,8 @@ flake8-isort==2.0
# environment
awsebcli==3.7.8
Werkzeug==0.11.11
pyOpenSSL==16.1.0
# test
......
[flake8]
max-line-length = 119
max-line-length = 150
exclude = *migrations*
[isort]
......
......@@ -12,7 +12,7 @@
<link rel="icon" href="{% static "landing/ico/favicon.ico" %}">
<title>Recursos Educativos</title>
<!-- Bootstrap core CSS -->
{% bootstrap_css %}
......@@ -23,7 +23,7 @@
<!-- Custom styles for this template -->
<link href="{% static "landing/css/landing.css" %}" rel="stylesheet">
<script src="{% static "landing/js/ie-emulation-modes-warning.js" %}"></script>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
......@@ -36,16 +36,39 @@
<body>
<nav class="navbar">
<div class="container-fluid">
<nav class="navbar">
<div class="container-fluid">
<div class="navbar-header">
<h1 class="navbar-brand"><a href="/">Recursos Educativos</a></h1>
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#myNavbar">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Recursos Educativos</a>
</div>
<div class="collapse navbar-collapse">
<h1 class="navbar-brand navbar-right"><a href="/repository" class="navbar-link">Explora</a></h1>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li><a href="/repository">Explora</a></li>