1
0
mirror of https://github.com/djohnlewis/stackdump synced 2024-12-04 23:17:37 +00:00

Fleshed initial HTML views and pages.

This commit is contained in:
Samuel Lai 2011-11-01 22:03:22 +11:00
parent 5e930bbc08
commit dc7a923fa9
7 changed files with 310 additions and 13 deletions

88
python/media/css/main.css Normal file
View File

@ -0,0 +1,88 @@
/* don't want it to have a fixed position; so we'll override it */
.topbar {
position: absolute;
}
.topbar-text {
float: left;
display: block;
padding: 8px 2px 12px;
color: grey;
font-size: 20px;
font-weight: 200;
line-height: 1;
}
.topbar-text a {
color: white;
}
.topbar-divider {
padding-left: 3px;
padding-right: 3px;
}
/* leave enough gap for the topbar */
#content {
margin-top: 40px;
}
.search-container {
padding: 60px;
}
.site-logo {
float: left;
}
.site-title {
padding-left: 64px; /* 48px for logo + 16px for padding */
}
#search {
margin-top: 40px;
}
.tagline {
display: block;
}
.random-questions-container {
padding-left: 60px;
padding-right: 60px;
}
.available-sites-container {
margin-top: 40px;
}
.available-sites {
list-style-image: none;
list-style-type: none;
margin-left: 0;
}
.available-sites li {
clear: both;
margin-bottom: 16px;
}
.available-sites img {
float: left;
}
.available-sites h6 {
padding-top: 7px;
line-height: normal;
}
.available-sites .tagline {
padding-top: 3px;
}
#footer {
margin-top: 50px;
border-top: solid 1px #999999;
background-color: #CCCCCC;
padding: 7px 10px;
}

View File

@ -1,14 +1,19 @@
from bottle import route, run, static_file
from jinja2 import Environment, PackageLoader
import sys
import os
from bottle import route, run, static_file, debug, abort, request, redirect
from jinja2 import Environment, PackageLoader
from sqlobject import sqlhub, connectionForURI, AND, OR, SQLObjectNotFound
from pysolr import Solr
from stackdump.models import Site, Badge, Comment, User
# STATIC VARIABLES
BOTTLE_ROOT = os.path.abspath(os.path.dirname(sys.argv[0]))
MEDIA_ROOT = os.path.abspath(BOTTLE_ROOT + '/../../media')
# hopefully this is thread-safe; not sure though. Will need to experiment/check.
# TODO: thread-safe?
TEMPLATE_ENV = Environment(loader=PackageLoader('stackdump', 'templates'))
# WEB REQUEST METHODS
@ -18,13 +23,70 @@ TEMPLATE_ENV = Environment(loader=PackageLoader('stackdump', 'templates'))
def serve_static(filename):
return static_file(filename, root=MEDIA_ROOT)
@route('/hello')
def hello():
return "Hello World!"
@route('/')
def index():
return render_template('index.html')
context = { }
context['site_root_path'] = ''
context['sites'] = Site.select()
return render_template('index.html', context)
@route('/:site_key#\w+#')
@route('/:site_key#\w+#/')
def site_index(site_key):
context = { }
context['site_root_path'] = '%s/' % site_key
try:
context['site'] = Site.selectBy(key=site_key).getOne()
except SQLObjectNotFound:
abort(code=404, output='No site exists with the key %s.' % site_key)
return render_template('site_index.html', context)
@route('/search')
def search():
query = request.GET.get('q')
if not query:
redirect(settings.APP_URL_ROOT)
page = request.GET.get('p', 0)
rows_per_page = request.GET.get('r', 10)
# perform search
results = solr.search(query, start=page*rows_per_page, rows=rows_per_page)
context = { }
# TODO: scrub this first to avoid injection attacks?
context['query'] = query
context['results'] = results
return render_template('results.html', context)
@route('/:site_key#\w+#/search')
def site_search(site_key):
context = { }
context['site_root_path'] = '%s/' % site_key
try:
context['site'] = Site.selectBy(key=site_key).getOne()
except SQLObjectNotFound:
raise HTTPError(code=404, output='No site exists with the key %s.' % site_key)
query = request.GET.get('q')
if not query:
redirect(settings.APP_URL_ROOT)
page = request.GET.get('p', 0)
rows_per_page = request.GET.get('r', 10)
# perform search
results = solr.search(query, start=page*rows_per_page, rows=rows_per_page)
# TODO: scrub this first to avoid injection attacks?
context['query'] = query
context['results'] = results
return render_template('site_results.html', context)
# END WEB REQUEST METHODS
@ -51,11 +113,27 @@ def get_template_settings():
# INITIALISATION
if __name__ == '__main__':
# only print this on the parent process, not the child ones. Applies when
# only do these things in the child processes, not the parents. Applies when
# the auto-reload option is on (reloader=True). When it is on, the
# BOTTLE_CHILD env var is True if this is the child process.
if not os.environ.get('BOTTLE_CHILD', True):
if os.environ.get('BOTTLE_CHILD', True):
print('Serving media from: %s' % MEDIA_ROOT)
# connect to the data sources
db_path = os.path.abspath(os.path.join(BOTTLE_ROOT, '../../../data/stackdump.sqlite'))
# connect to the database
# TODO: thread-safe?
print('Connecting to the database...')
conn_str = 'sqlite://' + db_path
sqlhub.processConnection = connectionForURI(conn_str)
print('Connected.\n')
# connect to solr
# TODO: thread-safe?
print('Connecting to solr...')
solr = Solr("http://localhost:8983/solr/")
print('Connected.\n')
# load the settings file
__import__('settings')
@ -65,6 +143,9 @@ if __name__ == '__main__':
else:
settings = { }
if settings.get('DEBUG', False):
debug(True)
# run the server!
server = settings.get('SERVER_ADAPTER', 'wsgiref')

