template_data_source.py revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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
7
8from branch_utility import BranchUtility
9import compiled_file_system as compiled_fs
10from docs_server_utils import FormatKey
11from file_system import FileNotFoundError
12from third_party.handlebar import Handlebar
13import url_constants
14
15EXTENSIONS_URL = '/chrome/extensions'
16
17def _MakeChannelDict(channel_name):
18  channel_dict = {
19    'channels': [{'name': name} for name in BranchUtility.GetAllChannelNames()],
20    'current': channel_name
21  }
22  for channel in channel_dict['channels']:
23    if channel['name'] == channel_name:
24      channel['isCurrent'] = True
25  return channel_dict
26
27class TemplateDataSource(object):
28  """Renders Handlebar templates, providing them with the context in which to
29  render.
30
31  Also acts as a data source itself, providing partial Handlebar templates to
32  those it renders.
33
34  Each instance of TemplateDataSource is bound to a Request so that it can
35  render templates with request-specific data (such as Accept-Language); use
36  a Factory to cheaply construct these.
37  """
38
39  class Factory(object):
40    """A factory to create lightweight TemplateDataSource instances bound to
41    individual Requests.
42    """
43    def __init__(self,
44                 channel_name,
45                 api_data_source_factory,
46                 api_list_data_source_factory,
47                 intro_data_source_factory,
48                 samples_data_source_factory,
49                 sidenav_data_source_factory,
50                 compiled_fs_factory,
51                 ref_resolver_factory,
52                 public_template_path,
53                 private_template_path,
54                 static_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 = static_path
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      return None
154