Latest Version: 0.9.6.2
/Users/bbangert/Programming/Python/WebHelpers/webhelpers/rails/urls.py
0001"""URL Helpers"""
0002# Last synced with Rails copy at Revision 6070 on Feb 8th, 2007.
0003
0004import re
0005import urllib
0006
0007from routes import url_for, request_config
0008
0009import tags
0010from asset_tag import compute_public_path
0011from javascript import *
0012from webhelpers.util import html_escape
0013
0014def get_url(url):
0015    if callable(url):
0016        return url()
0017    else:
0018        return url
0019
0020def url(*args, **kargs):
0021    """
0022    Lazily evaluates url_for() arguments
0023    
0024    Used instead of url_for() for functions so that the function will be evaluated
0025    in a lazy manner rather than at initial function call.
0026    """
0027    args = args
0028    kargs = kargs
0029    def call():
0030        return url_for(*args, **kargs)
0031    return call
0032
0033def link_to(name, url='', **html_options):
0034    """
0035    Creates a link tag of the given ``name`` using an URL created by the set of ``options``.
0036    
0037    See the valid options in the documentation for Routes url_for.
0038    
0039    The html_options has three special features. One for creating javascript confirm alerts where if you pass
0040    ``confirm='Are you sure?'`` , the link will be guarded with a JS popup asking that question. If the user
0041    accepts, the link is processed, otherwise not.
0042    
0043    Another for creating a popup window, which is done by either passing ``popup`` with True or the options
0044    of the window in Javascript form.
0045    
0046    And a third for making the link do a POST request (instead of the regular GET) through a dynamically added
0047    form element that is instantly submitted. Note that if the user has turned off Javascript, the request will
0048    fall back on the GET. So its your responsibility to determine what the action should be once it arrives at
0049    the controller. The POST form is turned on by passing ``post`` as True. Note, it's not possible to use POST
0050    requests and popup targets at the same time (an exception will be thrown).
0051    
0052    Examples::
0053    
0054        >> link_to("Delete this page", url(action="destroy", id=4), confirm="Are you sure?")
0055        >> link_to("Help", url(action="help"), popup=True)
0056        >> link_to("Busy loop", url(action="busy"), popup=['new_window', 'height=300,width=600'])
0057        >> link_to("Destroy account", url(action="destroy"), confirm="Are you sure?", method='delete')
0058    """
0059    if html_options:
0060        html_options = convert_options_to_javascript(**html_options)
0061        tag_op = tags.tag_options(**html_options)
0062    else:
0063        tag_op = ''
0064    if callable(url):
0065        url = url()
0066    else:
0067        url = html_escape(url)
0068    return "<a href=\"%s\"%s>%s</a>" % (url, tag_op, name or url)
0069
0070def button_to(name, url='', **html_options):
0071    """
0072    Generates a form containing a sole button that submits to the
0073    URL given by ``url``.  
0074    
0075    Use this method instead of ``link_to`` for actions that do not have the safe HTTP GET semantics
0076    implied by using a hypertext link.
0077    
0078    The parameters are the same as for ``link_to``.  Any ``html_options`` that you pass will be
0079    applied to the inner ``input`` element.
0080    In particular, pass
0081    
0082        disabled = True/False
0083    
0084    as part of ``html_options`` to control whether the button is
0085    disabled.  The generated form element is given the class
0086    'button-to', to which you can attach CSS styles for display
0087    purposes.
0088    
0089    The submit button itself will be displayed as an image if you provide both
0090    ``type`` and ``src`` as followed:
0091
0092         type='image', src='icon_delete.gif'
0093
0094    The ``src`` path will be computed as the image_tag() computes it's ``source``
0095    argument.
0096
0097    Example 1::
0098    
0099        # inside of controller for "feeds"
0100        >> button_to("Edit", url(action='edit', id=3))
0101        <form method="POST" action="/feeds/edit/3" class="button-to">
0102        <div><input value="Edit" type="submit" /></div>
0103        </form>
0104    
0105    Example 2::
0106    
0107        >> button_to("Destroy", url(action='destroy', id=3), confirm="Are you sure?", method='DELETE')
0108        <form method="POST" action="/feeds/destroy/3" class="button-to">
0109        <div>
0110            <input type="hidden" name="_method" value="DELETE" />
0111            <input onclick="return confirm('Are you sure?');" value="Destroy" type="submit" />
0112        </div>
0113        </form>
0114
0115    Example 3::
0116
0117        # Button as an image.
0118        >> button_to("Edit", url(action='edit', id=3), type='image', src='icon_delete.gif')
0119        <form method="POST" action="/feeds/edit/3" class="button-to">
0120        <div><input alt="Edit" src="/images/icon_delete.gif" type="image" value="Edit" /></div>
0121        </form>
0122    
0123    *NOTE*: This method generates HTML code that represents a form.
0124    Forms are "block" content, which means that you should not try to
0125    insert them into your HTML where only inline content is expected.
0126    For example, you can legally insert a form inside of a ``div`` or
0127    ``td`` element or in between ``p`` elements, but not in the middle of
0128    a run of text, nor can you place a form within another form.
0129    (Bottom line: Always validate your HTML before going public.)    
0130    """
0131    if html_options:
0132        convert_boolean_attributes(html_options, ['disabled'])
0133
0134    method_tag = ''
0135    method = html_options.pop('method', '')
0136    if method.upper() in ['PUT', 'DELETE']:
0137        method_tag = tags.tag('input', type_='hidden', id='_method', name_='_method',
0138                              value=method)
0139
0140    form_method = (method.upper() == 'GET' and method) or 'POST'
0141
0142    confirm = html_options.get('confirm')
0143    if confirm:
0144        del html_options['confirm']
0145        html_options['onclick'] = "return %s;" % confirm_javascript_function(confirm)
0146
0147    if callable(url):
0148        ur = url()
0149        url, name = ur, name or tags.escape_once(ur)
0150    else:
0151        url, name = url, name or url
0152
0153    submit_type = html_options.get('type')
0154    img_source = html_options.get('src')
0155    if submit_type == 'image' and img_source:
0156        html_options.update(dict(type=submit_type, value=name,
0157                                 alt=html_options.get('alt', name)))
0158        html_options['src'] = compute_public_path(img_source, 'images', 'png')
0159    else:
0160        html_options.update(dict(type='submit', value=name))
0161
0162    return """<form method="%s" action="%s" class="button-to"><div>""" %           (form_method, tags.escape_once(url)) + method_tag +           tags.tag("input", **html_options) + "</div></form>"
0165
0166def link_to_unless_current(name, url, **html_options):
0167    """
0168    Conditionally create a link tag of the given ``name`` using the ``url``
0169    
0170    If the current request uri is the same as the link's only the name is returned. This is useful
0171    for creating link bars where you don't want to link to the page currently being viewed.
0172    """
0173    return link_to_unless(current_page(url), name, url, **html_options)
0174
0175def link_to_unless(condition, name, url, **html_options):
0176    """
0177    Conditionally create a link tag of the given ``name`` using the ``url``
0178    
0179    If ``condition`` is True only the name is returned.
0180    """
0181    if condition:
0182        return name
0183    else:
0184        return link_to(name, url, **html_options)
0185
0186def link_to_if(condition, name, url, **html_options):
0187    """
0188    Conditionally create a link tag of the given ``name`` using the ``url`` 
0189    
0190    If ``condition`` is True only the name is returned.
0191    """
0192    return link_to_unless(not condition, name, url, **html_options)
0193
0194def current_page(url):
0195    """
0196    Returns true if the current page uri is equivalent to ``url``
0197    """
0198    currl = current_url()
0199    if callable(url):
0200        return url() == currl
0201    else:
0202        return url == currl
0203
0204def current_url(*args, **kwargs):
0205    """
0206    Returns the current page's url.
0207    """
0208    config = request_config()
0209    environ = config.environ
0210    qs = environ.get('QUERY_STRING', '')
0211    if qs:
0212        qs = '?' + qs
0213    return url_for(*args, **kwargs) + qs
0214
0215def convert_options_to_javascript(confirm=None, popup=None, post=None, method=None, **html_options):
0216    if post and not method:
0217        method = 'POST'
0218
0219    if popup and method:
0220        raise ValueError("You can't use popup and post in the same link")
0221    elif confirm and popup:
0222        oc = "if (%s) { %s };return false;" % (confirm_javascript_function(confirm),
0223                                               popup_javascript_function(popup))
0224    elif confirm and method:
0225        oc = "if (%s) { %s };return false;" % (confirm_javascript_function(confirm),
0226                                               method_javascript_function(method))
0227    elif confirm:
0228        oc = "return %s;" % confirm_javascript_function(confirm)
0229    elif method:
0230        oc = "%sreturn false;" % method_javascript_function(method)
0231    elif popup:
0232        oc = popup_javascript_function(popup) + 'return false;'
0233    else:
0234        oc = html_options.get('onclick')
0235    html_options['onclick'] = oc
0236    return html_options
0237
0238def convert_boolean_attributes(html_options, bool_attrs):
0239    for attr in bool_attrs:
0240        if html_options.has_key(attr) and html_options[attr]:
0241            html_options[attr] = attr
0242        elif html_options.has_key(attr):
0243            del html_options[attr]
0244
0245def confirm_javascript_function(confirm):
0246    return "confirm('%s')" % escape_javascript(confirm)
0247
0248def popup_javascript_function(popup):
0249    if isinstance(popup, list):
0250        return "window.open(this.href,'%s','%s');" % (popup[0], popup[-1])
0251    else:
0252        return "window.open(this.href);"
0253
0254def method_javascript_function(method):
0255    submit_function = "var f = document.createElement('form'); f.style.display = 'none'; " +           "this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;"
0257
0258    if method.upper() != 'POST':
0259        submit_function += "var m = document.createElement('input'); m.setAttribute('type', 'hidden'); "
0260        submit_function += "m.setAttribute('name', '_method'); m.setAttribute('value', '%s'); f.appendChild(m);" % method
0261
0262    return submit_function + "f.submit();"
0263
0264
0265def mail_to(email_address, name=None, cc=None, bcc=None, subject=None,
0266    body=None, replace_at=None, replace_dot=None, encode=None, **html_options):
0267    """
0268    Creates a link tag for starting an email to the specified 
0269    ``email_address``, which is also used as the name of the link unless
0270    ``name`` is specified. Additional HTML options, such as class or id, can be
0271    passed in the ``html_options`` hash.
0272    
0273    You can also make it difficult for spiders to harvest email address by 
0274    obfuscating them.
0275    
0276    Examples::
0277    
0278        >>> mail_to("me@domain.com", "My email", encode = "javascript")
0279        '<script type="text/javascript">\\n//<![CDATA[\\neval(unescape(\\'%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b\\'))\\n//]]>\\n</script>'
0280    
0281        >>> mail_to("me@domain.com", "My email", encode = "hex")
0282        '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>'
0283    
0284    You can also specify the cc address, bcc address, subject, and body parts
0285    of the message header to create a complex e-mail using the corresponding
0286    ``cc``, ``bcc``, ``subject``, and ``body`` keyword arguments. Each of these
0287    options are URI escaped and then appended to the ``email_address`` before
0288    being output. **Be aware that javascript keywords will not be escaped and
0289    may break this feature when encoding with javascript.**
0290    
0291    Examples::
0292    
0293        >>> mail_to("me@domain.com", "My email", cc="ccaddress@domain.com", bcc="bccaddress@domain.com", subject="This is an example email", body= "This is the body of the message.")
0294        '<a href="mailto:me@domain.com?cc=ccaddress%40domain.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email&amp;bcc=bccaddress%40domain.com">My email</a>'
0295    """
0296    extras = {}
0297    for key, option in ('cc', cc), ('bcc', bcc), ('subject', subject), ('body', body):
0298        if option:
0299            extras[key] = option
0300    options_query = urllib.urlencode(extras).replace("+", "%20")
0301    protocol = 'mailto:'
0302
0303    email_address_obfuscated = email_address
0304    if replace_at:
0305        email_address_obfuscated = email_address_obfuscated.replace('@', replace_at)
0306    if replace_dot:
0307        email_address_obfuscated = email_address_obfuscated.replace('.', replace_dot)
0308
0309    if encode == 'hex':
0310        email_address_obfuscated = ''.join(['&#%d;' % ord(x) for x in email_address_obfuscated])
0311        protocol = ''.join(['&#%d;' % ord(x) for x in protocol])
0312
0313        word_re = re.compile('\w')
0314        encoded_parts = []
0315        for x in email_address:
0316            if word_re.match(x):
0317                encoded_parts.append('%%%x' % ord(x))
0318            else:
0319                encoded_parts.append(x)
0320        email_address = ''.join(encoded_parts)
0321
0322    url = protocol + email_address
0323    if options_query:
0324        url += '?' + options_query
0325    html_options['href'] = url
0326
0327    tag = tags.content_tag('a', name or email_address_obfuscated, **html_options)
0328
0329    if encode == 'javascript':
0330        tmp = "document.write('%s');" % tag
0331        string = ''.join(['%%%x' % ord(x) for x in tmp])
0332        return javascript_tag("eval(unescape('%s'))" % string)
0333    else :
0334        return tag
0335
0336def js_obfuscate(data):
0337    """Obfuscates data in a Javascript tag
0338    
0339    Example::
0340        
0341        >>> js_obfuscate("<input type='hidden' name='check' value='valid' />")
0342        '<script type="text/javascript">\\n//<![CDATA[\\neval(unescape(\\'%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%69%6e%70%75%74%20%74%79%70%65%3d%27%68%69%64%64%65%6e%27%20%6e%61%6d%65%3d%27%63%68%65%63%6b%27%20%76%61%6c%75%65%3d%27%76%61%6c%69%64%27%20%2f%3e%27%29%3b\\'))\\n//]]>\\n</script>'
0343    """
0344    tmp = "document.write('%s');" % data
0345    string = ''.join(['%%%x' % ord(x) for x in tmp])
0346    return javascript_tag("eval(unescape('%s'))" % string)
0347
0348__all__ = ['url', 'link_to', 'button_to', 'link_to_unless_current', 'link_to_unless', 'link_to_if',
0349           'current_page', 'current_url', 'mail_to', 'js_obfuscate']

Top