| Index: chrome/common/extensions/docs/server2/samples_data_source.py
|
| diff --git a/chrome/common/extensions/docs/server2/samples_data_source.py b/chrome/common/extensions/docs/server2/samples_data_source.py
|
| index a692e979ca1c7bc5d4b80911603e950d6d5bfb2a..eabcb28979a81e46a9d8cacfee30042117b31a3e 100644
|
| --- a/chrome/common/extensions/docs/server2/samples_data_source.py
|
| +++ b/chrome/common/extensions/docs/server2/samples_data_source.py
|
| @@ -2,252 +2,77 @@
|
| # Use of this source code is governed by a BSD-style license that can be
|
| # found in the LICENSE file.
|
|
|
| -import json
|
| import logging
|
| -import posixpath
|
| -import re
|
| import traceback
|
|
|
| +from data_source import DataSource
|
| from extensions_paths import EXAMPLES
|
| -import third_party.json_schema_compiler.json_comment_eater as json_comment_eater
|
| -import url_constants
|
| +from future import All, Future
|
| +from platform_util import GetPlatforms
|
|
|
|
|
| -_DEFAULT_ICON_PATH = 'images/sample-default-icon.png'
|
| +def _GetSampleId(sample_name):
|
| + return sample_name.lower().replace(' ', '-')
|
|
|
|
|
| -class SamplesDataSource(object):
|
| - '''Constructs a list of samples and their respective files and api calls.
|
| - '''
|
| - class Factory(object):
|
| - '''A factory to create SamplesDataSource instances bound to individual
|
| - Requests.
|
| - '''
|
| - def __init__(self,
|
| - host_file_system,
|
| - app_samples_file_system,
|
| - compiled_fs_factory,
|
| - platform_bundle,
|
| - base_path):
|
| - self._host_file_system = host_file_system
|
| - self._app_samples_file_system = app_samples_file_system
|
| - self._platform_bundle = platform_bundle
|
| - self._base_path = base_path
|
| - self._extensions_cache = compiled_fs_factory.Create(
|
| - host_file_system,
|
| - self._MakeSamplesList,
|
| - SamplesDataSource,
|
| - category='extensions')
|
| - self._extensions_text_cache = compiled_fs_factory.ForUnicode(
|
| - host_file_system)
|
| - self._apps_cache = compiled_fs_factory.Create(
|
| - app_samples_file_system,
|
| - lambda *args: self._MakeSamplesList(*args, is_apps=True),
|
| - SamplesDataSource,
|
| - category='apps')
|
| - self._apps_text_cache = compiled_fs_factory.ForUnicode(
|
| - app_samples_file_system)
|
| +def GetAcceptedLanguages(request):
|
| + if request is None:
|
| + return []
|
| + accept_language = request.headers.get('Accept-Language', None)
|
| + if accept_language is None:
|
| + return []
|
| + return [lang_with_q.split(';')[0].strip()
|
| + for lang_with_q in accept_language.split(',')]
|
|
|
| - def Create(self, request):
|
| - '''Returns a new SamplesDataSource bound to |request|.
|
| - '''
|
| - return SamplesDataSource(self._extensions_cache,
|
| - self._apps_cache,
|
| - self._base_path,
|
| - request)
|
|
|
| - def _GetAPIItems(self, js_file):
|
| - chrome_pattern = r'chrome[\w.]+'
|
| - # Add API calls that appear normally, like "chrome.runtime.connect".
|
| - calls = set(re.findall(chrome_pattern, js_file))
|
| - # Add API calls that have been assigned into variables, like
|
| - # "var storageArea = chrome.storage.sync; storageArea.get", which should
|
| - # be expanded like "chrome.storage.sync.get".
|
| - for match in re.finditer(r'var\s+(\w+)\s*=\s*(%s);' % chrome_pattern,
|
| - js_file):
|
| - var_name, api_prefix = match.groups()
|
| - for var_match in re.finditer(r'\b%s\.([\w.]+)\b' % re.escape(var_name),
|
| - js_file):
|
| - api_suffix, = var_match.groups()
|
| - calls.add('%s.%s' % (api_prefix, api_suffix))
|
| - return calls
|
| -
|
| - def _GetDataFromManifest(self, path, text_cache, file_system):
|
| - manifest = text_cache.GetFromFile(path + '/manifest.json').Get()
|
| +def CreateSamplesView(samples_list, request):
|
| + return_list = []
|
| + for dict_ in samples_list:
|
| + name = dict_['name']
|
| + description = dict_['description']
|
| + if description is None:
|
| + description = ''
|
| + if name.startswith('__MSG_') or description.startswith('__MSG_'):
|
| try:
|
| - manifest_json = json.loads(json_comment_eater.Nom(manifest))
|
| - except ValueError as e:
|
| - logging.error('Error parsing manifest.json for %s: %s' % (path, e))
|
| - return None
|
| - l10n_data = {
|
| - 'name': manifest_json.get('name', ''),
|
| - 'description': manifest_json.get('description', None),
|
| - 'icon': manifest_json.get('icons', {}).get('128', None),
|
| - 'default_locale': manifest_json.get('default_locale', None),
|
| - 'locales': {}
|
| - }
|
| - if not l10n_data['default_locale']:
|
| - return l10n_data
|
| - locales_path = path + '/_locales/'
|
| - locales_dir = file_system.ReadSingle(locales_path).Get()
|
| - if locales_dir:
|
| - def load_locale_json(path):
|
| - return (path, json.loads(text_cache.GetFromFile(path).Get()))
|
| -
|
| - try:
|
| - locales_json = [load_locale_json(locales_path + f + 'messages.json')
|
| - for f in locales_dir]
|
| - except ValueError as e:
|
| - logging.error('Error parsing locales files for %s: %s' % (path, e))
|
| - else:
|
| - for path, json_ in locales_json:
|
| - l10n_data['locales'][path[len(locales_path):].split('/')[0]] = json_
|
| - return l10n_data
|
| -
|
| - def _MakeSamplesList(self, base_path, files, is_apps=False):
|
| - file_system = (self._app_samples_file_system if is_apps else
|
| - self._host_file_system)
|
| - text_cache = (self._apps_text_cache if is_apps else
|
| - self._extensions_text_cache)
|
| - samples_list = []
|
| - for filename in sorted(files):
|
| - if filename.rsplit('/')[-1] != 'manifest.json':
|
| - continue
|
| -
|
| - # This is a little hacky, but it makes a sample page.
|
| - sample_path = filename.rsplit('/', 1)[-2]
|
| - sample_files = [path for path in files
|
| - if path.startswith(sample_path + '/')]
|
| - js_files = [path for path in sample_files if path.endswith('.js')]
|
| - js_contents = [text_cache.GetFromFile(
|
| - posixpath.join(base_path, js_file)).Get()
|
| - for js_file in js_files]
|
| - api_items = set()
|
| - for js in js_contents:
|
| - api_items.update(self._GetAPIItems(js))
|
| -
|
| - api_calls = []
|
| - for item in sorted(api_items):
|
| - if len(item.split('.')) < 3:
|
| - continue
|
| - if item.endswith('.removeListener') or item.endswith('.hasListener'):
|
| - continue
|
| - if item.endswith('.addListener'):
|
| - item = item[:-len('.addListener')]
|
| - if item.startswith('chrome.'):
|
| - item = item[len('chrome.'):]
|
| - ref_data = self._platform_bundle.GetReferenceResolver(
|
| - 'apps' if is_apps else 'extensions').GetLink(item)
|
| - # TODO(kalman): What about references like chrome.storage.sync.get?
|
| - # That should link to either chrome.storage.sync or
|
| - # chrome.storage.StorageArea.get (or probably both).
|
| - # TODO(kalman): Filter out API-only references? This can happen when
|
| - # the API namespace is assigned to a variable, but it's very hard to
|
| - # to disambiguate.
|
| - if ref_data is None:
|
| - continue
|
| - api_calls.append({
|
| - 'name': ref_data['text'],
|
| - 'link': ref_data['href']
|
| - })
|
| -
|
| - if is_apps:
|
| - url = url_constants.GITHUB_BASE + '/' + sample_path
|
| - icon_base = url_constants.RAW_GITHUB_BASE + '/' + sample_path
|
| - download_url = url
|
| - else:
|
| - extension_sample_path = posixpath.join('examples', sample_path)
|
| - url = extension_sample_path
|
| - icon_base = extension_sample_path
|
| - download_url = extension_sample_path + '.zip'
|
| -
|
| - manifest_data = self._GetDataFromManifest(
|
| - posixpath.join(base_path, sample_path),
|
| - text_cache,
|
| - file_system)
|
| - if manifest_data['icon'] is None:
|
| - icon_path = posixpath.join(
|
| - self._base_path, 'static', _DEFAULT_ICON_PATH)
|
| - else:
|
| - icon_path = '%s/%s' % (icon_base, manifest_data['icon'])
|
| - manifest_data.update({
|
| - 'icon': icon_path,
|
| - 'download_url': download_url,
|
| - 'url': url,
|
| - 'files': [f.replace(sample_path + '/', '') for f in sample_files],
|
| - 'api_calls': api_calls
|
| - })
|
| - samples_list.append(manifest_data)
|
| + # Copy the sample dict so we don't change the dict in the cache.
|
| + sample_data = dict_.copy()
|
| + name_key = name[len('__MSG_'):-len('__')]
|
| + description_key = description[len('__MSG_'):-len('__')]
|
| + locale = sample_data['default_locale']
|
| + for lang in GetAcceptedLanguages(request):
|
| + if lang in sample_data['locales']:
|
| + locale = lang
|
| + break
|
| + locale_data = sample_data['locales'][locale]
|
| + sample_data['name'] = locale_data[name_key]['message']
|
| + sample_data['description'] = locale_data[description_key]['message']
|
| + sample_data['id'] = _GetSampleId(sample_data['name'])
|
| + except Exception:
|
| + logging.error(traceback.format_exc())
|
| + # Revert the sample to the original dict.
|
| + sample_data = dict_
|
| + return_list.append(sample_data)
|
| + else:
|
| + dict_['id'] = _GetSampleId(name)
|
| + return_list.append(dict_)
|
| + return return_list
|
|
|
| - return samples_list
|
|
|
| - def __init__(self,
|
| - extensions_cache,
|
| - apps_cache,
|
| - base_path,
|
| - request):
|
| - self._extensions_cache = extensions_cache
|
| - self._apps_cache = apps_cache
|
| - self._base_path = base_path
|
| +class SamplesDataSource(DataSource):
|
| + '''Constructs a list of samples and their respective files and api calls.
|
| + '''
|
| + def __init__(self, server_instance, request):
|
| + self._platform_bundle = server_instance.platform_bundle
|
| self._request = request
|
|
|
| - def _GetSampleId(self, sample_name):
|
| - return sample_name.lower().replace(' ', '-')
|
| + def _GetImpl(self, platform):
|
| + cache = self._platform_bundle.GetSamplesModel(platform).GetCache()
|
| + create_view = lambda samp_list: CreateSamplesView(samp_list, self._request)
|
| + return cache.GetFromFileListing('' if platform == 'apps'
|
| + else EXAMPLES).Then(create_view)
|
|
|
| - def _GetAcceptedLanguages(self):
|
| - accept_language = self._request.headers.get('Accept-Language', None)
|
| - if accept_language is None:
|
| - return []
|
| - return [lang_with_q.split(';')[0].strip()
|
| - for lang_with_q in accept_language.split(',')]
|
| -
|
| - def FilterSamples(self, key, api_name):
|
| - '''Fetches and filters the list of samples specified by |key|, returning
|
| - only the samples that use the API |api_name|. |key| is either 'apps' or
|
| - 'extensions'.
|
| - '''
|
| - return [sample for sample in self.get(key) if any(
|
| - call['name'].startswith(api_name + '.')
|
| - for call in sample['api_calls'])]
|
| -
|
| - def _CreateSamplesDict(self, key):
|
| - if key == 'apps':
|
| - samples_list = self._apps_cache.GetFromFileListing('').Get()
|
| - else:
|
| - samples_list = self._extensions_cache.GetFromFileListing(EXAMPLES).Get()
|
| - return_list = []
|
| - for dict_ in samples_list:
|
| - name = dict_['name']
|
| - description = dict_['description']
|
| - if description is None:
|
| - description = ''
|
| - if name.startswith('__MSG_') or description.startswith('__MSG_'):
|
| - try:
|
| - # Copy the sample dict so we don't change the dict in the cache.
|
| - sample_data = dict_.copy()
|
| - name_key = name[len('__MSG_'):-len('__')]
|
| - description_key = description[len('__MSG_'):-len('__')]
|
| - locale = sample_data['default_locale']
|
| - for lang in self._GetAcceptedLanguages():
|
| - if lang in sample_data['locales']:
|
| - locale = lang
|
| - break
|
| - locale_data = sample_data['locales'][locale]
|
| - sample_data['name'] = locale_data[name_key]['message']
|
| - sample_data['description'] = locale_data[description_key]['message']
|
| - sample_data['id'] = self._GetSampleId(sample_data['name'])
|
| - except Exception as e:
|
| - logging.error(traceback.format_exc())
|
| - # Revert the sample to the original dict.
|
| - sample_data = dict_
|
| - return_list.append(sample_data)
|
| - else:
|
| - dict_['id'] = self._GetSampleId(name)
|
| - return_list.append(dict_)
|
| - return return_list
|
| + def get(self, platform):
|
| + return self._GetImpl(platform).Get()
|
|
|
| - def get(self, key):
|
| - return {
|
| - 'apps': lambda: self._CreateSamplesDict('apps'),
|
| - 'extensions': lambda: self._CreateSamplesDict('extensions')
|
| - }.get(key, lambda: {})()
|
| + def Cron(self):
|
| + return All([self._GetImpl(platform) for platform in GetPlatforms()])
|
|
|