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