template_data_source.py revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 logging 6import os 7import traceback 8 9from branch_utility import BranchUtility 10import compiled_file_system as compiled_fs 11from docs_server_utils import FormatKey 12from file_system import FileNotFoundError 13from third_party.handlebar import Handlebar 14import url_constants 15 16EXTENSIONS_URL = '/chrome/extensions' 17 18def _MakeChannelDict(channel_name): 19 channel_dict = { 20 'channels': [{'name': name} for name in BranchUtility.GetAllChannelNames()], 21 'current': channel_name 22 } 23 for channel in channel_dict['channels']: 24 if channel['name'] == channel_name: 25 channel['isCurrent'] = True 26 return channel_dict 27 28class TemplateDataSource(object): 29 """Renders Handlebar templates, providing them with the context in which to 30 render. 31 32 Also acts as a data source itself, providing partial Handlebar templates to 33 those it renders. 34 35 Each instance of TemplateDataSource is bound to a Request so that it can 36 render templates with request-specific data (such as Accept-Language); use 37 a Factory to cheaply construct these. 38 """ 39 40 class Factory(object): 41 """A factory to create lightweight TemplateDataSource instances bound to 42 individual Requests. 43 """ 44 def __init__(self, 45 channel_name, 46 api_data_source_factory, 47 api_list_data_source_factory, 48 intro_data_source_factory, 49 samples_data_source_factory, 50 sidenav_data_source_factory, 51 compiled_fs_factory, 52 ref_resolver_factory, 53 public_template_path, 54 private_template_path): 55 self._branch_info = _MakeChannelDict(channel_name) 56 self._api_data_source_factory = api_data_source_factory 57 self._api_list_data_source_factory = api_list_data_source_factory 58 self._intro_data_source_factory = intro_data_source_factory 59 self._samples_data_source_factory = samples_data_source_factory 60 self._sidenav_data_source_factory = sidenav_data_source_factory 61 self._cache = compiled_fs_factory.Create(self._CreateTemplate, 62 TemplateDataSource) 63 self._ref_resolver = ref_resolver_factory.Create() 64 self._public_template_path = public_template_path 65 self._private_template_path = private_template_path 66 self._static_resources = '/%s/static' % channel_name 67 68 def _CreateTemplate(self, template_name, text): 69 return Handlebar(self._ref_resolver.ResolveAllLinks(text)) 70 71 def Create(self, request, path): 72 """Returns a new TemplateDataSource bound to |request|. 73 """ 74 return TemplateDataSource( 75 self._branch_info, 76 self._api_data_source_factory.Create(request), 77 self._api_list_data_source_factory.Create(), 78 self._intro_data_source_factory.Create(), 79 self._samples_data_source_factory.Create(request), 80 self._sidenav_data_source_factory.Create(path), 81 self._cache, 82 self._public_template_path, 83 self._private_template_path, 84 self._static_resources) 85 86 def __init__(self, 87 branch_info, 88 api_data_source, 89 api_list_data_source, 90 intro_data_source, 91 samples_data_source, 92 sidenav_data_source, 93 cache, 94 public_template_path, 95 private_template_path, 96 static_resources): 97 self._branch_info = branch_info 98 self._api_list_data_source = api_list_data_source 99 self._intro_data_source = intro_data_source 100 self._samples_data_source = samples_data_source 101 self._api_data_source = api_data_source 102 self._sidenav_data_source = sidenav_data_source 103 self._cache = cache 104 self._public_template_path = public_template_path 105 self._private_template_path = private_template_path 106 self._static_resources = static_resources 107 108 def Render(self, template_name): 109 """This method will render a template named |template_name|, fetching all 110 the partial templates needed from |self._cache|. Partials are retrieved 111 from the TemplateDataSource with the |get| method. 112 """ 113 template = self.GetTemplate(self._public_template_path, template_name) 114 if not template: 115 return None 116 # TODO error handling 117 render_data = template.render({ 118 'api_list': self._api_list_data_source, 119 'apis': self._api_data_source, 120 'branchInfo': self._branch_info, 121 'intros': self._intro_data_source, 122 'sidenavs': self._sidenav_data_source, 123 'partials': self, 124 'samples': self._samples_data_source, 125 'static': self._static_resources, 126 'app': 'app', 127 'extension': 'extension', 128 'apps_title': 'Apps', 129 'extensions_title': 'Extensions', 130 'apps_samples_url': url_constants.GITHUB_BASE, 131 # TODO(kalman): this is wrong, it's always getting from trunk, but meh 132 # it hardly ever shows up (only in the "cannot fetch samples" message). 133 # In fact I don't even know if it can show up anymore due the samples data 134 # being persisent. In any case, when the channel distinctions are gone 135 # this can go away, so, double meh. 136 'extensions_samples_url': url_constants.EXTENSIONS_SAMPLES, 137 'true': True, 138 'false': False 139 }) 140 if render_data.errors: 141 logging.error('Handlebar error(s) rendering %s:\n%s' % 142 (template_name, ' \n'.join(render_data.errors))) 143 return render_data.text 144 145 def get(self, key): 146 return self.GetTemplate(self._private_template_path, key) 147 148 def GetTemplate(self, base_path, template_name): 149 try: 150 return self._cache.GetFromFile( 151 '/'.join((base_path, FormatKey(template_name)))) 152 except FileNotFoundError as e: 153 logging.warning(traceback.format_exc()) 154 return None 155