Removing books interface.

This commit is contained in:
Robert Tice
2018-04-20 11:01:14 -04:00
parent c20c0cc803
commit 5c8de067a8
25 changed files with 24 additions and 812 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,8 @@
*.pyc
db.sqlite3
env
.DS_Store
.idea

View File

@@ -35,11 +35,6 @@ To enter many examples in the database where the examples are listed in a yaml f
./manage.py fill_examples --conf=resources/test_conf.yaml
```
To enter some books in the database, do this:
```sh
./manage.py fill_books --dir=resources/books/example
```
To launch the server, do this:
```sh
./manage.py runserver

View File

@@ -1,60 +0,0 @@
# The manage.py command to enter books in the database.
# This is meant to be used by the administrators of the project only.
import os
import yaml
from django.core.management.base import BaseCommand
from compile_server.app.models import Book
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('--remove_all', const=True, default=False,
action='store_const',
help='remove all books from the database')
parser.add_argument('--dir', nargs=1, type=str,
help='add the book found in the given dir')
def handle(self, *args, **options):
if options.get('remove_all', False):
# Remove all books from the database
Book.objects.all().delete()
if options.get('dir', None):
d = os.path.abspath(options.get('dir')[0])
# Consider directory as the book
if not os.path.isdir(d):
print "{} is not a valid directory".format(d)
return
# Look for 'chapters.yaml'
chapters_yaml = os.path.join(d, 'info.yaml')
if not os.path.isfile(chapters_yaml):
print 'There is no "chapters.yaml" in {}'.format(d)
return
# Check contents of chapters.yaml
with open(chapters_yaml, 'rb') as f:
try:
metadata = yaml.load(f.read())
except:
print format_traceback
print 'Could not decode yaml in {}'.format(chapters_yaml)
return
for field in ['title', 'description']:
if field not in metadata:
print 'chapters.yaml should contain a field {}'.format(
field)
return
b = Book(description=metadata['description'],
directory=d,
subpath=os.path.basename(os.path.normpath(d)),
title=metadata['title'],
author=metadata['author'])
b.save()

View File

@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2018-04-20 14:56
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('app', '0004_book_author'),
]
operations = [
migrations.DeleteModel(
name='Book',
),
]

View File

@@ -48,24 +48,3 @@ class Example(models.Model):
# An example is a contains a set of resources
resources = models.ManyToManyField(Resource)
class Book(models.Model):
""" The represents a book """
# the directory in relation to resources/books
# that has the resources for this book
directory = models.TextField()
# This is the name of the book formatted as a url subdomain
# This is be the same name of the folder where the book lives under resources
subpath = models.TextField()
# A description of the book
description = models.TextField()
# the title of the book
title = models.TextField()
# the author of the book
author = models.TextField()

View File

@@ -1,6 +1,6 @@
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from compile_server.app.models import Resource, Example, Book
from compile_server.app.models import Resource, Example
class UserSerializer(serializers.HyperlinkedModelSerializer):
@@ -38,8 +38,3 @@ class ExampleSerializer(serializers.ModelSerializer):
model = Example
fields = ('name', 'description', 'main')
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('title', 'description', 'directory', 'subpath')

View File

@@ -0,0 +1 @@
ace-builds

View File

