- 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> | |||
% if cookies: | |||
<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]: | |||
<% cookie = cookies[key] %>\ | |||
<tr> | |||
@@ -1,4 +1,5 @@ | |||
<%page args="environ, cookies"/>\ | |||
<%namespace module="toolserver.background" import="get_desc_url"/>\ | |||
<%! | |||
from os import path | |||
%>\ | |||
@@ -19,7 +20,7 @@ | |||
<a href="mailto:earwig@toolserver.org">Contact</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: | |||
<a id="bg_image_link" href="">Background</a> • \ | |||
<a href="${get_desc_url() | h}">Background</a> • \ | |||
% endif | |||
<a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0 Strict</a> | |||
</p> | |||
@@ -1,4 +1,5 @@ | |||
<%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"/>\ | |||
<%! | |||
from os import path | |||
@@ -31,10 +32,9 @@ | |||
<% selected = cookies["EarwigBackground"].value if "EarwigBackground" in cookies else "list" %>\ | |||
% if selected in ["plain-brown", "plain-blue"]: | |||
<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: | |||
<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 | |||
<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> | |||
@@ -10,6 +10,21 @@ CREATE DATABASE `u_earwig_toolserver` | |||
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` | |||
-- | |||
@@ -5,7 +5,7 @@ function copyvio_toggle_details(details) { | |||
if (link.innerHTML == "Show details:") { | |||
details.style.display = "block"; | |||
link.innerHTML = "Hide details:"; | |||
set_cookie("EarwigCVShowDetails", "True", 365); | |||
set_cookie("EarwigCVShowDetails", "True", 1095); | |||
} else { | |||
details.style.display = "none"; | |||
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() { | |||
var cookie = get_cookie("EarwigBackgroundCache"); | |||
var cookie = get_cookie("EarwigScreenCache"); | |||
if (cookie) { | |||
try { | |||
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) {} | |||
} | |||
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 | |||
import oursql | |||
_bot = None | |||
_connections = {} | |||
class Query(object): | |||
def __init__(self, environ, method="GET"): | |||
self.query = {} | |||
@@ -35,7 +38,15 @@ class Query(object): | |||
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): | |||
if dbname in _connections: | |||
return _connections[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: | |||
conn_args["read_default_file"] = expanduser("~/.my.cnf") | |||
@@ -43,7 +54,9 @@ def open_sql_connection(bot, dbname): | |||
conn_args["autoping"] = True | |||
if "autoreconnect" not in conn_args: | |||
conn_args["autoreconnect"] = True | |||
return oursql.connect(**conn_args) | |||
conn = oursql.connect(**conn_args) | |||
_connections[dbname] = conn | |||
return conn | |||
def urlstrip(context, url): | |||
if url.startswith("http://"): | |||
@@ -55,6 +68,3 @@ def urlstrip(context, url): | |||
if url.endswith("/"): | |||
url = url[:-1] | |||
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: | |||
key = "EarwigDefaultLang" | |||
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") | |||
if query.project: | |||
key = "EarwigDefaultProject" | |||
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") | |||
if query.background: | |||
key = "EarwigBackground" | |||
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") | |||
changes.add("background") | |||
if changes: | |||