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 5import copy 6import logging 7 8from compiled_file_system import SingleFile, Unicode 9from data_source import DataSource 10from extensions_paths import JSON_TEMPLATES 11from future import Gettable, Future 12from third_party.json_schema_compiler.json_parse import Parse 13 14 15def _AddLevels(items, level): 16 '''Add a 'level' key to each item in |items|. 'level' corresponds to how deep 17 in |items| an item is. |level| sets the starting depth. 18 ''' 19 for item in items: 20 item['level'] = level 21 if 'items' in item: 22 _AddLevels(item['items'], level + 1) 23 24 25def _AddSelected(items, path): 26 '''Add 'selected' and 'child_selected' properties to |items| so that the 27 sidenav can be expanded to show which menu item has been selected. Returns 28 True if an item was marked 'selected'. 29 ''' 30 for item in items: 31 if item.get('href', '') == path: 32 item['selected'] = True 33 return True 34 if 'items' in item: 35 if _AddSelected(item['items'], path): 36 item['child_selected'] = True 37 return True 38 39 return False 40 41 42class SidenavDataSource(DataSource): 43 '''Provides templates with access to JSON files used to create the side 44 navigation bar. 45 ''' 46 def __init__(self, server_instance, request): 47 self._cache = server_instance.compiled_fs_factory.Create( 48 server_instance.host_file_system_provider.GetTrunk(), 49 self._CreateSidenavDict, 50 SidenavDataSource) 51 self._server_instance = server_instance 52 self._request = request 53 54 @SingleFile 55 @Unicode 56 def _CreateSidenavDict(self, _, content): 57 items = Parse(content) 58 # Start at level 2, the top <ul> element is level 1. 59 _AddLevels(items, level=2) 60 self._QualifyHrefs(items) 61 return items 62 63 def _QualifyHrefs(self, items): 64 '''Force hrefs in |items| to either be absolute (http://...) or qualified 65 (beginning with /, in which case it will be moved relative to |base_path|). 66 Relative hrefs emit a warning and should be updated. 67 ''' 68 for item in items: 69 if 'items' in item: 70 self._QualifyHrefs(item['items']) 71 72 href = item.get('href') 73 if href is not None and not href.startswith(('http://', 'https://')): 74 if not href.startswith('/'): 75 logging.warn('Paths in sidenav must be qualified. %s is not.' % href) 76 else: 77 href = href.lstrip('/') 78 item['href'] = self._server_instance.base_path + href 79 80 def Cron(self): 81 futures = [self._cache.GetFromFile('%s/%s_sidenav.json' % 82 (JSON_TEMPLATES, platform)) 83 for platform in ('apps', 'extensions')] 84 return Future(delegate=Gettable(lambda: [f.Get() for f in futures])) 85 86 def get(self, key): 87 sidenav = copy.deepcopy(self._cache.GetFromFile( 88 '%s/%s_sidenav.json' % (JSON_TEMPLATES, key)).Get()) 89 _AddSelected(sidenav, self._server_instance.base_path + self._request.path) 90 return sidenav 91