@@ -1,291 +0,0 @@
body {
color: #777;
}
.pure-img-responsive {
max-width: 100%;
height: auto;
}
/*
Add transition to containers so they can push in and out.
*/
#wrapper,
#sidebar,
.menu-link {
-webkit-transition: all 0.2s ease-out;
-moz-transition: all 0.2s ease-out;
-ms-transition: all 0.2s ease-out;
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
/*
This is the parent `<div>` that contains the menu and the content area.
*/
#wrapper {
position: relative;
left: 0;
padding-left: 0;
}
#wrapper.active #sidebar {
left: 300px;
width: 300px;
}
#wrapper.active .menu-link {
left: 300px;
}
/*
The content `<div>` is where all your content goes.
*/
#main {
margin: 0 0 0 330px;
}
.content {
margin: 0 auto;
padding: 0 2em;
max-width: 800px;
margin-bottom: 50px;
line-height: 1.6em;
}
.header {
margin: 0;
color: #333;
text-align: center;
padding: 2.5em 2em 0;
border-bottom: 1.5px solid #eee;
position: -webkit-sticky;
position: sticky;
top: 0;
background: white;
}
.header h1 {
margin: 0.2em 0;
font-size: 3em;
font-weight: 300;
}
.header h2 {
font-weight: 300;
color: #ccc;
padding: 0;
margin-top: 0;
}
.content-subhead {
margin: 50px 0 20px 0;
font-weight: 300;
color: #888;
}
/*
The `#sidebar` `<div>` is the parent `<div>` that contains the `.pure-menu` that
appears on the left side of the page.
*/
#sidebar {
margin-left: -300px;
/* "#sidebar" width */
width: 300px;
position: fixed;
top: 0;
left: 0;
bottom: 0;
z-index: 1000;
/* so the menu or its navicon stays above all content */
background: #191818;
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch;
}
/*
All anchors inside the menu should be styled like this.
*/
#sidebar a {
color: #999;
border: none;
padding: 0.6em 0.6em 0.6em 0.6em;
white-space: normal;
}
/*
Remove all background/borders, since we are applying them to #sidebar.
*/
#sidebar .pure-menu,
#sidebar .pure-menu ul {
border: none;
background: transparent;
}
/*
Add that light border to separate items into groups.
*/
#sidebar .pure-menu ul,
#sidebar .pure-menu .menu-item-divided {
border-top: 1px solid #333;
}
#sidebar ul ul {
margin-left: 15px;
}
/*
Change color of the anchor links on hover/focus.
*/
#sidebar .pure-menu li a:hover,
#sidebar .pure-menu li a:focus {
background: #333;
}
/*
This styles the selected menu item `<li>`.
*/
#sidebar .pure-menu-selected,
#sidebar .pure-menu-heading {
background: #1f8dd6;
color: white;
}
/*
This styles a link within a selected menu item `<li>`.
*/
#sidebar .pure-menu-selected a {
color: #fff;
}
/*
This styles the menu heading.
*/
#sidebar .pure-menu-heading {
font-size: 110%;
color: #fff;
margin: 0;
}
/* -- Dynamic Button For Responsive Menu -------------------------------------*/
/*
The button to open/close the Menu is custom-made and not part of Pure. Here's
how it works:
*/
/*
`.menu-link` represents the responsive menu toggle that shows/hides on
small screens.
*/
.menu-link {
position: fixed;
display: block;
/* show this only on small screens */
top: 0;
left: 0;
/* "#sidebar width" */
background: #000;
background: rgba(0, 0, 0, 0.7);
font-size: 10px;
/* change this value to increase/decrease button size */
z-index: 10;
width: 2em;
height: auto;
padding: 2.1em 1.6em;
}
.menu-link:hover,
.menu-link:focus {
background: #000;
}
.menu-link span {
position: relative;
display: block;
}
.menu-link span,
.menu-link span:before,
.menu-link span:after {
background-color: #fff;
width: 100%;
height: 0.2em;
}
.menu-link span:before,
.menu-link span:after {
position: absolute;
margin-top: -0.6em;
content: " ";
}
.menu-link span:after {
margin-top: 0.6em;
}
/* -- Responsive Styles (Media Queries) ------------------------------------- */
/*
Hides the menu at `48em`, but modify this based on your app's needs.
*/
@media (min-width: 1165px) {
.header,
.content {
padding-left: 2em;
padding-right: 2em;
}
#wrapper {
padding-left: 150px;
/* left col width "#sidebar" */
left: 0;
}
#sidebar {
left: 300px;
}
.menu-link {
position: fixed;
left: 300px;
display: none;
}
#wrapper.active .menu-link {
left: 300px;
}
}
@media (max-width: 1165px) {
/* Only apply this when the window is small. Otherwise, the following
case results in extra padding on the left:
* Make the window small.
* Tap the menu to trigger the active state.
* Make the window large again.
*/
#wrapper.active {
position: relative;
left: 300px;
}
}

