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