app/soc/views/helper/html_menu.py
author Pawel Solyga <Pawel.Solyga@gmail.com>
Thu, 16 Oct 2008 23:31:57 +0000
changeset 365 74dec172944e
parent 276 56357a92c110
child 390 d12c95ade374
permissions -rw-r--r--
Create decorators module in view/helper and add view decorator that catches exceptions like DeadlineExceededError, MemoryError, AssertionError (this code is being moved from respond function). Add view decorator to all view functions. In addition remove not needed imports from all affected files and fix too long lines. Patch by: Pawel Solyga Review by: to-be-reviewed

#!/usr/bin/python2.5
#
# Copyright 2008 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Helpers for displaying arbitrarily nested menus as HTML lists.
"""

__authors__ = [
  '"Todd Larsen" <tlarsen@google.com>',
  ]


from soc.logic import menu


class HtmlMenu:
  """Ordered collection of MenuItem objects as <p>...</p> paragraphs.
  """
  ITEM_PREFIX_FMT = '%(indent)s<p>'
  ITEM_SUFFIX_FMT = '%(indent)s</p>'

  def __init__(self, menu, item_class=None):
    """Wraps an soc.logic.menu.Menu in order to render it as HTML.
    
    Args:
      menu: an soc.logic.menu.Menu object
      item_class: style used to render the MenuItems contained in menu;
        default is None, which causes AHrefMenuItem to be used  
    """
    self._menu = menu

    # workaround for circular dependency between AHrefMenuItem and this class
    if not item_class:
      item_class = AHrefMenuItem

    self._item_class = item_class

  def getHtmlTags(self, indent):
    """Returns list of HTML tags for arbitrarily nested items in the menu.
    
    Args:
      indent: string prepended to the beginning of each line of output
        (usually consists entirely of spaces)
        
    Returns:
      a list of strings that can be joined with '\n' into a single string
      to produce an entire <ul>...</ul> list in HTML
    """
    tags = []

    if self._menu.items:
      tags.append(self.ITEM_PREFIX_FMT % {'indent': indent})

      for item in self._menu.items:
        tags.extend(self._item_class(
            item, menu_class=self.__class__).getHtmlTags(indent + ' '))
    
      tags.append(self.ITEM_SUFFIX_FMT % {'indent': indent})

    return tags

  def __str__(self):
    return '\n'.join(self.getHtmlTags(''))


class UlMenu(HtmlMenu):
  """Ordered collection of MenuItem objects as a <ul> list.
  """
  ITEM_PREFIX_FMT = '%(indent)s<ul>'
  ITEM_SUFFIX_FMT = '%(indent)s</ul>'

  def __init__(self, menu, item_class=None):
    """Wraps an soc.logic.menu.Menu in order to render it as HTML.
    
    Args:
      menu: an soc.logic.menu.Menu object
      item_class: style used to render the MenuItems contained in menu;
        default is None, which causes LiMenuItem to be used  
    """
    # workaround for circular dependency between LiMenuItem and this class
    if not item_class:
      item_class = LiMenuItem

    HtmlMenu.__init__(self, menu, item_class=item_class)


class HtmlMenuItem:
  """Base class for specific MenuItem wrappers used by HtmlMenu sub-classes.
  """
  
  def __init__(self, item, menu_class=HtmlMenu):
    """Wraps an soc.logic.menu.MenuItem in order to render it as HTML.
    
    Args:
      item: an soc.logic.menu.MenuItem object to wrap, in order to produce
        a representation of it as HTML tags later
      menu_class: a class derived from HtmlMenu, used to style any sub-menu
        of the MenuItem; default is HtmlMenu
    """
    self._item = self.escapeItem(item)
    self._menu_class = menu_class

  def getItemHtmlTags(self, indent):
    """Returns list of HTML tags for the menu item itself.
    
    This method is intended to be overridden by sub-classes.
    
    Args:
      indent: string prepended to the beginning of each line of output
        (usually consists entirely of spaces)
        
    Returns:
      a list of strings that can be joined with '\n' into a single string
      to produce:
        <b>name</b> value <i>(annotation)</i>
      with value and/or <i>(annotation)</i> omitted if either is missing
    """
    # TODO(tlarsen): implement "selected" style

    tags = ['%s<b>%s</b>' % (indent, self._item.name)]
    
    if self._item.value:
      tags.append('%s%s' % (indent, self._item.value))

    if self._item.annotation:
      tags.append('%s<i>(%s)</i>' % (indent, self._item.annotation))
      
    return tags

  def getSubMenuHtmlTags(self, indent):
    """Returns list of HTML tags for any sub-menu, if one exists.
    
    Args:
      indent: string prepended to the beginning of each line of output
        (usually consists entirely of spaces)
        
    Returns:
      an empty list if there is no sub-menu
        -OR-
      the list of HTML tags that render the entire sub-menu (depends on the
      menu_class that was provided to __init__()
    """
    if not self._item.sub_menu:
      return []
  
    return self._menu_class(self._item.sub_menu,
        item_class=self.__class__).getHtmlTags(indent)

  def getHtmlTags(self, indent):
    """Returns list of HTML tags for a menu item (and possibly its sub-menus).
    
    Args:
      indent: string prepended to the beginning of each line of output
        (usually consists entirely of spaces)
        
    Returns:
      a list of strings that can be joined with '\n' into a single string
      to produce an HTML representation of the wrapped MenuItem, with
      arbitrarily nested sub-menus possibly appended
    """
    return self.getItemHtmlTags(indent) + self.getSubMenuHtmlTags(indent)

  def escapeItem(self, item):
    """HTML-escapes possibly user-supplied fields to prevent XSS.
    
    Args:
      item: an soc.logic.menu.MenuItem that is altered in-place; the
        fields that are potentially user-provided (name, value, annotation)
        are escaped using self.escapeText()
        
    Returns:
      the originally supplied item, for convenience, so that this method can
      be combined with an assignment
    """
    item.name = self.escapeText(item.name)
    item.value = self.escapeText(item.value)
    item.annotation = self.escapeText(item.annotation)
    return item

  def escapeText(self, text):
    """
    """
    # TODO(tlarsen): user-supplied content *must* be escaped to prevent XSS
    return text

  def __str__(self):
    return '\n'.join(self.getHtmlTags(''))


class AHrefMenuItem(HtmlMenuItem):
  """Provides HTML menu item properties as attributes as an <a href> link. 
  """
  
  def getItemHtmlTags(self, indent):
    """Returns list of HTML tags for the menu item itself.
    
    Args:
      indent: string prepended to the beginning of each line of output
        (usually consists entirely of spaces)
        
    Returns:
      a list of strings that can be joined with '\n' into a single string
      to produce an <a href="...">...</a> link, or just the MenuItem.name
      as plain text if there was no AHrefMenuItem.value URL
    """
    # TODO(tlarsen): implement "selected" style

    if not self._item.value:
      # if no URL, then not a link, so just display item.name as text
      return [self._item.name]
  
    # URL supplied, so make an <a href="item.value">item.name</a> link
    return ['%s<a href=' % indent,
            '%s "%s"' % (indent, self._item.value),
            '%s>%s</a>' % (indent, self._item.name)]


class LiMenuItem(AHrefMenuItem):
  """Provides HTML menu item properties as attributes as an <li> list item.
  """

  def __init__(self, item, menu_class=UlMenu):
    """Wraps an soc.logic.menu.MenuItem in order to render it as HTML.
    
    Args:
      item: an soc.logic.menu.MenuItem object to wrap, in order to produce
        a representation of it as HTML tags later
      menu_class: a class derived from HtmlMenu, used to style any sub-menu
        of the MenuItem; default is UlMenu
    """
    AHrefMenuItem.__init__(self, item, menu_class=menu_class)

  def getHtmlTags(self, indent):
    """Returns <a href> link wrapped as an <li> list item.
    
    See also AHrefMenuItem.getHtmlTags().
    """
    return (['%s<li>' % indent]
            + AHrefMenuItem.getHtmlTags(self, indent + ' ')
            + ['%s</li>' % indent])