View File

@@ -1,67 +0,0 @@
$( document ).ready( function() {
$( document ).on( "scroll", onScroll);
$( ".toc-link" ).click( function(e) {
var hid = lid2hid( $( this ).attr( 'id' ) );
var div_position = $( hid ).offset().top;
var padding = 12;
var header_offset = $( "#title_header" ).outerHeight();
$( 'html, body' ).animate( {
scrollTop: (div_position - header_offset - padding)
}, 800);
if ( $( "#sidebar" ).hasClass( 'active' ) )
toggleAll(e);
});
$( "#sidebarLink" ).click( toggleAll );
$( "#main" ).click( function(e) {
if ( $( "#sidebar" ).hasClass( 'active' ) )
toggleAll(e);
});
onScroll();
});
function lid2hid(lid) {
return "#header" + lid.substr(5);
}
function onScroll() {
var active = "pure-menu-selected"
$( '#toc a' ).each( function () {
var refElement = $( lid2hid( $( this ).attr( 'id' ) ) );
var currLink = $( this );
if ( inView( refElement) ) {
$( '#toc ul li a' ).removeClass( active );
currLink.addClass( active );
}
});
}
function inView(elem) {
var viewport = {};
viewport.top = $( window ).scrollTop();
viewport.bottom = viewport.top + $( window ).height();
var bounds = {};
bounds.top = $( elem ).offset().top;
bounds.bottom = bounds.top + $( elem ).outerHeight();
return ((bounds.top <= viewport.bottom) && (bounds.bottom >= viewport.top));
}
function toggleAll(e) {
var active = 'active';
e.preventDefault();
$( "#wrapper" ).toggleClass( active );
$( "#sidebar" ).toggleClass( active );
$( "#sidebarLink" ).toggleClass( active );
}

View File

@@ -1,31 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Book Reader</title>
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.0/build/pure-min.css" integrity="sha384-nn4HPE8lTHyVtfCBi5yW9d20FjT8BJwUXyWZT9InLYax14RDjBj46LmSztkmNP9w" crossorigin="anonymous">
<link rel="stylesheet" href="{% static "common.css" %}" />
<link rel="stylesheet" href="{% static "book_base.css" %}" />
</head>
<body>
<div id="wrapper">
{% block reader %}{% endblock %}
</div>
<script src="http://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
<script src="{% static "ace-builds/src/ace.js" %}" type="text/javascript" charset="utf-8"></script>
<script src="{% static "editors.js" %}"></script>
<script src="{% static "sidebar.js" %}"></script>
</body>
</html>

View File

