Files
i2p.www/i2p2www/__init__.py

336 lines
10 KiB
Python
Raw Normal View History

2012-12-19 12:00:12 +00:00
from flask import Flask, request, g, redirect, url_for, abort, render_template, send_from_directory, safe_join
from flaskext.babel import Babel
from werkzeug.routing import BaseConverter
2012-06-03 01:06:09 +00:00
from docutils.core import publish_parts
2012-06-01 21:15:42 +00:00
import os.path
2012-06-01 23:15:41 +00:00
import os
2012-12-05 01:21:34 +00:00
from random import randint
try:
import json
except ImportError:
import simplejson as json
2012-06-01 21:15:42 +00:00
from helpers import LazyView, Pagination
2012-06-01 21:15:42 +00:00
CURRENT_I2P_VERSION = '0.9.4'
CANONICAL_DOMAIN = 'www.i2p2.de'
2012-06-01 21:15:42 +00:00
TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), 'pages')
2012-06-01 23:15:41 +00:00
STATIC_DIR = os.path.join(os.path.dirname(__file__), 'static')
2012-06-03 01:06:09 +00:00
BLOG_DIR = os.path.join(os.path.dirname(__file__), 'blog')
MEETINGS_DIR = os.path.join(os.path.dirname(__file__), 'meetings/logs')
2012-06-01 21:15:42 +00:00
BLOG_ENTRIES_PER_PAGE = 20
MEETINGS_PER_PAGE = 20
2012-12-05 01:21:34 +00:00
MIRRORS_FILE = os.path.join(TEMPLATE_DIR, 'downloads/mirrors')
app = application = Flask('i2p2www', template_folder=TEMPLATE_DIR, static_url_path='/_static', static_folder=STATIC_DIR)
2012-06-01 21:15:42 +00:00
app.debug = bool(os.environ.get('APP_DEBUG', 'False'))
babel = Babel(app)
#######################
# Custom URL converters
class LangConverter(BaseConverter):
def __init__(self, url_map):
super(LangConverter, self).__init__(url_map)
self.regex = '(?:[a-z]{2})(-[a-z]{2})?'
def to_python(self, value):
parts = value.split('-')
if len(parts) == 2:
return parts[0] + '_' + parts[1].upper()
return value
def to_url(self, value):
parts = value.split('_')
if len(parts) == 2:
return parts[0] + '-' + parts[1].lower()
return value
app.url_map.converters['lang'] = LangConverter
######
# URLs
def url(url_rule, import_name, **options):
view = LazyView('i2p2www.' + import_name)
app.add_url_rule(url_rule, view_func=view, **options)
url('/', 'views.main_index')
url('/<lang:lang>/', 'views.site_show', defaults={'page': 'index'})
url('/<lang:lang>/<path:page>', 'views.site_show')
url('/<lang:lang>/blog/', 'blog.views.blog_index', defaults={'page': 1})
url('/<lang:lang>/blog/page/<int:page>', 'blog.views.blog_index')
url('/<lang:lang>/blog/entry/<path:slug>', 'blog.views.blog_entry')
url('/<lang:lang>/feed/blog/rss', 'blog.views.blog_rss')
url('/<lang:lang>/feed/blog/atom', 'blog.views.blog_atom')
url('/<lang:lang>/meetings/', 'meetings.views.meetings_index', defaults={'page': 1})
url('/<lang:lang>/meetings/page/<int:page>', 'meetings.views.meetings_index')
url('/<lang:lang>/meetings/<int:id>', 'meetings.views.meetings_show')
url('/<lang:lang>/meetings/<int:id>.log', 'meetings.views.meetings_show_log')
url('/<lang:lang>/meetings/<int:id>.rst', 'meetings.views.meetings_show_rst')
url('/<lang:lang>/feed/meetings/atom', 'meetings.views.meetings_atom')
2012-12-19 07:59:22 +00:00
2012-12-19 10:30:49 +00:00
url('/meeting<int:id>', 'legacy.legacy_meeting')
url('/meeting<int:id>.html', 'legacy.legacy_meeting')
url('/status-<int:year>-<int:month>-<int:day>', 'legacy.legacy_status')
url('/status-<int:year>-<int:month>-<int:day>.html', 'legacy.legacy_status')
url('/<string:f>_<lang:lang>', 'legacy.legacy_show')
url('/<string:f>_<lang:lang>.html', 'legacy.legacy_show')
url('/<string:f>/', 'legacy.legacy_show')
2012-12-19 10:30:49 +00:00
url('/<string:f>.html', 'legacy.legacy_show')
#################
# Babel selectors
@babel.localeselector
def get_locale():
# If the language is already set from the url, use that
if hasattr(g, 'lang'):
return g.lang
# otherwise try to guess the language from the user accept
# header the browser transmits. The best match wins.
return request.accept_languages.best_match(['en', 'es', 'zh', 'de', 'fr', 'it', 'nl', 'ru', 'sv', 'cs', 'ar'])
2012-06-01 21:15:42 +00:00
2012-09-10 12:14:29 +00:00
##########################
# Hooks - helper functions
def after_this_request(f):
if not hasattr(g, 'after_request_callbacks'):
g.after_request_callbacks = []
g.after_request_callbacks.append(f)
return f
2012-09-10 12:14:29 +00:00
###########################
# Hooks - url preprocessing
@app.url_value_preprocessor
def pull_lang(endpoint, values):
if not values:
return
g.lang=values.pop('lang', None)
2012-06-03 01:06:09 +00:00
@app.url_defaults
def set_lang(endpoint, values):
if not values:
return
if endpoint == 'static':
# Static urls shouldn't have a lang flag
# (causes complete reload on lang change)
return
2012-06-03 01:06:09 +00:00
if 'lang' in values:
return
if hasattr(g, 'lang'):
values['lang'] = g.lang
2012-09-10 12:14:29 +00:00
########################
# Hooks - before request
# Detect and store chosen theme
@app.before_request
def detect_theme():
theme = 'duck'
if 'style' in request.cookies:
theme = request.cookies['style']
if 'theme' in request.args.keys():
theme = request.args['theme']
2012-12-14 21:51:45 +00:00
if not os.path.isfile(safe_join(safe_join(STATIC_DIR, 'styles'), '%s.css' % theme)):
theme = 'duck'
g.theme = theme
@after_this_request
def remember_theme(resp):
if g.theme == 'duck' and 'style' in request.cookies:
resp.delete_cookie('style')
elif g.theme != 'duck':
resp.set_cookie('style', g.theme)
return resp
2012-06-01 23:15:41 +00:00
2012-09-10 12:14:29 +00:00
#######################
# Hooks - after request
@app.after_request
def call_after_request_callbacks(response):
for callback in getattr(g, 'after_request_callbacks', ()):
response = callback(response)
return response
##################
# Template filters
@app.template_filter('restructuredtext')
def restructuredtext(value):
parts = publish_parts(source=value, writer_name="html")
return parts['html_body']
####################
# Context processors
@app.context_processor
def utility_processor():
# Shorthand for getting a site url
def get_site_url(path=None):
lang = 'en'
if hasattr(g, 'lang') and g.lang:
lang = g.lang
if path:
return url_for('site_show', lang=lang, page=path)
else:
return url_for('site_show', lang=lang)
# Provide the canonical link to the current page
def get_canonical_link():
protocol = request.url.split('//')[0]
return protocol + '//' + CANONICAL_DOMAIN + request.path
# Convert an I2P url to an equivalent clearnet one
i2ptoclear = {
'www.i2p2.i2p': 'www.i2p2.de',
#'forum.i2p': 'forum.i2p2.de',
'trac.i2p2.i2p': 'trac.i2p2.de',
2012-12-15 13:01:09 +00:00
'mail.i2p': 'i2pmail.org',
}
def convert_url_to_clearnet(value):
if not value.endswith('.i2p'):
# The url being passed in isn't an I2P url, so just return it
return value
if request.headers.get('X-I2P-Desthash') and not request.headers.get('X-Forwarded-Server'):
# The request is from within I2P, so use I2P url
return value
# The request is either directly from clearnet or through an inproxy
try:
# Return the known clearnet url corresponding to the I2P url
return i2ptoclear[value]
except KeyError:
# The I2P site has no known clearnet address, so use an inproxy
return value + '.to'
# Convert a paginated URL to that of another page
def url_for_other_page(page):
args = request.view_args.copy()
args['page'] = page
return url_for(request.endpoint, **args)
# Change the theme of the current page
def change_theme(theme):
args = request.view_args.copy()
args['theme'] = theme
return url_for(request.endpoint, **args)
return dict(i2pconv=convert_url_to_clearnet,
url_for_other_page=url_for_other_page,
change_theme=change_theme,
site_url=get_site_url,
canonical=get_canonical_link)
################
2012-09-10 11:56:51 +00:00
# Error handlers
2012-06-03 01:06:09 +00:00
@app.errorhandler(404)
def page_not_found(error):
return render_template('global/error_404.html'), 404
2012-09-10 11:28:34 +00:00
2012-09-11 00:17:00 +00:00
@app.errorhandler(500)
def server_error(error):
return render_template('global/error_500.html'), 500
2012-09-10 11:56:51 +00:00
###################
# Download handlers
2012-12-05 01:21:34 +00:00
# Read in mirrors from file
def read_mirrors():
file = open(MIRRORS_FILE, 'r')
dat = file.read()
file.close()
lines=dat.split('\n')
ret={}
for line in lines:
try:
obj=json.loads(line)
except ValueError:
continue
if 'protocol' not in obj:
continue
protocol=obj['protocol']
if protocol not in ret:
ret[protocol]=[]
ret[protocol].append(obj)
return ret
2012-09-10 11:56:51 +00:00
# List of downloads
@app.route('/<lang:lang>/download')
def downloads_list():
2012-06-03 01:06:09 +00:00
# TODO: read mirror list or list of available files
return render_template('downloads/list.html')
2012-09-10 11:56:51 +00:00
# Specific file downloader
@app.route('/<lang:lang>/download/<path:file>')
def downloads_select(file):
if (file == 'debian'):
return render_template('downloads/debian.html')
2012-12-05 01:21:34 +00:00
mirrors=read_mirrors()
data = {
'version': CURRENT_I2P_VERSION,
'file': file,
}
2012-12-05 01:21:34 +00:00
obj=[]
for protocol in mirrors.keys():
a={}
a['name']=protocol
a['mirrors']=mirrors[protocol]
for mirror in a['mirrors']:
mirror['url']=mirror['url'] % data
2012-12-05 01:21:34 +00:00
obj.append(a)
return render_template('downloads/select.html', mirrors=obj, file=file)
@app.route('/download/<string:protocol>/any/<path:file>', defaults={'mirror': None})
2012-12-05 01:21:34 +00:00
@app.route('/download/<string:protocol>/<int:mirror>/<path:file>')
def downloads_redirect(protocol, file, mirror):
2012-12-05 01:21:34 +00:00
mirrors=read_mirrors()
if not protocol in mirrors:
abort(404)
mirrors=mirrors[protocol]
data = {
'version': CURRENT_I2P_VERSION,
'file': file,
}
2012-12-05 01:21:34 +00:00
if mirror:
return redirect(mirrors[mirror]['url'] % data)
return redirect(mirrors[randint(0, len(mirrors) - 1)]['url'] % data)
2012-06-03 01:06:09 +00:00
2012-12-11 08:37:35 +00:00
############
# Root files
@app.route('/hosts.txt')
def hosts():
return send_from_directory(STATIC_DIR, 'hosts.txt', mimetype='text/plain')
@app.route('/robots.txt')
def robots():
return send_from_directory(STATIC_DIR, 'robots.txt', mimetype='text/plain')
@app.route('/favicon.ico')
def favicon():
return send_from_directory(os.path.join(app.root_path, 'static'),
'favicon.ico', mimetype='image/vnd.microsoft.icon')
2012-06-03 01:06:09 +00:00
if __name__ == '__main__':
app.run(debug=True)