/raptorizemw/middleware.py

https://github.com/ralphbean/raptorizemw · Python · 151 lines · 89 code · 30 blank · 32 comment · 15 complexity · a0472412d1f3e8961c2d9ad77500f6e7 MD5 · raw file

  1. import BeautifulSoup
  2. import datetime
  3. import random
  4. import webob
  5. import raptorizemw.resources
  6. truthy = frozenset(('t', 'true', 'y', 'yes', 'on', '1'))
  7. def asbool(s):
  8. """ Return the boolean value ``True`` if the case-lowered value of string
  9. input ``s`` is any of ``t``, ``true``, ``y``, ``on``, or ``1``, otherwise
  10. return the boolean value ``False``. If ``s`` is the value ``None``,
  11. return ``False``. If ``s`` is already one of the boolean values ``True``
  12. or ``False``, return it."""
  13. if s is None:
  14. return False
  15. if isinstance(s, bool):
  16. return s
  17. s = str(s).strip()
  18. return s.lower() in truthy
  19. class RaptorizeMiddleware(object):
  20. """ WSGI middleware that throws a raptor on your page. """
  21. def __init__(self, app, serve_resources=True, random_chance=1.0,
  22. only_on_april_1st=False, enterOn='timer', delayTime=2000,
  23. **kw):
  24. """ Configuration arguments are documented in README.rst
  25. Also at http://pypi.python.org/pypi/raptorizemw
  26. """
  27. if not enterOn in ['timer', 'konami-code']:
  28. raise ValueError("enterOn must be either 'timer' or 'konami-code'")
  29. self.resources_app = raptorizemw.resources.ResourcesApp()
  30. self.app = app
  31. self.serve_resources = serve_resources
  32. self.random_chance = float(random_chance)
  33. self.only_on_april_1st = asbool(only_on_april_1st)
  34. self.enterOn = enterOn
  35. self.delayTime = int(delayTime)
  36. def __call__(self, environ, start_response):
  37. """ Process a request.
  38. Do one of two things::
  39. - If this request is for a raptor resource (image, sound, js
  40. code). Ignore the next WSGI layer in the call chain and just
  41. serve the resource myself.
  42. - Call the next layer in the WSGI call chain and retrieve its
  43. output. Determine if I should actually raptorize this request
  44. and if so, insert our magic javascript in the <head> tag.
  45. """
  46. __app__ = None
  47. if self.serve_resources and 'raptorizemw' in environ['PATH_INFO']:
  48. __app__ = self.resources_app
  49. else:
  50. __app__ = self.app
  51. req = webob.Request(environ)
  52. resp = req.get_response(__app__, catch_exc_info=True)
  53. if self.should_raptorize(req, resp):
  54. resp = self.raptorize(resp)
  55. return resp(environ, start_response)
  56. def should_raptorize(self, req, resp):
  57. """ Determine if this request should be raptorized. Boolean. """
  58. if resp.status != "200 OK":
  59. return False
  60. content_type = resp.headers.get('Content-Type', 'text/plain').lower()
  61. if not 'html' in content_type:
  62. return False
  63. if random.random() > self.random_chance:
  64. return False
  65. if self.only_on_april_1st:
  66. now = datetime.datetime.now()
  67. if now.month != 20 and now.day != 1:
  68. return False
  69. return True
  70. def raptorize(self, resp):
  71. """ Raptorize this response!
  72. Insert javascript into the <head> tag.
  73. If jquery is already included, make sure not to stomp on it by
  74. re-including it.
  75. """
  76. soup = BeautifulSoup.BeautifulSoup(resp.body)
  77. if not soup.html:
  78. return resp
  79. if not soup.html.head:
  80. soup.html.insert(0, BeautifulSoup.Tag(soup, "head"))
  81. prefix = self.resources_app.prefix
  82. js_helper = BeautifulSoup.Tag(
  83. soup, "script", attrs=[
  84. ('type', 'text/javascript'),
  85. ('src', prefix + '/js_helper.js'),
  86. ])
  87. soup.html.head.insert(len(soup.html.head), js_helper)
  88. payload_js = BeautifulSoup.Tag(
  89. soup, "script", attrs=[
  90. ('type', 'text/javascript'),
  91. ])
  92. payload_js.setString(
  93. """
  94. run_with_jquery(function() {
  95. include_js("%s", function() {
  96. $(window).load(function() {
  97. $('body').raptorize({
  98. enterOn: "%s",
  99. delayTime: %i,
  100. });
  101. });
  102. })
  103. });
  104. """ % (
  105. prefix + '/jquery.raptorize.1.0.js',
  106. self.enterOn,
  107. self.delayTime
  108. )
  109. )
  110. soup.html.head.insert(len(soup.html.head), payload_js)
  111. resp.body = str(soup.prettify())
  112. return resp
  113. def make_middleware(app=None, *args, **kw):
  114. """ Given an app, return that app wrapped in RaptorizeMiddleware """
  115. app = RaptorizeMiddleware(app, *args, **kw)
  116. return app