@@ -1,48 +0,0 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>AdaCore Books</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="{% static "book_base.css" %}" />
</head>
<body>
<div class="content">
<table class="table table-hover">
<thead>
<tr>
<th>AdaCore Books</th>
</tr>
</thead>
<tbody>
{% for b in books %}
<tr class='clickable-row' data-href="{{ HTTP_HOST }}/books/{{ b.subpath }}">
<td>{{ b.title }}</td>
<td>{{ b.description }}</td>
<td>{{ b.author }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<script src="{% static "ace-builds/src/ace.js" %}" type="text/javascript" charset="utf-8"></script>
<script src="{% static "jquery-3.2.1.min.js" %}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="{% static "editors.js" %}"></script>
<script type=""text/javascript">
jQuery(document).ready(function($) {
$(".clickable-row").click(function() {
window.location = $(this).data("href");
});
});
</script>
</body>
</html>

View File

@@ -1,23 +0,0 @@
{% extends 'book_base.html' %} {% block reader %}
<!-- Menu toggle -->
<a href="#sidebar" id="sidebarLink" class="menu-link">
<!-- Hamburger icon -->
<span></span>
</a>
<div id="sidebar">
<div class="pure-menu">
<div id="toc">{{ sidebar|safe }}</div>
</div>
</div>
<div id="main">
<div id="title_header" class="header">
<h1>{{ book_info.title }}</h1>
<h2><a href="{{ HTTP_HOST }}/books/">Back to book list</a></h2>
</div>
<div id="content" class="content">{{ content|safe }}</div>
</div>
{% endblock%}

View File

@@ -1,8 +0,0 @@
from django import template
import markdown
register = template.Library()
@register.filter
def markdownify(text):
return markdown.markdown(text)

View File

@@ -1,10 +0,0 @@
from django import template
import docutils
register = template.Library()
@register.filter
def rstify(text):
html = docutils.core.publish_parts(text, writer_name='html')['html_body']
print html
return html

View File

@@ -4,11 +4,6 @@ from __future__ import unicode_literals
from django.shortcuts import render
# Create your views here.
from bs4 import BeautifulSoup
from docutils import core
import markdown
import os
import yaml
from django.conf import settings
@@ -21,10 +16,9 @@ from rest_framework.response import Response
from compile_server.app.serializers import (UserSerializer,
GroupSerializer,
ResourceSerializer,
ExampleSerializer,
BookSerializer)
ExampleSerializer)
from compile_server.app.models import Resource, Example, Book
from compile_server.app.models import Resource, Example
class UserViewSet(viewsets.ModelViewSet):
@@ -113,118 +107,3 @@ def code_embed(request, example_name):
def examples_list(request):
context = {'examples': Example.objects.all}
return render(request, 'examples_list.html', context)
def book_list(request):
booklist = {'books': Book.objects.all}
return render(request, 'book_list.html', booklist)
def md_filter(text):
return markdown.markdown(text)
def rst_filter(text):
parts = core.publish_parts(text, writer_name='html')
return parts['body_pre_docinfo'] + parts['fragment']
def toc_filter(htmldata):
def append_li(ul, i, h):
h['id'] = "header" + str(i)
new_link_tag = toc_soup.new_tag('a', href='#')
new_link_tag.string = h.string
new_link_tag['class'] = "toc-link pure-menu-link"
new_link_tag['id'] = "hlink" + str(i)
new_li_tag = toc_soup.new_tag('li')
new_li_tag['class'] = "pure-menu-item"
new_li_tag.append(new_link_tag)
ul.append(new_li_tag)
def append_ul(ul):
new_ul = toc_soup.new_tag('ul')
new_ul['class'] = "pure-menu-list"
ul.append(new_ul)
return new_ul
prev_level = 1
reader_soup = BeautifulSoup(htmldata['content'], "html.parser")
toc_soup = BeautifulSoup(htmldata['sidebar'], "html.parser")
current_ul = toc_soup.ul
headers = reader_soup.find_all(['h1', 'h2'])
for i, h in enumerate(headers):
cur_level = int(h.name[1:])
if cur_level < prev_level:
outer_ul = current_ul.find_parent('ul')
if outer_ul is not None:
prev_level = cur_level
current_ul = outer_ul
elif cur_level > prev_level:
prev_level = cur_level
current_ul = append_ul(current_ul)
append_li(current_ul, i, h)
htmldata['content'] = str(reader_soup)
htmldata['sidebar'] = str(toc_soup)
return htmldata
def book_router(request, subpath):
resources_base_path = os.path.join(settings.RESOURCES_DIR, "books")
matches = Book.objects.filter(subpath=subpath)
if not matches:
booklist = {'books': Book.object.all}
return render(request, 'book_list.html', booklist)
bk = matches[0]
serializer = BookSerializer(bk)
book = serializer.data
# open book info
with open(os.path.join(book['directory'], "info.yaml"), 'r') as f:
try:
bookdata = yaml.load(f.read())
except:
print format_traceback
print 'Could not decode yaml in {}'.format(book['directory'])
return
# store chapters and parts list in htmldata
htmldata = {}
htmldata['book_info'] = book
htmldata['content'] = ''
htmldata['sidebar'] = '<ul class="pure-menu-list"></ul>'
# get list of pages from info.yaml, convert to html, and concat into string
for page in bookdata['pages']:
filepath = os.path.join(book['directory'], page)
filename, file_ext = os.path.splitext(filepath)
if os.path.isfile(filepath):
with open(filepath, 'r') as f:
content = f.read()
if file_ext == '.md':
htmldata['content'] += md_filter(content)
elif file_ext == '.rst':
htmldata['content'] += rst_filter(content)
else:
with open(os.path.join(resources_base_path, "under-construction.md")) as f:
htmldata['content'] += md_filter(f.read())
htmldata = toc_filter(htmldata)
return render(request, 'readerpage.html', htmldata)

