helpers.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. import json
  5. from django.conf import settings
  6. from django.contrib.staticfiles.storage import staticfiles_storage
  7. from django.template.loader import render_to_string
  8. from django.utils.safestring import mark_safe
  9. from django.utils.six import string_types
  10. from django_browserid.compat import reverse
  11. from django_browserid.util import LazyEncoder
  12. MANDATORY_LINK_CLASS_LOGIN = 'browserid-login'
  13. DEFAULT_LINK_CLASS_LOGIN = MANDATORY_LINK_CLASS_LOGIN + ' persona-button'
  14. MANDATORY_LINK_CLASS_LOGOUT = 'browserid-logout'
  15. DEFAULT_LINK_CLASS_LOGOUT = MANDATORY_LINK_CLASS_LOGOUT
  16. # Use no-op shims for registering template helpers for jingo if it isn't
  17. # found.
  18. class JingoRegister(object):
  19. def filter(self, func, *args, **kwargs):
  20. return func
  21. def function(self, func, *args, **kwargs):
  22. return func
  23. try:
  24. from jingo import register
  25. except ImportError:
  26. register = JingoRegister()
  27. @register.function
  28. def browserid_info():
  29. """
  30. Output the HTML for the info tag, which contains the arguments for
  31. navigator.id.request from the BROWSERID_REQUEST_ARGS setting. Should
  32. be called once at the top of the page just below the <body> tag.
  33. """
  34. # Force request_args to be a dictionary, in case it is lazily generated.
  35. request_args = dict(getattr(settings, 'BROWSERID_REQUEST_ARGS', {}))
  36. info = json.dumps({
  37. 'loginUrl': reverse('browserid.login'),
  38. 'logoutUrl': reverse('browserid.logout'),
  39. 'csrfUrl': reverse('browserid.csrf'),
  40. 'requestArgs': request_args,
  41. }, cls=LazyEncoder)
  42. return render_to_string('browserid/info.html', {
  43. 'info': info,
  44. })
  45. def browserid_button(text=None, next=None, link_class=None, attrs=None, href='#'):
  46. """
  47. Output the HTML for a BrowserID link.
  48. :param text:
  49. Text to use inside the link.
  50. :param next:
  51. Value to use for the data-next attribute on the link.
  52. :param link_class:
  53. Class to use for the link.
  54. :param attrs:
  55. Dictionary of attributes to add to the link. Values here override those
  56. set by other arguments.
  57. If given a string, it is parsed as JSON and is expected to be an object.
  58. :param href:
  59. href to use for the link.
  60. """
  61. attrs = attrs or {}
  62. if isinstance(attrs, string_types):
  63. attrs = json.loads(attrs)
  64. attrs.setdefault('class', link_class)
  65. attrs.setdefault('href', href)
  66. attrs.setdefault('data-next', next)
  67. return render_to_string('browserid/button.html', {
  68. 'text': text,
  69. 'attrs': attrs,
  70. })
  71. @register.function
  72. def browserid_login(text='Sign in', color=None, next=None,
  73. link_class=DEFAULT_LINK_CLASS_LOGIN,
  74. attrs=None, fallback_href='#'):
  75. """
  76. Output the HTML for a BrowserID login link.
  77. :param text:
  78. Text to use inside the link. Defaults to 'Sign in', which is not
  79. localized.
  80. :param color:
  81. Color to use for the login button; this will only work if you have
  82. included the default CSS provided by
  83. :py:func:`django_browserid.helpers.browserid_css`.
  84. Supported colors are: `'dark'`, `'blue'`, and `'orange'`.
  85. :param next:
  86. URL to redirect users to after they login from this link. Defaults to
  87. :attr:`.views.Verify.success_url`.
  88. :param link_class:
  89. CSS class for the link. Defaults to `browserid-login persona-button`.
  90. :param attrs:
  91. Dictionary of attributes to add to the link. Values here override those
  92. set by other arguments.
  93. If given a string, it is parsed as JSON and is expected to be an object.
  94. :param fallback_href:
  95. Value to use for the href of the link. If the user has disabled
  96. JavaScript, the login link will bring them to this page, which can be
  97. used as a non-JavaScript login fallback.
  98. """
  99. if MANDATORY_LINK_CLASS_LOGIN not in link_class.split(' '):
  100. link_class += ' ' + MANDATORY_LINK_CLASS_LOGIN
  101. if color:
  102. if 'persona-button' not in link_class.split(' '):
  103. link_class += ' persona-button {0}'.format(color)
  104. else:
  105. link_class += ' ' + color
  106. next = next or ''
  107. return browserid_button(text, next, link_class, attrs, fallback_href)
  108. @register.function
  109. def browserid_logout(text='Sign out', next=None,
  110. link_class=DEFAULT_LINK_CLASS_LOGOUT, attrs=None):
  111. """
  112. Output the HTML for a BrowserID logout link.
  113. :param text:
  114. Text to use inside the link. Defaults to 'Sign out', which is not
  115. localized.
  116. :param next:
  117. URL to redirect users to after they logout from this link. Defaults
  118. to :attr:`.views.Logout.redirect_url`.
  119. :param link_class:
  120. CSS classes for the link. The classes will be appended to the
  121. default class `browserid-logout`.
  122. :param attrs:
  123. Dictionary of attributes to add to the link. Values here override those
  124. set by other arguments.
  125. If given a string, it is parsed as JSON and is expected to be an object.
  126. """
  127. next = next or ''
  128. if MANDATORY_LINK_CLASS_LOGOUT not in link_class.split(' '):
  129. link_class += ' ' + MANDATORY_LINK_CLASS_LOGOUT
  130. return browserid_button(text, next, link_class,
  131. attrs, reverse('browserid.logout'))
  132. @register.function
  133. def browserid_js(include_shim=True):
  134. """
  135. Return <script> tags for the JavaScript required by the BrowserID login
  136. button. Requires use of the staticfiles app.
  137. If the BROWSERID_AUTOLOGIN_ENABLED setting is True, an extra JavaScript
  138. file for mocking out Persona will be included, and the shim won't
  139. be included regardless of the value of the ``include_shim`` setting.
  140. :param include_shim:
  141. A boolean that determines if the persona.org JavaScript shim is included
  142. in the output. Useful if you want to minify the button JavaScript using
  143. a library like django-compressor that can't handle external JavaScript.
  144. """
  145. files = []
  146. autologin_enabled = getattr(settings, 'BROWSERID_AUTOLOGIN_ENABLED', False)
  147. # Include navigator.id shim only if we're not doing autologin.
  148. if include_shim and not autologin_enabled:
  149. files.append(getattr(settings, 'BROWSERID_SHIM', 'https://login.persona.org/include.js'))
  150. # Include django-browserid API
  151. files.append(staticfiles_storage.url('browserid/api.js'))
  152. # If we're doing autologin, include the JS to mock out certain parts
  153. # of the API.
  154. if autologin_enabled:
  155. files.append(staticfiles_storage.url('browserid/autologin.js'))
  156. # Include the JS to bind to login buttons.
  157. files.append(staticfiles_storage.url('browserid/browserid.js'))
  158. tags = ['<script type="text/javascript" src="{0}"></script>'.format(path)
  159. for path in files]
  160. return mark_safe('\n'.join(tags))
  161. @register.function
  162. def browserid_css():
  163. """
  164. Return <link> tag for the optional CSS included with django-browserid.
  165. Requires use of the staticfiles app.
  166. """
  167. url = staticfiles_storage.url('browserid/persona-buttons.css')
  168. return mark_safe('<link rel="stylesheet" href="{0}" />'.format(url))