get rid to spam checking and captcha since its just annoying
This commit is contained in:
parent
b03af04f78
commit
6cba016f0c
@ -11,14 +11,13 @@
|
|||||||
from werkzeug import redirect, Response
|
from werkzeug import redirect, Response
|
||||||
from werkzeug.exceptions import NotFound
|
from werkzeug.exceptions import NotFound
|
||||||
from lodgeit import local
|
from lodgeit import local
|
||||||
from lodgeit.lib import antispam
|
|
||||||
from lodgeit.i18n import list_languages as i18n_list_languages, _
|
from lodgeit.i18n import list_languages as i18n_list_languages, _
|
||||||
from lodgeit.utils import render_to_response, url_for
|
from lodgeit.utils import render_to_response, url_for
|
||||||
from lodgeit.models import Paste
|
from lodgeit.models import Paste
|
||||||
from lodgeit.database import session
|
from lodgeit.database import session
|
||||||
from lodgeit.lib.highlighting import list_languages, STYLES, get_style
|
from lodgeit.lib.highlighting import list_languages, STYLES, get_style
|
||||||
from lodgeit.lib.pagination import generate_pagination
|
from lodgeit.lib.pagination import generate_pagination
|
||||||
from lodgeit.lib.captcha import check_hashed_solution, Captcha
|
|
||||||
|
|
||||||
|
|
||||||
class PasteController(object):
|
class PasteController(object):
|
||||||
@ -32,7 +31,7 @@ class PasteController(object):
|
|||||||
language = local.request.session.get('language', 'text')
|
language = local.request.session.get('language', 'text')
|
||||||
|
|
||||||
code = error = ''
|
code = error = ''
|
||||||
show_captcha = private = False
|
private = False
|
||||||
parent_id = None
|
parent_id = None
|
||||||
req = local.request
|
req = local.request
|
||||||
getform = req.form.get
|
getform = req.form.get
|
||||||
@ -41,18 +40,6 @@ class PasteController(object):
|
|||||||
code = getform('code', u'')
|
code = getform('code', u'')
|
||||||
language = getform('language')
|
language = getform('language')
|
||||||
parent_id = getform('parent')
|
parent_id = getform('parent')
|
||||||
spam = getform('webpage') or antispam.is_spam(code)
|
|
||||||
|
|
||||||
if spam:
|
|
||||||
error = _('your paste contains spam')
|
|
||||||
captcha = getform('captcha')
|
|
||||||
if captcha:
|
|
||||||
if check_hashed_solution(captcha):
|
|
||||||
error = None
|
|
||||||
else:
|
|
||||||
error = _('your paste contains spam and the '
|
|
||||||
'CAPTCHA solution was incorrect')
|
|
||||||
show_captcha = True
|
|
||||||
|
|
||||||
if code and language and not error:
|
if code and language and not error:
|
||||||
paste = Paste(code, language, parent_id, req.user_hash,
|
paste = Paste(code, language, parent_id, req.user_hash,
|
||||||
@ -81,7 +68,6 @@ class PasteController(object):
|
|||||||
code=code,
|
code=code,
|
||||||
language=language,
|
language=language,
|
||||||
error=error,
|
error=error,
|
||||||
show_captcha=show_captcha,
|
|
||||||
private=private
|
private=private
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -191,9 +177,5 @@ class PasteController(object):
|
|||||||
return redirect(local.request.headers.get('referer') or
|
return redirect(local.request.headers.get('referer') or
|
||||||
url_for('pastes/new_paste'))
|
url_for('pastes/new_paste'))
|
||||||
|
|
||||||
def show_captcha(self):
|
|
||||||
"""Show a captcha."""
|
|
||||||
return Captcha().get_response(set_cookie=True)
|
|
||||||
|
|
||||||
|
|
||||||
controller = PasteController
|
controller = PasteController
|
||||||
|
@ -21,10 +21,6 @@ msgstr ""
|
|||||||
msgid "your paste contains spam"
|
msgid "your paste contains spam"
|
||||||
msgstr "Dein Paste beinhaltet Spam"
|
msgstr "Dein Paste beinhaltet Spam"
|
||||||
|
|
||||||
#: lodgeit/controllers/pastes.py:51
|
|
||||||
msgid "your paste contains spam and the CAPTCHA solution was incorrect"
|
|
||||||
msgstr "Dein Paste beinhaltet Spam und die CAPTCHA-Eingabe war falsch"
|
|
||||||
|
|
||||||
#: lodgeit/controllers/static.py:20
|
#: lodgeit/controllers/static.py:20
|
||||||
msgid "Pasting"
|
msgid "Pasting"
|
||||||
msgstr "Pasting"
|
msgstr "Pasting"
|
||||||
@ -556,14 +552,6 @@ msgstr "Ein Fehler ist aufgetreten"
|
|||||||
msgid "Could not submit your paste because %(error)s."
|
msgid "Could not submit your paste because %(error)s."
|
||||||
msgstr "Konnte den Paste nicht senden, da Fehler aufgetreten sind: %(error)s"
|
msgstr "Konnte den Paste nicht senden, da Fehler aufgetreten sind: %(error)s"
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:12
|
|
||||||
msgid "Please fill out the CAPTCHA to proceed:"
|
|
||||||
msgstr "Bitte fülle das CAPTCHA Feld aus um fortzufahren"
|
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:13
|
|
||||||
msgid "a captcha you can't see. Sorry :("
|
|
||||||
msgstr "ein Captcha das du nicht sehen kannst. Tut mir leid :("
|
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:17
|
#: lodgeit/views/new_paste.html:17
|
||||||
msgid "hide this message"
|
msgid "hide this message"
|
||||||
msgstr "verberge diese Nachricht"
|
msgstr "verberge diese Nachricht"
|
||||||
|
@ -21,9 +21,6 @@ msgstr ""
|
|||||||
msgid "your paste contains spam"
|
msgid "your paste contains spam"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lodgeit/controllers/pastes.py:51
|
|
||||||
msgid "your paste contains spam and the CAPTCHA solution was incorrect"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lodgeit/controllers/static.py:20
|
#: lodgeit/controllers/static.py:20
|
||||||
msgid "Pasting"
|
msgid "Pasting"
|
||||||
@ -499,14 +496,6 @@ msgstr ""
|
|||||||
msgid "Could not submit your paste because %(error)s."
|
msgid "Could not submit your paste because %(error)s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:12
|
|
||||||
msgid "Please fill out the CAPTCHA to proceed:"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:13
|
|
||||||
msgid "a captcha you can't see. Sorry :("
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lodgeit/views/new_paste.html:17
|
#: lodgeit/views/new_paste.html:17
|
||||||
msgid "hide this message"
|
msgid "hide this message"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
lodgeit.lib.antispam
|
|
||||||
~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Fight stupid spammers.
|
|
||||||
|
|
||||||
:copyright: 2007-2008 by Armin Ronacher, Christopher Grebs.
|
|
||||||
:license: BSD
|
|
||||||
"""
|
|
||||||
from __future__ import division
|
|
||||||
import re
|
|
||||||
from operator import sub
|
|
||||||
from itertools import starmap
|
|
||||||
|
|
||||||
|
|
||||||
_url_pattern = (
|
|
||||||
r'(?:(?:https?|ftps?|file|ssh|mms|irc|rsync|smb)://|'
|
|
||||||
r'(?:mailto|telnet|s?news|sips?|skype):)'
|
|
||||||
)
|
|
||||||
|
|
||||||
_link_re = re.compile(r'''(?xi)
|
|
||||||
(%(url)s[^\s\'"]+)|
|
|
||||||
(<a(?:\s+\w+\s*=\s*
|
|
||||||
(?:"\s*%(url)s.*?"|'\s*%(url)s.*?'|%(url)s.*?)
|
|
||||||
)+\s*>.*?</a>)
|
|
||||||
''' % dict(url=_url_pattern))
|
|
||||||
|
|
||||||
|
|
||||||
# maximum number of links in percent
|
|
||||||
MAX_LINK_PERCENTAGE = 30
|
|
||||||
|
|
||||||
# maximum number of links in the text (hard limit)
|
|
||||||
MAX_LINK_NUMBER = 15
|
|
||||||
|
|
||||||
|
|
||||||
def check_for_link_spam(code):
|
|
||||||
"""It's spam if more than 30% of the text are links."""
|
|
||||||
spans = [x.span() for x in _link_re.finditer(code)]
|
|
||||||
if len(spans) > MAX_LINK_PERCENTAGE:
|
|
||||||
return True
|
|
||||||
return (sum(starmap(sub, spans)) * -100) / (len(code) or 1) \
|
|
||||||
> MAX_LINK_PERCENTAGE
|
|
||||||
|
|
||||||
|
|
||||||
def is_spam(code):
|
|
||||||
"""Check if the code provided contains spam."""
|
|
||||||
return check_for_link_spam(code)
|
|
@ -1,412 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
lodgeit.captcha
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
A module that produces image and audio captchas. Uses some code of
|
|
||||||
PyCAPTCHA by Micah Dowty and was originally used in inyoka.
|
|
||||||
|
|
||||||
|
|
||||||
:copyright: Copyright 2007-2008 by Armin Ronacher, Micah Dowty.
|
|
||||||
:license: BSD
|
|
||||||
"""
|
|
||||||
import random
|
|
||||||
import colorsys
|
|
||||||
import math
|
|
||||||
from os import listdir
|
|
||||||
from os.path import abspath, join, dirname, pardir
|
|
||||||
from PIL import ImageFont, ImageDraw, Image, ImageChops, ImageColor
|
|
||||||
from werkzeug import Response
|
|
||||||
from lodgeit import local
|
|
||||||
|
|
||||||
try:
|
|
||||||
from hashlib import sha1
|
|
||||||
except ImportError:
|
|
||||||
from sha import new as sha1
|
|
||||||
|
|
||||||
resource_path = abspath(join(dirname(__file__), pardir, 'res'))
|
|
||||||
|
|
||||||
|
|
||||||
def check_hashed_solution(solution, hashed_solution=None, secret_key=None):
|
|
||||||
"""Check a solution against the hashed solution from the first
|
|
||||||
request by using the secret key.
|
|
||||||
"""
|
|
||||||
if hashed_solution is None:
|
|
||||||
hashed_solution = local.request.session.get('captcha_id')
|
|
||||||
if hashed_solution is None:
|
|
||||||
return False
|
|
||||||
return hashed_solution == calculate_hash(solution, secret_key)
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_hash(solution, secret_key=None):
|
|
||||||
"""Calculate the hash."""
|
|
||||||
if secret_key is None:
|
|
||||||
secret_key = local.application.secret_key
|
|
||||||
return sha1('%s|%s' % (
|
|
||||||
secret_key,
|
|
||||||
solution.encode('utf-8')
|
|
||||||
)).hexdigest()
|
|
||||||
|
|
||||||
|
|
||||||
def generate_word():
|
|
||||||
"""This function returns a pronounceable word."""
|
|
||||||
consonants = 'bcdfghjklmnprstvwz'
|
|
||||||
vowels = 'aeiou'
|
|
||||||
both = consonants + vowels
|
|
||||||
length = random.randrange(8, 12)
|
|
||||||
return ''.join(
|
|
||||||
random.choice(consonants) +
|
|
||||||
random.choice(vowels) +
|
|
||||||
random.choice(both) for x in xrange(length // 3)
|
|
||||||
)[:length]
|
|
||||||
|
|
||||||
|
|
||||||
def get_random_resource(type, prefix=None):
|
|
||||||
"""Return a random resource of a given type."""
|
|
||||||
path = join(resource_path, type)
|
|
||||||
choices = (x for x in listdir(path) if not x.startswith('.'))
|
|
||||||
if prefix is not None:
|
|
||||||
choices = (x for x in choices if x.startswith(prefix))
|
|
||||||
return join(path, random.choice(tuple(choices)))
|
|
||||||
|
|
||||||
|
|
||||||
def random_color(saturation=0.5, lumination=None):
|
|
||||||
"""Return a random number with the given saturation."""
|
|
||||||
hue = random.random()
|
|
||||||
if lumination is None:
|
|
||||||
lumination = random.random()
|
|
||||||
r, g, b = colorsys.hls_to_rgb(hue, lumination, saturation)
|
|
||||||
return '#%02x%02x%02x' % (
|
|
||||||
int(r * 255) & 0xff,
|
|
||||||
int(g * 255) & 0xff,
|
|
||||||
int(b * 255) & 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Captcha(object):
|
|
||||||
"""Represents a captcha."""
|
|
||||||
default_size = (300, 100)
|
|
||||||
|
|
||||||
def __init__(self, solution=None):
|
|
||||||
if solution is None:
|
|
||||||
solution = generate_word()
|
|
||||||
self.solution = solution
|
|
||||||
self.layers = [
|
|
||||||
RandomBackground(),
|
|
||||||
RandomDistortion()
|
|
||||||
]
|
|
||||||
text_layer = TextLayer(self.solution, bg=self.layers[0].bg)
|
|
||||||
self.layers.extend((text_layer, SineWarp()))
|
|
||||||
|
|
||||||
def hash_solution(self, secret_key=None):
|
|
||||||
"""Return the solution as hashed value."""
|
|
||||||
return calculate_hash(self.solution, secret_key)
|
|
||||||
|
|
||||||
def render_image(self, size=None):
|
|
||||||
if size is None:
|
|
||||||
size = self.default_size
|
|
||||||
image = Image.new('RGBA', size)
|
|
||||||
for layer in self.layers:
|
|
||||||
image = layer.render(image)
|
|
||||||
return image
|
|
||||||
|
|
||||||
def get_response(self, size=None, set_cookie=False):
|
|
||||||
response = Response(mimetype='image/png')
|
|
||||||
self.render_image(size=None).save(response.stream, 'PNG')
|
|
||||||
if set_cookie:
|
|
||||||
local.request.session['captcha_id'] = self.hash_solution()
|
|
||||||
return response
|
|
||||||
|
|
||||||
|
|
||||||
class Layer(object):
|
|
||||||
"""Baseclass for a captcha layer."""
|
|
||||||
bg = 'dark'
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class TextLayer(Layer):
|
|
||||||
"""Add text to the captcha."""
|
|
||||||
bg = 'transparent'
|
|
||||||
|
|
||||||
def __init__(self, text, min_size=32, max_size=48, bg='dark'):
|
|
||||||
self.text = text
|
|
||||||
self.alignment = (random.random(), random.random())
|
|
||||||
if bg == 'dark':
|
|
||||||
color = random_color(saturation=0.3, lumination=0.8)
|
|
||||||
else:
|
|
||||||
color = random_color(saturation=0.1, lumination=0.1)
|
|
||||||
self.text_color = color
|
|
||||||
self.transparency = random.randint(20, 60)
|
|
||||||
self.font = ImageFont.truetype(get_random_resource('fonts'),
|
|
||||||
random.randrange(min_size, max_size))
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
text_layer = Image.new('RGB', image.size, (0, 0, 0))
|
|
||||||
alpha = Image.new('L', image.size, 0)
|
|
||||||
|
|
||||||
# draw grayscale image white on black
|
|
||||||
text_image = Image.new('L', image.size, 0)
|
|
||||||
draw = ImageDraw.Draw(text_image)
|
|
||||||
text_size = self.font.getsize(self.text)
|
|
||||||
x = int((image.size[0] - text_size[0]) * self.alignment[0] + 0.5)
|
|
||||||
y = int((image.size[1] - text_size[1]) * self.alignment[1] + 0.5)
|
|
||||||
draw.text((x, y), self.text, font=self.font,
|
|
||||||
fill=255 - self.transparency)
|
|
||||||
|
|
||||||
# colorize the text and calculate the alpha channel
|
|
||||||
alpha = ImageChops.lighter(alpha, text_image)
|
|
||||||
color_layer = Image.new('RGBA', image.size, self.text_color)
|
|
||||||
mask = Image.eval(text_image, lambda x: 255 * (x != 0))
|
|
||||||
text_layer = Image.composite(color_layer, text_layer, mask)
|
|
||||||
|
|
||||||
# paste the text on the image with the correct alphachannel
|
|
||||||
image.paste(text_layer, alpha)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class CombinedLayer(Layer):
|
|
||||||
"""Combines multiple layers."""
|
|
||||||
|
|
||||||
def __init__(self, layers):
|
|
||||||
self.layers = layers
|
|
||||||
if layers:
|
|
||||||
self.bg = layers[0].bg
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
for layer in self.layers:
|
|
||||||
image = layer.render(image)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class RandomBackground(CombinedLayer):
|
|
||||||
"""Selects a random background."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
layers = [random.choice([SolidColor, DarkBackground,
|
|
||||||
LightBackground])()]
|
|
||||||
for x in xrange(random.randrange(1, 4)):
|
|
||||||
layers.append(random.choice([
|
|
||||||
NoiseBackground,
|
|
||||||
GridBackground
|
|
||||||
])())
|
|
||||||
CombinedLayer.__init__(self, layers)
|
|
||||||
self.bg = layers[0].bg
|
|
||||||
|
|
||||||
|
|
||||||
class RandomDistortion(CombinedLayer):
|
|
||||||
"""Selects a random distortion."""
|
|
||||||
background = 'transparent'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
layers = []
|
|
||||||
for x in xrange(random.randrange(1, 3)):
|
|
||||||
layers.append(random.choice((
|
|
||||||
WigglyBlocks,
|
|
||||||
SineWarp
|
|
||||||
))())
|
|
||||||
CombinedLayer.__init__(self, layers)
|
|
||||||
|
|
||||||
|
|
||||||
class Picture(Layer):
|
|
||||||
"""Add a background to the captcha."""
|
|
||||||
|
|
||||||
def __init__(self, picture):
|
|
||||||
self.image = Image.open(picture)
|
|
||||||
self.offset = (random.random(), random.random())
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
tile = self.image
|
|
||||||
for j in xrange(-1, int(image.size[1] / tile.size[1]) + 1):
|
|
||||||
for i in xrange(-1, int(image.size[0] / tile.size[0]) + 1):
|
|
||||||
dest = (int((self.offset[0] + i) * tile.size[0]),
|
|
||||||
int((self.offset[1] + j) * tile.size[1]))
|
|
||||||
image.paste(tile, dest)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class LightBackground(Picture):
|
|
||||||
bg = 'light'
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
Picture.__init__(self, get_random_resource('backgrounds/light'))
|
|
||||||
|
|
||||||
|
|
||||||
class DarkBackground(Picture):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
Picture.__init__(self, get_random_resource('backgrounds/dark'))
|
|
||||||
|
|
||||||
|
|
||||||
class NoiseBackground(Layer):
|
|
||||||
"""Add some noise as background. You can combine this with another
|
|
||||||
background layer.
|
|
||||||
"""
|
|
||||||
bg = 'transparent'
|
|
||||||
|
|
||||||
def __init__(self, saturation=0.1, num_dots=None):
|
|
||||||
self.saturation = saturation
|
|
||||||
self.num_dots = random.randrange(300, 500)
|
|
||||||
self.seed = random.random()
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
r = random.Random(self.seed)
|
|
||||||
for i in xrange(self.num_dots):
|
|
||||||
dot_size = random.randrange(1, 5)
|
|
||||||
bx = int(r.uniform(0, image.size[0] - dot_size))
|
|
||||||
by = int(r.uniform(0, image.size[1] - dot_size))
|
|
||||||
image.paste(random_color(self.saturation, 0.4),
|
|
||||||
(bx, by, bx + dot_size - 1,
|
|
||||||
by + dot_size - 1))
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class GridBackground(Layer):
|
|
||||||
"""Add a grid as background. You can combine this with another
|
|
||||||
background layer.
|
|
||||||
"""
|
|
||||||
bg = 'transparent'
|
|
||||||
|
|
||||||
def __init__(self, size=None, color=None):
|
|
||||||
if size is None:
|
|
||||||
size = random.randrange(10, 50)
|
|
||||||
if color is None:
|
|
||||||
color = random_color(0, 0.4)
|
|
||||||
self.size = size
|
|
||||||
self.color = color
|
|
||||||
self.offset = (random.uniform(0, self.size),
|
|
||||||
random.uniform(0, self.size))
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
draw = ImageDraw.Draw(image)
|
|
||||||
for i in xrange(image.size[0] / self.size + 1):
|
|
||||||
draw.line((i * self.size + self.offset[0], 0,
|
|
||||||
i * self.size + self.offset[0], image.size[1]),
|
|
||||||
fill=self.color)
|
|
||||||
for i in xrange(image.size[0] / self.size + 1):
|
|
||||||
draw.line((0, i * self.size + self.offset[1],
|
|
||||||
image.size[0], i * self.size+self.offset[1]),
|
|
||||||
fill=self.color)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class SolidColor(Layer):
|
|
||||||
"""A solid color background. Very weak on its own, but good
|
|
||||||
to combine with other backgrounds.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, color=None):
|
|
||||||
if color is None:
|
|
||||||
color = random_color(0.2, random.random() > 0.5 and 0.3 or 0.7)
|
|
||||||
self.color = ImageColor.getrgb(color)
|
|
||||||
if colorsys.rgb_to_hls(*[x / 255.0 for x in self.color])[1] > 0.5:
|
|
||||||
self.bg = 'light'
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
image.paste(self.color)
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class WigglyBlocks(Layer):
|
|
||||||
"""Randomly select and shift blocks of the image"""
|
|
||||||
bg = 'transparent'
|
|
||||||
|
|
||||||
def __init__(self, block_size=None, sigma=0.01, iterations=None):
|
|
||||||
if block_size is None:
|
|
||||||
block_size = random.randrange(15, 25)
|
|
||||||
if iterations is None:
|
|
||||||
iterations = random.randrange(250, 350)
|
|
||||||
self.block_size = block_size
|
|
||||||
self.sigma = sigma
|
|
||||||
self.iterations = iterations
|
|
||||||
self.seed = random.random()
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
r = random.Random(self.seed)
|
|
||||||
for i in xrange(self.iterations):
|
|
||||||
# Select a block
|
|
||||||
bx = int(r.uniform(0, image.size[0] - self.block_size))
|
|
||||||
by = int(r.uniform(0, image.size[1] - self.block_size))
|
|
||||||
block = image.crop((bx, by, bx + self.block_size - 1,
|
|
||||||
by + self.block_size - 1))
|
|
||||||
|
|
||||||
# Figure out how much to move it.
|
|
||||||
# The call to floor() is important so we always round toward
|
|
||||||
# 0 rather than to -inf. Just int() would bias the block motion.
|
|
||||||
mx = int(math.floor(r.normalvariate(0, self.sigma)))
|
|
||||||
my = int(math.floor(r.normalvariate(0, self.sigma)))
|
|
||||||
|
|
||||||
# Now actually move the block
|
|
||||||
image.paste(block, (bx+mx, by+my))
|
|
||||||
return image
|
|
||||||
|
|
||||||
|
|
||||||
class WarpBase(Layer):
|
|
||||||
"""Abstract base class for image warping. Subclasses define a function
|
|
||||||
that maps points in the output image to points in the input image. This
|
|
||||||
warping engine runs a grid of points through this transform and uses PIL's
|
|
||||||
mesh transform to warp the image.
|
|
||||||
"""
|
|
||||||
bg = 'transparent'
|
|
||||||
filtering = Image.BILINEAR
|
|
||||||
resolution = 10
|
|
||||||
|
|
||||||
def get_transform(self, image):
|
|
||||||
"""Return a transformation function, subclasses should override this"""
|
|
||||||
return lambda x, y: (x, y)
|
|
||||||
|
|
||||||
def render(self, image):
|
|
||||||
r = self.resolution
|
|
||||||
x_points = image.size[0] / r + 2
|
|
||||||
y_points = image.size[1] / r + 2
|
|
||||||
f = self.get_transform(image)
|
|
||||||
|
|
||||||
# Create a list of arrays with transformed points
|
|
||||||
x_rows = []
|
|
||||||
y_rows = []
|
|
||||||
for j in xrange(y_points):
|
|
||||||
x_row = []
|
|
||||||
y_row = []
|
|
||||||
for i in xrange(x_points):
|
|
||||||
x, y = f(i * r, j * r)
|
|
||||||
|
|
||||||
# Clamp the edges so we don't get black undefined areas
|
|
||||||
x = max(0, min(image.size[0] - 1, x))
|
|
||||||
y = max(0, min(image.size[1] - 1, y))
|
|
||||||
|
|
||||||
x_row.append(x)
|
|
||||||
y_row.append(y)
|
|
||||||
x_rows.append(x_row)
|
|
||||||
y_rows.append(y_row)
|
|
||||||
|
|
||||||
# Create the mesh list, with a transformation for
|
|
||||||
# each square between points on the grid
|
|
||||||
mesh = []
|
|
||||||
for j in xrange(y_points - 1):
|
|
||||||
for i in xrange(x_points-1):
|
|
||||||
mesh.append((
|
|
||||||
# Destination rectangle
|
|
||||||
(i * r, j * r, (i + 1) * r, (j + 1) * r),
|
|
||||||
# Source quadrilateral
|
|
||||||
(x_rows[j][i], y_rows[j][i],
|
|
||||||
x_rows[j + 1][i], y_rows[j+1][i],
|
|
||||||
x_rows[j + 1][i + 1], y_rows[j + 1][i + 1],
|
|
||||||
x_rows[j][i+1], y_rows[j][i + 1]),
|
|
||||||
))
|
|
||||||
return image.transform(image.size, Image.MESH, mesh, self.filtering)
|
|
||||||
|
|
||||||
|
|
||||||
class SineWarp(WarpBase):
|
|
||||||
"""Warp the image using a random composition of sine waves"""
|
|
||||||
|
|
||||||
def __init__(self, amplitude_range=(3, 6.5), period_range=(0.04, 0.1)):
|
|
||||||
self.amplitude = random.uniform(*amplitude_range)
|
|
||||||
self.period = random.uniform(*period_range)
|
|
||||||
self.offset = (random.uniform(0, math.pi * 2 / self.period),
|
|
||||||
random.uniform(0, math.pi * 2 / self.period))
|
|
||||||
|
|
||||||
def get_transform(self, image):
|
|
||||||
return (lambda x, y, a=self.amplitude, p=self.period,
|
|
||||||
o=self.offset: (math.sin((y + o[0]) * p) * a + x,
|
|
||||||
math.sin((x + o[1]) * p) * a + y))
|
|
@ -179,19 +179,6 @@ div.related h3 a {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.notification div.captcha {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.notification div.captcha p {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.notification div.captcha img {
|
|
||||||
display: block;
|
|
||||||
margin: 8px 0 8px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.related h3 a:hover,
|
div.related h3 a:hover,
|
||||||
div.paste_filter h3 a:hover {
|
div.paste_filter h3 a:hover {
|
||||||
background-color: #739BA5;
|
background-color: #739BA5;
|
||||||
|
@ -21,9 +21,6 @@ urlmap = Map([
|
|||||||
Rule('/unidiff/<new_id>/<old_id>/', endpoint='pastes/unidiff_paste'),
|
Rule('/unidiff/<new_id>/<old_id>/', endpoint='pastes/unidiff_paste'),
|
||||||
Rule('/tree/<identifier>/', endpoint='pastes/show_tree'),
|
Rule('/tree/<identifier>/', endpoint='pastes/show_tree'),
|
||||||
|
|
||||||
# captcha for new paste
|
|
||||||
Rule('/_captcha.png', endpoint='pastes/show_captcha'),
|
|
||||||
|
|
||||||
# paste list
|
# paste list
|
||||||
Rule('/all/', endpoint='pastes/show_all'),
|
Rule('/all/', endpoint='pastes/show_all'),
|
||||||
Rule('/all/<int:page>/', endpoint='pastes/show_all'),
|
Rule('/all/<int:page>/', endpoint='pastes/show_all'),
|
||||||
|
@ -7,15 +7,7 @@
|
|||||||
<div class="notification">
|
<div class="notification">
|
||||||
<h3>{% trans %}Error While Pasting{% endtrans %}</h3>
|
<h3>{% trans %}Error While Pasting{% endtrans %}</h3>
|
||||||
<p>{% trans error=error|e %}Could not submit your paste because {{ error }}.{% endtrans %}</p>
|
<p>{% trans error=error|e %}Could not submit your paste because {{ error }}.{% endtrans %}</p>
|
||||||
{% if show_captcha %}
|
|
||||||
<div class="captcha">
|
|
||||||
<p>{% trans %}Please fill out the CAPTCHA to proceed:{% endtrans %}</p>
|
|
||||||
<img src="/_captcha.png" alt="{% trans %}a captcha you can't see. Sorry :({% endtrans %}">
|
|
||||||
<input type="text" name="captcha" size="20" />
|
|
||||||
</div>
|
|
||||||
{%- else %}
|
|
||||||
<p><a href="javascript:LodgeIt.hideNotification()">{% trans %}hide this message{% endtrans %}</a></p>
|
<p><a href="javascript:LodgeIt.hideNotification()">{% trans %}hide this message{% endtrans %}</a></p>
|
||||||
{%- endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if parent %}
|
{% if parent %}
|
||||||
|
Loading…
Reference in New Issue
Block a user