View File

@@ -55,12 +55,6 @@ urlpatterns = [
# Get a list of all the examples
url(r'^examples_list/', views.examples_list),
# URL router for Books
url(r'^books/(.+)', views.book_router),
# URL router for Book landing
url(r'^books', views.book_list),
# The landing page
url(r'', views.examples_list),
]

View File

@@ -54,16 +54,3 @@ for a in $list; do ./manage.py fill_examples --dir=resources/Compile_And_Prove_D
./manage.py fill_examples --remove_all
./manage.py fill_exmaples --conf=resources/test_conf.yaml
################
# Import books #
################
# TODO: add a facility to do this
# Setup:
cd resources/books ; git clone https://github.com/Robert-Tice/Ada_For_The_CPP_Java_Developer.git ; git clone https://github.com/Robert-Tice/Ada_for_the_C_Programmer_Book.git
./manage.py fill_books --remove_all
list="Ada_for_the_C_Programmer_Book Ada_For_The_CPP_Java_Developer"
for a in $list; do ./manage.py fill_books --dir=resources/books/$a ; done

View File

@@ -1,12 +0,0 @@
---
title: "Example Book"
description: "This is an example book that is a placeholder."
author: "Joe Schmo"
pages:
- "part1-chapter1.md"
- "part1-chapter2.rst"
- "part2-chapter1.md"
- "part2-chapter2.rst"
- "part2-chapter1.rst"
...

View File

