Home | History | Annotate | Download | only in server2
      1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import copy
      6 import logging
      7 import posixpath
      8 
      9 from compiled_file_system import SingleFile, Unicode
     10 from data_source import DataSource
     11 from extensions_paths import JSON_TEMPLATES
     12 from future import Future
     13 from third_party.json_schema_compiler.json_parse import Parse
     14 
     15 
     16 def _AddLevels(items, level):
     17   '''Add a 'level' key to each item in |items|. 'level' corresponds to how deep
     18   in |items| an item is. |level| sets the starting depth.
     19   '''
     20   for item in items:
     21     item['level'] = level
     22     if 'items' in item:
     23       _AddLevels(item['items'], level + 1)
     24 
     25 
     26 def _AddAnnotations(items, path, parent=None):
     27   '''Add 'selected', 'child_selected' and 'related' properties to 
     28   |items| so that the sidenav can be expanded to show which menu item has 
     29   been selected and the related pages section can be drawn. 'related'
     30   is added to all items with the same parent as the selected item.
     31   If more than one item exactly matches the path, the deepest one is considered 
     32   'selected'. A 'parent' property is added to the selected path.
     33   
     34   Returns True if an item was marked 'selected'.
     35   '''
     36   for item in items:
     37     if 'items' in item:
     38       if _AddAnnotations(item['items'], path, item):
     39         item['child_selected'] = True
     40         return True
     41 
     42     if item.get('href', '') == path:
     43       item['selected'] = True
     44       if parent:
     45         item['parent'] = { 'title': parent.get('title', None), 
     46                           'href': parent.get('href', None) }
     47       
     48       for sibling in items:
     49         sibling['related'] = True
     50         
     51       return True
     52 
     53   return False
     54 
     55 
     56 class SidenavDataSource(DataSource):
     57   '''Provides templates with access to JSON files used to create the side
     58   navigation bar.
     59   '''
     60   def __init__(self, server_instance, request):
     61     self._cache = server_instance.compiled_fs_factory.Create(
     62         server_instance.host_file_system_provider.GetTrunk(),
     63         self._CreateSidenavDict,
     64         SidenavDataSource)
     65     self._server_instance = server_instance
     66     self._request = request
     67 
     68   @SingleFile
     69   @Unicode
     70   def _CreateSidenavDict(self, _, content):
     71     items = Parse(content)
     72     # Start at level 2, the top <ul> element is level 1.
     73     _AddLevels(items, level=2)
     74     self._QualifyHrefs(items)
     75     return items
     76 
     77   def _QualifyHrefs(self, items):
     78     '''Force hrefs in |items| to either be absolute (http://...) or qualified
     79     (beginning with /, in which case it will be moved relative to |base_path|).
     80     Relative hrefs emit a warning and should be updated.
     81     '''
     82     for item in items:
     83       if 'items' in item:
     84         self._QualifyHrefs(item['items'])
     85 
     86       href = item.get('href')
     87       if href is not None and not href.startswith(('http://', 'https://')):
     88         if not href.startswith('/'):
     89           logging.warn('Paths in sidenav must be qualified. %s is not.' % href)
     90         else:
     91           href = href.lstrip('/')
     92         item['href'] = self._server_instance.base_path + href
     93 
     94   def Cron(self):
     95     return self._cache.GetFromFile(
     96         posixpath.join(JSON_TEMPLATES, 'chrome_sidenav.json'))
     97 
     98   def get(self, key):
     99     # TODO(mangini/kalman): Use |key| to decide which sidenav to use,
    100     # which will require a more complex Cron method.
    101     sidenav = self._cache.GetFromFile(
    102         posixpath.join(JSON_TEMPLATES, 'chrome_sidenav.json')).Get()
    103     sidenav = copy.deepcopy(sidenav)
    104     _AddAnnotations(sidenav,
    105                     self._server_instance.base_path + self._request.path)
    106     return sidenav
    107