- All POTD logic is now in Python instead of JS. - Removed EarwigBackgroundCache cookie (now stored in a new `background` table in the database). - Added EarwigScreenCache cookie - Increase cookie life from a year to three.pull/24/head
@@ -69,7 +69,7 @@ | |||||
<h2>Cookies</h2> | <h2>Cookies</h2> | ||||
% if cookies: | % if cookies: | ||||
<table> | <table> | ||||
<% cookie_order = ["EarwigDefaultProject", "EarwigDefaultLang", "EarwigBackground", "EarwigCVShowDetails", "EarwigBackgroundCache"] %>\ | |||||
<% cookie_order = ["EarwigDefaultProject", "EarwigDefaultLang", "EarwigBackground", "EarwigScreenCache", "EarwigCVShowDetails"] %>\ | |||||
% for key in [key for key in cookie_order if key in cookies]: | % for key in [key for key in cookie_order if key in cookies]: | ||||
<% cookie = cookies[key] %>\ | <% cookie = cookies[key] %>\ | ||||
<tr> | <tr> | ||||
@@ -1,4 +1,5 @@ | |||||
<%page args="environ, cookies"/>\ | <%page args="environ, cookies"/>\ | ||||
<%namespace module="toolserver.background" import="get_desc_url"/>\ | |||||
<%! | <%! | ||||
from os import path | from os import path | ||||
%>\ | %>\ | ||||
@@ -19,7 +20,7 @@ | |||||
<a href="mailto:earwig@toolserver.org">Contact</a> • \ | <a href="mailto:earwig@toolserver.org">Contact</a> • \ | ||||
<a href="https://github.com/earwig/toolserver">View Source</a> • \ | <a href="https://github.com/earwig/toolserver">View Source</a> • \ | ||||
% if ("EarwigBackground" in cookies and cookies["EarwigBackground"].value in ["potd", "list"]) or "EarwigBackground" not in cookies: | % if ("EarwigBackground" in cookies and cookies["EarwigBackground"].value in ["potd", "list"]) or "EarwigBackground" not in cookies: | ||||
<a id="bg_image_link" href="">Background</a> • \ | |||||
<a href="${get_desc_url() | h}">Background</a> • \ | |||||
% endif | % endif | ||||
<a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0 Strict</a> | <a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0 Strict</a> | ||||
</p> | </p> | ||||
@@ -1,4 +1,5 @@ | |||||
<%page args="environ, cookies, title, slug=None, add_css=(), add_js=()"/>\ | <%page args="environ, cookies, title, slug=None, add_css=(), add_js=()"/>\ | ||||
<%namespace module="toolserver.background" import="set_background"/>\ | |||||
<%namespace name="index" file="/index.mako" import="get_tools"/>\ | <%namespace name="index" file="/index.mako" import="get_tools"/>\ | ||||
<%! | <%! | ||||
from os import path | from os import path | ||||
@@ -31,10 +32,9 @@ | |||||
<% selected = cookies["EarwigBackground"].value if "EarwigBackground" in cookies else "list" %>\ | <% selected = cookies["EarwigBackground"].value if "EarwigBackground" in cookies else "list" %>\ | ||||
% if selected in ["plain-brown", "plain-blue"]: | % if selected in ["plain-brown", "plain-blue"]: | ||||
<body style="background-image: url('${root}/static/images/background-${selected[6:]}.png');"> | <body style="background-image: url('${root}/static/images/background-${selected[6:]}.png');"> | ||||
% elif selected == "potd": | |||||
<body onload="set_background_potd()" style="background-repeat: no-repeat;"> | |||||
% else: | % else: | ||||
<body onload="set_background_list()" style="background-repeat: no-repeat;"> | |||||
<% bg_url = set_background(cookies, selected) %>\ | |||||
<body onload="update_screen_size()" style="background-image: url('${bg_url | h}'); background-size: cover;"> | |||||
% endif | % endif | ||||
<div id="header"> | <div id="header"> | ||||
<p id="heading"><a class="dark" href="${pretty}">earwig</a><span class="light">@</span><a class="mid" href="https://wiki.toolserver.org/">toolserver</a><span class="light">:</span><a class="dark" href="${this}">${slug}</a></p> | <p id="heading"><a class="dark" href="${pretty}">earwig</a><span class="light">@</span><a class="mid" href="https://wiki.toolserver.org/">toolserver</a><span class="light">:</span><a class="dark" href="${this}">${slug}</a></p> | ||||
@@ -10,6 +10,21 @@ CREATE DATABASE `u_earwig_toolserver` | |||||
DEFAULT COLLATE utf8_unicode_ci; | DEFAULT COLLATE utf8_unicode_ci; | ||||
-- | -- | ||||
-- Table structure for table `background` | |||||
-- | |||||
DROP TABLE IF EXISTS `background`; | |||||
CREATE TABLE `background` ( | |||||
`background_id` int(9) unsigned NOT NULL, | |||||
`background_filename` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL, | |||||
`background_url` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL, | |||||
`background_descurl` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL, | |||||
`background_width` int(9) unsigned DEFAULT NULL, | |||||
`background_height` int(9) unsigned DEFAULT NULL, | |||||
PRIMARY KEY (`background_id`) | |||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; | |||||
-- | |||||
-- Table structure for table `language` | -- Table structure for table `language` | ||||
-- | -- | ||||
@@ -5,7 +5,7 @@ function copyvio_toggle_details(details) { | |||||
if (link.innerHTML == "Show details:") { | if (link.innerHTML == "Show details:") { | ||||
details.style.display = "block"; | details.style.display = "block"; | ||||
link.innerHTML = "Hide details:"; | link.innerHTML = "Hide details:"; | ||||
set_cookie("EarwigCVShowDetails", "True", 365); | |||||
set_cookie("EarwigCVShowDetails", "True", 1095); | |||||
} else { | } else { | ||||
details.style.display = "none"; | details.style.display = "none"; | ||||
link.innerHTML = "Show details:"; | link.innerHTML = "Show details:"; | ||||
@@ -1,181 +1,26 @@ | |||||
function set_background_potd() { | |||||
if (cache_cookie()) return; | |||||
var d = new Date(); | |||||
var callback = "earwigpotd1"; | |||||
var date = (d.getUTCFullYear()) + "-" + zero_pad(d.getUTCMonth() + 1, 2) + "-" + zero_pad(d.getUTCDate(), 2); | |||||
var base = "//commons.wikimedia.org/w/api.php?action=query&prop=revisions&rvprop=content&format=json&titles=Template:Potd/"; | |||||
var url = base + date + "&callback=" + callback; | |||||
var script = document.createElement("script"); | |||||
var head = document.getElementsByTagName("head")[0]; | |||||
window[callback] = function(data) { | |||||
head.removeChild(script); | |||||
parse_potd_file_name(data); | |||||
}; | |||||
script.src = url; | |||||
head.appendChild(script); | |||||
} | |||||
function set_background_list() { | |||||
if (cache_cookie()) return; | |||||
var callback = "earwigpotd1"; | |||||
var base = "//commons.wikimedia.org/w/api.php?action=query&prop=revisions&rvprop=content&format=json&titles=User:The+Earwig/POTD"; | |||||
var url = base + "&callback=" + callback; | |||||
var script = document.createElement("script"); | |||||
var head = document.getElementsByTagName("head")[0]; | |||||
window[callback] = function(data) { | |||||
head.removeChild(script); | |||||
parse_list_file_name(data); | |||||
}; | |||||
script.src = url; | |||||
head.appendChild(script); | |||||
function update_screen_size() { | |||||
var cache = cache_cookie(); | |||||
var data = { | |||||
"width": window.screen.availWidth, | |||||
"height": window.screen.availHeight | |||||
} | |||||
if (!cookie || cookie["width"] != data["width"] || cookie["height"] != data["height"]) { | |||||
set_cookie("EarwigScreenCache", JSON.stringify(data), 1095); | |||||
} | |||||
} | } | ||||
function cache_cookie() { | function cache_cookie() { | ||||
var cookie = get_cookie("EarwigBackgroundCache"); | |||||
var cookie = get_cookie("EarwigScreenCache"); | |||||
if (cookie) { | if (cookie) { | ||||
try { | try { | ||||
data = JSON.parse(cookie); | data = JSON.parse(cookie); | ||||
var filename = data.filename; | |||||
var url = data.url; | |||||
var descurl = data.descurl; | |||||
var imgwidth = data.imgwidth; | |||||
var imgheight = data.imgheight; | |||||
if (filename && url && descurl && imgwidth && imgheight) { | |||||
set_background(filename, url, descurl, imgwidth, imgheight); | |||||
return true; | |||||
var width = data.width; | |||||
var height = data.height; | |||||
if (width && height) { | |||||
return {"width": width, "height": height}; | |||||
} | } | ||||
} | } | ||||
catch (SyntaxError) {} | catch (SyntaxError) {} | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
function parse_potd_file_name(data) { | |||||
var content = ""; | |||||
var res = data["query"]["pages"]; | |||||
for (pageid in res) { | |||||
content = res[pageid]["revisions"][0]["*"]; | |||||
} | |||||
var filename = /\{\{Potd filename\|(1=)?(.*?)\|.*?\}\}/.exec(content)[2]; | |||||
var callback = "earwigpotd2"; | |||||
var base = "//commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=url|size&format=json&titles=File:"; | |||||
var url = base + escape(filename) + "&callback=" + callback; | |||||
var script = document.createElement("script"); | |||||
var head = document.getElementsByTagName("head")[0]; | |||||
window[callback] = function(data) { | |||||
head.removeChild(script); | |||||
parse_file_url(data, escape(filename.replace(/ /g, "_"))); | |||||
}; | |||||
script.src = url; | |||||
head.appendChild(script); | |||||
} | |||||
function parse_list_file_name(data) { | |||||
var content = ""; | |||||
var res = data["query"]["pages"]; | |||||
for (pageid in res) { | |||||
content = res[pageid]["revisions"][0]["*"]; | |||||
} | |||||
var filenames = []; | |||||
var regexp = /\*\*?\s*\[\[:File:(.*?)\]\]/g; | |||||
while ((match = regexp.exec(content)) !== null) { | |||||
filenames.push(match[1]); | |||||
} | |||||
var filename = filenames[Math.floor(Math.random() * filenames.length)]; | |||||
var callback = "earwigpotd2"; | |||||
var base = "//commons.wikimedia.org/w/api.php?action=query&prop=imageinfo&iiprop=url|size&format=json&titles=File:"; | |||||
var url = base + escape(filename) + "&callback=" + callback; | |||||
var script = document.createElement("script"); | |||||
var head = document.getElementsByTagName("head")[0]; | |||||
window[callback] = function(data) { | |||||
head.removeChild(script); | |||||
parse_file_url(data, escape(filename.replace(/ /g, "_"))); | |||||
}; | |||||
script.src = url; | |||||
head.appendChild(script); | |||||
} | |||||
function parse_file_url(data, filename) { | |||||
var url = ""; | |||||
var descurl = ""; | |||||
var imgwidth = 1024; | |||||
var imgheight = 768; | |||||
var res = data["query"]["pages"]; | |||||
for (pageid in res) { | |||||
r = res[pageid]["imageinfo"][0]; | |||||
url = r["url"]; | |||||
descurl = r["descriptionurl"]; | |||||
imgwidth = r["width"]; | |||||
imgheight = r["height"]; | |||||
} | |||||
set_background(filename, url, descurl, imgwidth, imgheight); | |||||
var data = {"filename": filename, "url": url, "descurl": descurl, "imgwidth": imgwidth, "imgheight": imgheight}; | |||||
var expires = new Date(); | |||||
expires.setUTCMilliseconds(0); | |||||
expires.setUTCSeconds(0); | |||||
expires.setUTCMinutes(0); | |||||
expires.setUTCHours(0); | |||||
expires.setUTCDate(expires.getUTCDate() + 1); | |||||
set_cookie_with_date("EarwigBackgroundCache", JSON.stringify(data), expires); | |||||
} | |||||
function set_background(filename, url, descurl, imgwidth, imgheight) { | |||||
var s = get_window_size(); | |||||
var winwidth = s[0]; | |||||
var winheight = s[1]; | |||||
var width = winwidth; | |||||
if (imgwidth/imgheight > winwidth/winheight) { | |||||
width = Math.round((imgwidth/imgheight) * winheight); | |||||
} | |||||
if (width >= imgwidth) { | |||||
document.body.style.backgroundImage = "url('" + url + "')"; | |||||
if (width > imgwidth) { | |||||
document.body.style.setProperty("background-size", "cover"); | |||||
} | |||||
} else { | |||||
url = url.replace(/\/commons\//, "/commons/thumb/") + "/" + width + "px-" + filename; | |||||
document.body.style.backgroundImage = "url('" + url + "')"; | |||||
} | |||||
document.getElementById("bg_image_link").href = descurl; | |||||
} | |||||
function zero_pad(value, length) { | |||||
value = String(value); | |||||
length = length || 2; | |||||
while (value.length < length) { | |||||
value = "0" + value; | |||||
} | |||||
return value; | |||||
} | |||||
function get_window_size() { | |||||
// See http://www.javascripter.net/faq/browserw.htm | |||||
if (document.body && document.body.offsetWidth && document.body.offsetHeight) { | |||||
return [document.body.offsetWidth, document.body.offsetHeight]; | |||||
} | |||||
if (document.compatMode=="CSS1Compat" && document.documentElement && document.documentElement.offsetWidth && document.documentElement.offsetHeight) { | |||||
return [document.documentElement.offsetWidth, document.documentElement.offsetHeight]; | |||||
} | |||||
if (window.innerWidth && window.innerHeight) { | |||||
return [window.innerWidth, window.innerHeight]; | |||||
} | |||||
return [1024, 768]; | |||||
} |
@@ -0,0 +1,115 @@ | |||||
# -*- coding: utf-8 -*- | |||||
from datetime import datetime, timedelta | |||||
from json import loads | |||||
import random | |||||
import re | |||||
from time import time | |||||
from earwigbot import exceptions | |||||
from .misc import get_bot, open_sql_connection | |||||
_descurl = None | |||||
def set_background(cookies, selected): | |||||
global _descurl | |||||
conn = open_sql_connection(get_bot(), "globals") | |||||
if "EarwigScreenCache" in cookies: | |||||
cache = cookies["EarwigScreenCache"].value | |||||
try: | |||||
screen = loads(cache) | |||||
int(screen["width"]) | |||||
int(screen["height"]) | |||||
except (ValueError, KeyError): | |||||
screen = {"width": 1024, "height": 768} | |||||
else: | |||||
screen = {"width": 1024, "height": 768} | |||||
if selected == "potd": | |||||
info = _update_url(conn, "background_potd", 1, _get_fresh_potd) | |||||
else: | |||||
info = _update_url(conn, "background_list", 2, _get_fresh_list) | |||||
filename, url, descurl, width, height = info | |||||
bg_url = _build_url(screen, filename, url, width, height) | |||||
_descurl = descurl | |||||
return bg_url | |||||
def get_desc_url(): | |||||
return _descurl | |||||
def _update_url(conn, service, bg_id, callback): | |||||
query1 = "SELECT update_time FROM updates WHERE update_service = ?" | |||||
query2 = "SELECT 1 FROM background WHERE background_id = ?" | |||||
query3 = "DELETE FROM background WHERE background_id = ?" | |||||
query4 = "INSERT INTO background VALUES (?, ?, ?, ?, ?, ?)" | |||||
query5 = "SELECT 1 FROM updates WHERE update_service = ?" | |||||
query6 = "UPDATE updates SET update_time = ? WHERE update_service = ?" | |||||
query7 = "INSERT INTO updates VALUES (?, ?)" | |||||
query8 = "SELECT * FROM background WHERE background_id = ?" | |||||
with conn.cursor() as cursor: | |||||
cursor.execute(query1, (service,)) | |||||
try: | |||||
update_time = datetime.utcfromtimestamp(cursor.fetchall()[0][0]) | |||||
except IndexError: | |||||
update_time = datetime.min | |||||
plus_one = update_time + timedelta(days=1) | |||||
max_age = datetime(plus_one.year, plus_one.month, plus_one.day) | |||||
if datetime.utcnow() > max_age: | |||||
filename, url, descurl, width, height = callback() | |||||
cursor.execute(query2, (bg_id,)) | |||||
if cursor.fetchall(): | |||||
cursor.execute(query3, (bg_id,)) | |||||
cursor.execute(query4, (bg_id, filename, url, descurl, width, | |||||
height)) | |||||
cursor.execute(query5, (service,)) | |||||
if cursor.fetchall(): | |||||
cursor.execute(query6, (time(), service)) | |||||
else: | |||||
cursor.execute(query7, (service, time())) | |||||
else: | |||||
cursor.execute(query8, (bg_id,)) | |||||
filename, url, descurl, width, height = cursor.fetchone()[1:] | |||||
return filename, url, descurl, width, height | |||||
def _get_fresh_potd(): | |||||
site = _get_site() | |||||
date = datetime.utcnow().strftime("%Y-%m-%d") | |||||
page = site.get_page("Template:Potd/" + date) | |||||
regex = ur"\{\{Potd filename\|(?:1=)?(.*?)\|.*?\}\}" | |||||
filename = re.search(regex, page.get()).group(1) | |||||
return _load_file(site, filename) | |||||
def _get_fresh_list(): | |||||
site = _get_site() | |||||
page = site.get_page("User:The Earwig/POTD") | |||||
regex = ur"\*\*?\s*\[\[:File:(.*?)\]\]" | |||||
filenames = re.findall(regex, page.get()) | |||||
filename = random.choice(filenames) | |||||
return _load_file(site, filename) | |||||
def _load_file(site, filename): | |||||
res = site.api_query(action="query", prop="imageinfo", iiprop="url|size", | |||||
titles="File:" + filename) | |||||
data = res["query"]["pages"].values()[0]["imageinfo"][0] | |||||
url = data["url"] | |||||
descurl = data["descriptionurl"] | |||||
width = data["width"] | |||||
height = data["height"] | |||||
return filename.replace(" ", "_"), url, descurl, width, height | |||||
def _get_site(): | |||||
bot = get_bot() | |||||
try: | |||||
return bot.wiki.get_site("commonswiki") | |||||
except exceptions.SiteNotFoundError: | |||||
return bot.wiki.add_site(project="wikimedia", lang="commons") | |||||
def _build_url(screen, filename, url, imgwidth, imgheight): | |||||
width = screen["width"] | |||||
if float(imgwidth) / imgheight > float(screen["width"]) / screen["height"]: | |||||
width = int(float(imgwidth) / imgheight * screen["width"]) | |||||
if width >= imgwidth: | |||||
return url | |||||
url = url.replace("/commons/", "/commons/thumb/") | |||||
return url + "/" + width + "px-" + filename |
@@ -6,6 +6,9 @@ from urlparse import parse_qs | |||||
from earwigbot.bot import Bot | from earwigbot.bot import Bot | ||||
import oursql | import oursql | ||||
_bot = None | |||||
_connections = {} | |||||
class Query(object): | class Query(object): | ||||
def __init__(self, environ, method="GET"): | def __init__(self, environ, method="GET"): | ||||
self.query = {} | self.query = {} | ||||
@@ -35,7 +38,15 @@ class Query(object): | |||||
self.query[key] = value | self.query[key] = value | ||||
def get_bot(): | |||||
global _bot | |||||
if not _bot: | |||||
_bot = Bot(".earwigbot", 100) # Don't print any logs to the console | |||||
return _bot | |||||
def open_sql_connection(bot, dbname): | def open_sql_connection(bot, dbname): | ||||
if dbname in _connections: | |||||
return _connections[dbname] | |||||
conn_args = bot.config.wiki["_toolserverSQL"][dbname] | conn_args = bot.config.wiki["_toolserverSQL"][dbname] | ||||
if "read_default_file" not in conn_args and "user" not in conn_args and "passwd" not in conn_args: | if "read_default_file" not in conn_args and "user" not in conn_args and "passwd" not in conn_args: | ||||
conn_args["read_default_file"] = expanduser("~/.my.cnf") | conn_args["read_default_file"] = expanduser("~/.my.cnf") | ||||
@@ -43,7 +54,9 @@ def open_sql_connection(bot, dbname): | |||||
conn_args["autoping"] = True | conn_args["autoping"] = True | ||||
if "autoreconnect" not in conn_args: | if "autoreconnect" not in conn_args: | ||||
conn_args["autoreconnect"] = True | conn_args["autoreconnect"] = True | ||||
return oursql.connect(**conn_args) | |||||
conn = oursql.connect(**conn_args) | |||||
_connections[dbname] = conn | |||||
return conn | |||||
def urlstrip(context, url): | def urlstrip(context, url): | ||||
if url.startswith("http://"): | if url.startswith("http://"): | ||||
@@ -55,6 +68,3 @@ def urlstrip(context, url): | |||||
if url.endswith("/"): | if url.endswith("/"): | ||||
url = url[:-1] | url = url[:-1] | ||||
return url | return url | ||||
def get_bot(): | |||||
return Bot(".earwigbot", 100) # Don't print any logs to the console |
@@ -25,17 +25,17 @@ def _do_set(query, headers, cookies): | |||||
if query.lang: | if query.lang: | ||||
key = "EarwigDefaultLang" | key = "EarwigDefaultLang" | ||||
if key not in cookies or cookies[key].value != query.lang: | if key not in cookies or cookies[key].value != query.lang: | ||||
set_cookie(headers, cookies, key, query.lang, 365) | |||||
set_cookie(headers, cookies, key, query.lang, 1095) | |||||
changes.add("site") | changes.add("site") | ||||
if query.project: | if query.project: | ||||
key = "EarwigDefaultProject" | key = "EarwigDefaultProject" | ||||
if key not in cookies or cookies[key].value != query.project: | if key not in cookies or cookies[key].value != query.project: | ||||
set_cookie(headers, cookies, key, query.project, 365) | |||||
set_cookie(headers, cookies, key, query.project, 1095) | |||||
changes.add("site") | changes.add("site") | ||||
if query.background: | if query.background: | ||||
key = "EarwigBackground" | key = "EarwigBackground" | ||||
if key not in cookies or cookies[key].value != query.background: | if key not in cookies or cookies[key].value != query.background: | ||||
set_cookie(headers, cookies, key, query.background, 365) | |||||
set_cookie(headers, cookies, key, query.background, 1095) | |||||
delete_cookie(headers, cookies, "EarwigBackgroundCache") | delete_cookie(headers, cookies, "EarwigBackgroundCache") | ||||
changes.add("background") | changes.add("background") | ||||
if changes: | if changes: | ||||