View File

@ -3,6 +3,8 @@
# It is modelled after the Django settings file. This file is just like any
# other Python file, except the local variables form the settings dictionary.
DEBUG = True
# see http://bottlepy.org/docs/dev/tutorial.html#multi-threaded-server
SERVER_ADAPTER = 'cherrypy'
SERVER_HOST = '0.0.0.0'

View File

@ -10,7 +10,38 @@
</head>
<body>
{%- block body %}
{% endblock -%}
<div class="topbar">
<div class="fill">
<div class="container">
<span class="topbar-text">
<a href="{{ SETTINGS.APP_URL_ROOT }}">Stackdump</a>
{% if site %}
<span class="topbar-divider">//</span>
<a href="{{ SETTINGS.APP_URL_ROOT }}{{ site_root_path }}">{{ site.name }}</a>
from {{ site.dump_date }}
{% endif %}
</span>
<form method="get" action="{{ SETTINGS.APP_URL_ROOT }}{{ site_root_path }}search" class="pull-right">
<input type="text" placeholder="Search" />
</form>
</div>
</div>
</div>
<div id="content">
<div class="container">
{%- block body %}
{% endblock -%}
</div>
</div>
<div id="footer">
Stackdump is licensed under
<a href="http://en.wikipedia.org/wiki/MIT_License">MIT License</a>. The
contents is licensed under <a href="http://creativecommons.org/licenses/by-sa/3.0/">
Creative Commons [cc-wiki]</a>.
</div>
</body>
</html>

View File

@ -3,5 +3,49 @@
{% block title %}Stackdump Home{% endblock %}
{% block body %}
Welcome to stackdump.
<div class="row">
<div class="span12">
<div class="search-container">
<h1>
Welcome to Stackdump,
<small class="tagline">the offline browser for StackExchange sites.</small>
</h1>
<form id="search" method="get" action="{{ SETTINGS.APP_URL_ROOT }}search">
<input type="text" class="xlarge" name="q" />
<input type="submit" class="btn primary" value="Search" />
</form>
</div>
<div class="random-questions-container">
<h3>Random questions</h3>
<ul class="random-questions">
<li>
Question 1
</li>
<li>
Question 2
</li>
<li>
Question 3
</li>
</ul>
</div>
</div>
<div class="span4">
<div class="well available-sites-container">
<h3>Available sites</h3>
<ul class="available-sites">
{% for s in sites %}
<li>
<img src="{{ SETTINGS.APP_URL_ROOT }}media/images/logos/{{ s.key }}.png" alt="{{ s.name }} logo" />
<h6>
<a href="{{ SETTINGS.APP_URL_ROOT }}{{ s.key }}/">{{ s.name }}</a>
<small class="tagline">{{ s.dump_date }}</small>
</h6>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,15 @@
{% extends 'base.html' %}
{% block title %}Stackdump search for "{{ query }}"{% endblock %}
{% block body %}
<div class="row">
<div class="span16">
<ul id="search-results">
{% for r in results %}
<li>{{ r }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends 'base.html' %}
{% block title %}Stackdump // {{ site.name }} Home{% endblock %}
{% block body %}
<div class="row">
<div class="span16">
<div class="search-container">
<img class="site-logo" src="{{ SETTINGS.APP_URL_ROOT }}media/images/logos/{{ site.key }}.png" alt="{{ site.name }} logo" />
<h1 class="site-title">
{{ site.name }}
<small class="tagline">{{ site.desc }}</small>
</h1>
<form id="search" method="post" action="{{ SETTINGS.APP_URL_ROOT }}{{ site_root_path }}search">
<input type="text" class="xlarge" name="q" />
<input type="submit" class="btn primary" value="Search" />
</form>
</div>
<div class="random-questions-container">
<h3>Random questions</h3>
<ul class="random-questions">
<li>
Question 1
</li>
<li>
Question 2
</li>
<li>
Question 3
</li>
</ul>
</div>
</div>
</div>
{% endblock %}