Package Products :: Package ZenWidgets :: Module ZenTableManager |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenWidgets.ZenTableManager

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 as published by
      8  # the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__="""ZenTableManager
     15
     16  ZenTableManager is a Zope Product that helps manage and display
     17  large sets of tabular data.  It allows for column sorting,
     18  break down of the set into pages, and filtering of elements
     19  in the table.  It also allows users to store their own default
     20  page size (but publishes a hook to get this values from
     21  a different location).
     22
     23
     24  $Id: ZenTableManager.py,v 1.4 2004/04/03 04:18:22 edahl Exp $"""
     25
     26  __revision__ = "$Revision: 1.4 $"[11:-2]
     27
     28  import re
     29  import types
     30  import ZTUtils
     31  from Globals import InitializeClass
     32  from Acquisition import aq_base
     33  from OFS.SimpleItem import SimpleItem
     34  from OFS.PropertyManager import PropertyManager
     35  from DocumentTemplate.sequence.SortEx import sort
     36
     37  from ZenTableState import ZenTableState
     38
     39
     40 -class TableStateNotFound(Exception): pass
     41
     42
     43 -def manage_addZenTableManager(context, id="", REQUEST = None):
     44      """make a CVDeviceLoader"""
     45      if not id: id = "ZenTableManager"
     46      ztm = ZenTableManager(id)
     47      context._setObject(id, ztm)
     48      ztm = context._getOb(id)
     49      ztm.initTableManagerSkins()
     50
     51      if REQUEST is not None:
     52          REQUEST.RESPONSE.redirect(context.absolute_url()
     53                                       +'/manage_main')
     54
     55 -class ZenTableManager(SimpleItem, PropertyManager):
     56      """ZenTableManager manages display of tabular data"""
     57
     58      portal_type = meta_type = 'ZenTableManager'
     59
     60      _properties = (
     61          {'id':'defaultBatchSize', 'type':'int','mode':'w'},
     62          {'id':'abbrStartLabel', 'type':'int','mode':'w'},
     63          {'id':'abbrEndLabel', 'type':'int','mode':'w'},
     64          {'id':'abbrPadding', 'type':'int','mode':'w'},
     65          {'id':'abbrSeparator', 'type':'string','mode':'w'},
     66      )
     67
     68      manage_options = (
     69                          PropertyManager.manage_options +
     70                          SimpleItem.manage_options
     71                       )
     72
     73
     74 -    def __init__(self, id):
     75          self.id = id
     76          self.defaultBatchSize = 40
     77          self.abbrStartLabel = 15
     78          self.abbrEndLabel = 5
     79          self.abbrPadding = 5
     80          self.abbrSeparator = ".."
     81          self.abbrThresh = self.abbrStartLabel + \
     82                          self.abbrEndLabel + self.abbrPadding
     83
     84
     85 -    def getDefaultBatchSize(self):
     86          dbs = self.defaultBatchSize
     87          zu = getattr(self, "ZenUsers", None)
     88          if zu and zu.getUserSettings():
     89              dbs = zu.getUserSettings().defaultPageSize
     90          return dbs
     91
     92
     93 -    def setupTableState(self, tableName, **keys):
     94          """initialize or setup the session variable to track table state"""
     95          tableState = self.getTableState(tableName, **keys)
     96          request = self.REQUEST
     97          tableState.updateFromRequest(request)
     98          return tableState
     99
    100
    101 -    def getTableState(self, tableName, attrname=None, default=None, **keys):
    102          """return an existing table state or a single value from the state"""
    103          from Products.ZenUtils.Utils import unused
    104          unused(default)
    105          request = self.REQUEST
    106          tableStates = self.getTableStates()
    107          tableState = tableStates.get(tableName, None)
    108          if not tableState:
    109              dbs = self.getDefaultBatchSize()
    110              tableStates[tableName] = ZenTableState(request,tableName,dbs,**keys)
    111              tableState = tableStates[tableName]
    112          if attrname == None:
    113              return tableStates[tableName]
    114          return getattr(tableState, attrname, None)
    115
    116
    117 -    def getReqTableState(self, tableName, attrname):
    118          """
    119          Return attrname from request if present if not return from tableState.
    120          """
    121          request = self.REQUEST
    122          if request.has_key(attrname):
    123              return request[attrname]
    124          return self.getTableState(tableName, attrname)
    125
    126
    127 -    def setTableState(self, tableName, attrname, value):
    128          """Set the value of a table state attribute and return it."""
    129          tableState = self.getTableState(tableName)
    130          return tableState.setTableState(attrname, value)
    131
    132
    133 -    def setReqTableState(self, tableName, attrname, default=None, reset=False):
    134          """set the a value in the table state from the request"""
    135          tableState = self.getTableState(tableName)
    136          value = self.REQUEST.get(attrname, None)
    137          tableState = self.getTableState(tableName)
    138          return tableState.setTableState(attrname, value,
    139                                          default=default, reset=reset)
    140
    141
    142 -    def getBatch(self, tableName, objects, **keys):
    143          """Filter, sort and batch objects and pass return set.
    144          """
    145          if not objects:
    146              objects = []
    147          tableState = self.setupTableState(tableName, **keys)
    148          if tableState.onlyMonitored and objects:
    149              objects = [o for o in objects if o.monitored()]
    150          if tableState.filter and objects:
    151              objects = self.filterObjects(objects, tableState.filter,
    152                                          tableState.filterFields)
    153          # objects is frequently a generator.  Need a list in order to sort
    154          if not isinstance(objects, list):
    155              objects = list(objects)
    156          if tableState.sortedHeader:
    157              objects = self.sortObjects(objects, tableState)
    158          tableState.totalobjs = len(objects)
    159          tableState.buildPageNavigation(objects)
    160          if not hasattr(self.REQUEST, 'doExport'):
    161              objects = ZTUtils.Batch(objects,
    162                          tableState.batchSize or len(objects),
    163                          start=tableState.start, orphan=0)
    164          return objects
    165
    166
    167 -    def getBatchForm(self, objects, request):
    168          """Create batch based on objects no sorting for filter applied.
    169          """
    170          batchSize = request.get('batchSize',self.defaultBatchSize)
    171          if batchSize in ['', '0']:
    172              batchSize = 0
    173          else:
    174              batchSize = int(batchSize)
    175          start = int(request.get('start',0))
    176          resetStart = int(request.get('resetStart',0))
    177          lastindex = request.get('lastindex',0)
    178          navbutton = request.get('navbutton',None)
    179          if navbutton == "first" or resetStart:
    180              start = 0
    181          elif navbutton == "last":
    182              start=lastindex
    183          elif navbutton == "next":
    184              start = start + batchSize
    185              if start > lastindex: start = lastindex
    186          elif navbutton == "prev":
    187              start = start - batchSize
    188          elif request.has_key("nextstart"):
    189              start = request.nextstart
    190          if 0 < start > len(objects): start = 0
    191          request.start = start
    192          objects = ZTUtils.Batch(objects, batchSize or len(objects),
    193                      start=request.start, orphan=0)
    194          return objects
    195
    196
    197 -    def filterObjects(self, objects, regex, filterFields):
    198          """filter objects base on a regex in regex and list of fields
    199          in filterFields."""
    200          if self.REQUEST.SESSION.has_key('message'):
    201              self.REQUEST.SESSION.delete('message')
    202          if not regex:
    203              return objects
    204          try: search = re.compile(regex,re.I).search
    205          except re.error:
    206              self.REQUEST.SESSION['message'] = "Invalid regular expression."
    207              return objects
    208          filteredObjects = []
    209          for obj in objects:
    210              target = []
    211              for field in filterFields:
    212                  if isinstance(obj, dict):
    213                      value = obj.get(field, None)
    214                  else:
    215                      value = getattr(obj, field, None)
    216                  if callable(value):
    217                      value = value()
    218                  if type(value) not in types.StringTypes:
    219                      value = str(value)
    220                  target.append(value)
    221              targetstring = " ".join(target)
    222              if search(targetstring): filteredObjects.append(obj)
    223          return filteredObjects
    224
    225
    226 -    def sortObjects(self, objects, request):
    227          """Sort objects.
    228          """
    229          def dictAwareSort(objects, field, rule, sence):
    230              if not objects:
    231                  return objects
    232              class Wrapper:
    233                  def __init__(self, field, cargo):
    234                      if callable(field): field = field()
    235                      self.field = field
    236                      self.cargo = cargo
    237              if isinstance(objects[0], dict):
    238                  objects = [Wrapper(o.get(field, ''), o) for o in objects]
    239              else:
    240                  objects = [Wrapper(getattr(o, field, ''), o) for o in objects]
    241              objects = sort(objects, (('field', rule, sence),))
    242              return [w.cargo for w in objects]
    243
    244          if (getattr(aq_base(request), 'sortedHeader', False)
    245              and getattr(aq_base(request),"sortedSence", False)):
    246              sortedHeader = request.sortedHeader
    247              sortedSence = request.sortedSence
    248              sortRule = getattr(aq_base(request), "sortRule", "cmp")
    249              objects = dictAwareSort(objects, sortedHeader, sortRule, sortedSence)
    250          return objects
    251
    252
    253 -    def getTableHeader(self, tableName, fieldName, fieldTitle,
    254                  sortRule='cmp', style='tableheader',attributes=""):
    255          """generate a <th></th> tag that allows column sorting"""
    256          href = self.getTableHeaderHref(tableName, fieldName, sortRule)
    257          style = self.getTableHeaderStyle(tableName, fieldName, style)
    258          tag = """<th class="%s" %s>""" % (style, attributes)
    259          tag += """<a class="%s" href="%s""" % (style, href)
    260          tag += fieldTitle + "</a></th>\n"
    261          return tag
    262
    263
    264 -    def getTableHeaderHref(self, tableName, fieldName,
    265                              sortRule='cmp',params=""):
    266          """build the href attribute for the table table headers"""
    267
    268          tableState = self.getTableState(tableName)
    269          sortedHeader = tableState.sortedHeader
    270          sortedSence = tableState.sortedSence
    271          if sortedHeader == fieldName:
    272              if sortedSence == 'asc':
    273                  sortedSence = 'desc'
    274              elif sortedSence == 'desc':
    275                  fieldName = ''
    276                  sortedSence = ''
    277          else:
    278              sortedSence = 'asc'
    279          href = "%s?tableName=%s&sortedHeader=%s&" % (
    280                  self.REQUEST.URL, tableName, fieldName)
    281          href += "sortedSence=%s&sortRule=%s%s\">" % (
    282                  sortedSence, sortRule, params)
    283          tableState.addFilterField(fieldName)
    284          return href
    285
    286
    287 -    def getTableHeaderStyle(self, tableName, fieldName, style):
    288          """apends "selected" onto the CSS style if this field is selected"""
    289          if self.getTableState(tableName, "sortedHeader") == fieldName:
    290              style = style + "selected"
    291          return style
    292
    293
    294 -    def getTableStates(self):
    295          session = self.REQUEST.SESSION
    296          try:
    297              return session['zentablestates']
    298          except KeyError:
    299              init = {}
    300              session['zentablestates'] = init
    301              return init
    302
    303
    304 -    def tableStatesHasTable(self, tableName):
    305          return self.getTableStates().has_key(tableName)
    306
    307
    308 -    def getNavData(self, objects, batchSize, sortedHeader):
    309          pagenav = []
    310          if batchSize in ['', '0']:
    311              batchSize = 0
    312          else:
    313              batchSize = int(batchSize)
    314          for index in range(0, len(objects), batchSize or len(objects)):
    315              if sortedHeader:
    316                  label = self._buildTextLabel(objects[index], sortedHeader)
    317              elif batchSize:
    318                  label = str(1+index/batchSize)
    319              else:
    320                  label = '1'
    321              pagenav.append({ 'label': label, 'index': index })
    322          return pagenav
    323
    324
    325 -    def _buildTextLabel(self, item, sortedHeader):
    326          startAbbr = ""
    327          endAbbr = ""
    328          attr = getattr(item, sortedHeader, "")
    329          if callable(attr): attr = attr()
    330          label = str(attr)
    331          if len(label) > self.abbrThresh:
    332              startAbbr = label[:self.abbrStartLabel]
    333              if self.abbrEndLabel > 0:
    334                  endAbbr = label[-self.abbrEndLabel:]
    335              label = "".join((startAbbr, self.abbrSeparator, endAbbr))
    336          return label
    337
    338
    339 -    def initTableManagerSkins(self):
    340          """setup the skins that come with ZenTableManager"""
    341          layers = ('zentablemanager','zenui')
    342          try:
    343              import string
    344              from Products.CMFCore.utils import getToolByName
    345              from Products.CMFCore.DirectoryView import addDirectoryViews
    346              skinstool = getToolByName(self, 'portal_skins')
    347              for layer in layers:
    348                  if layer not in skinstool.objectIds():
    349                      addDirectoryViews(skinstool, 'skins', globals())
    350              skins = skinstool.getSkinSelections()
    351              for skin in skins:
    352                  path = skinstool.getSkinPath(skin)
    353                  path = map(string.strip, string.split(path,','))
    354                  for layer in layers:
    355                      if layer not in path:
    356                          try:
    357                              path.insert(path.index('custom')+1, layer)
    358                          except ValueError:
    359                              path.append(layer)
    360                  path = ','.join(path)
    361                  skinstool.addSkinSelection(skin, path)
    362          except ImportError, e:
    363              if "Products.CMFCore.utils" in e.args: pass
    364              else: raise
    365          except AttributeError, e:
    366              if "portal_skin" in e.args: pass
    367              else: raise
    368
    369
    370  InitializeClass(ZenTableManager)
    371