@@ -1,32 +0,0 @@
Part 1 Chapter 1
=========================
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id massa felis. Nam eget risus sit amet ante tempor lacinia. Mauris ut nunc sem. Cras mattis, nibh quis fermentum porttitor, arcu tortor porttitor magna, ac adipiscing quam urna at lectus. Ut at dolor in elit tempor ultrices sagittis sed lacus. Nullam a lectus mauris. Pellentesque molestie, leo in auctor semper, magna sem mattis tellus, a consectetur nisl tellus volutpat quam. Etiam ultricies risus sed sapien convallis aliquet. Curabitur vehicula purus vitae justo commodo facilisis. Quisque at porta ipsum. Sed purus leo, mattis sed ultricies ac, ultricies eget lacus. Sed ac nibh est. Suspendisse sed orci nisl. Vestibulum ultrices metus sapien, sed interdum nunc. In arcu neque, sollicitudin ut porta eu, viverra at elit. Nam accumsan condimentum metus nec accumsan. Nunc porta consectetur nisi in ornare. Vestibulum tempor mollis dui quis luctus. Aliquam dolor enim, tristique a blandit eu, auctor ut odio.
Test Editor
------------
This is a test of inserting code blocks in the markdown
<div example_editor="Hello world runnable example" inline="true">
<div class="resource" region="editable_1">
Put_Line ("Hello, World Editable 1");
</div>
</div>
<div example_editor="Hello world runnable example" inline="true" readonly="true">
<div class="resource" region="editable_1">
for I in 1 .. 10 loop
Put_Line ("Hello, World " & I'Img);
end loop;
</div>
</div>
Fusce molestie commodo nisi, bibendum dictum purus vehicula vel. Nulla mattis lobortis ipsum ac ornare. Integer sed erat vel mauris volutpat sodales nec auctor nulla. Aliquam ac adipiscing erat. Aliquam erat volutpat. Aenean fringilla congue odio non mattis. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Quisque porta vehicula diam, et placerat eros iaculis et. Cras nec ante ipsum, eu cursus sem. Suspendisse nec orci neque, ac egestas sapien.
Pellentesque augue neque, bibendum eu elementum non, aliquam non nunc. Sed eget dolor vel nibh tincidunt viverra. Pellentesque elementum magna id dolor sodales ut feugiat ligula volutpat. Proin ultricies, quam id viverra dictum, velit nulla varius eros, fringilla vestibulum felis arcu at felis. Vivamus neque metus, gravida non dictum vehicula, pulvinar a tortor. In tristique ullamcorper diam vel ultrices. Nam lobortis lectus eget tortor ullamcorper et suscipit sapien placerat. Cras urna neque, laoreet at dictum vel, elementum sed dolor. Duis sem eros, tincidunt quis venenatis in, facilisis non dolor. Phasellus leo diam, semper in tempus et, tempor vel sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Mauris et eros erat, elementum iaculis velit. Donec nec quam felis, sed consequat velit. Nam dolor velit, bibendum vitae convallis id, semper sit amet diam. Integer auctor ultrices metus luctus mattis. Nulla sagittis suscipit arcu, et consequat ipsum dignissim eu. Nulla laoreet libero eget erat fermentum id porttitor odio blandit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nullam eget nunc eget tortor pretium sodales. Curabitur dapibus tristique lorem nec dignissim. Nullam pretium laoreet arcu ut semper. Nam ac risus et lacus auctor tempor. Curabitur bibendum, est tristique fermentum mattis, quam libero rutrum enim, at interdum dolor nisi ac nisi. Morbi ac tellus sem. In ornare vehicula risus sit amet venenatis. Pellentesque magna tellus, suscipit ac dapibus ut, rhoncus nec ante. Nullam ac purus eu massa vestibulum luctus a sed erat.
Nam lobortis aliquam scelerisque. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce auctor nisl feugiat nunc iaculis malesuada. Vestibulum neque nunc, adipiscing lacinia vehicula ut, blandit at diam. Pellentesque lobortis, justo non sollicitudin vestibulum, est tortor mattis sem, at pulvinar ante felis non ligula. Integer consequat congue adipiscing. Proin ut sodales nunc. Ut eleifend venenatis aliquam. Phasellus et viverra mauris. Quisque commodo sodales feugiat. Proin dignissim mollis quam, vel tristique velit sodales in. Aliquam egestas euismod venenatis.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam id massa felis. Nam eget risus sit amet ante tempor lacinia. Mauris ut nunc sem. Cras mattis, nibh quis fermentum porttitor, arcu tortor porttitor magna, ac adipiscing quam urna at lectus. Ut at dolor in elit tempor ultrices sagittis sed lacus. Nullam a lectus mauris. Pellentesque molestie, leo in auctor semper, magna sem mattis tellus, a consectetur nisl tellus volutpat quam. Etiam ultricies risus sed sapien convallis aliquet. Curabitur vehicula purus vitae justo commodo facilisis. Quisque at porta ipsum. Sed purus leo, mattis sed ultricies ac, ultricies eget lacus. Sed ac nibh est. Suspendisse sed orci nisl. Vestibulum ultrices metus sapien, sed interdum nunc. In arcu neque, sollicitudin ut porta eu, viverra at elit. Nam accumsan condimentum metus nec accumsan. Nunc porta consectetur nisi in ornare. Vestibulum tempor mollis dui quis luctus. Aliquam dolor enim, tristique a blandit eu, auctor ut odio.

Some files were not shown because too many files have changed in this diff Show More