Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(205)

Side by Side Diff: chrome/common/extensions/docs/server2/content_provider.py

Issue 148293018: Docserver: Make the .html extension unnecessary for content pages, for example, (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: yoz Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import mimetypes 5 import mimetypes
6 import posixpath 6 import posixpath
7 7
8 from compiled_file_system import SingleFile 8 from compiled_file_system import SingleFile
9 from directory_zipper import DirectoryZipper 9 from directory_zipper import DirectoryZipper
10 from docs_server_utils import ToUnicode 10 from docs_server_utils import ToUnicode
11 from file_system import FileNotFoundError 11 from file_system import FileNotFoundError
12 from future import Gettable, Future 12 from future import Gettable, Future
13 from path_canonicalizer import PathCanonicalizer 13 from path_canonicalizer import PathCanonicalizer
14 from path_util import AssertIsValid, ToDirectory 14 from path_util import AssertIsValid, ToDirectory
15 from special_paths import SITE_VERIFICATION_FILE
15 from third_party.handlebar import Handlebar 16 from third_party.handlebar import Handlebar
16 from third_party.markdown import markdown 17 from third_party.markdown import markdown
17 18
18 19
19 _MIMETYPE_OVERRIDES = { 20 _MIMETYPE_OVERRIDES = {
20 # SVG is not supported by mimetypes.guess_type on AppEngine. 21 # SVG is not supported by mimetypes.guess_type on AppEngine.
21 '.svg': 'image/svg+xml', 22 '.svg': 'image/svg+xml',
22 } 23 }
23 24
24 25
25 class ContentAndType(object): 26 class ContentAndType(object):
26 '''Return value from ContentProvider.GetContentAndType. 27 '''Return value from ContentProvider.GetContentAndType.
27 ''' 28 '''
28 29
29 def __init__(self, content, content_type): 30 def __init__(self, content, content_type):
30 self.content = content 31 self.content = content
31 self.content_type = content_type 32 self.content_type = content_type
32 33
33 34
34 class ContentProvider(object): 35 class ContentProvider(object):
35 '''Returns file contents correctly typed for their content-types (in the HTTP 36 '''Returns file contents correctly typed for their content-types (in the HTTP
36 sense). Content-type is determined from Python's mimetype library which 37 sense). Content-type is determined from Python's mimetype library which
37 guesses based on the file extension. 38 guesses based on the file extension.
38 39
39 Typically the file contents will be either str (for binary content) or 40 Typically the file contents will be either str (for binary content) or
40 unicode (for text content). However, HTML files *may* be returned as 41 unicode (for text content). However, HTML files *may* be returned as
41 Handlebar templates (if supports_templates is True on construction), in which 42 Handlebar templates (if |supports_templates| is True on construction), in
42 case the caller will presumably want to Render them. 43 which case the caller will presumably want to Render them.
44
45 Zip file are automatically created and returned for .zip file extensions if
46 |supports_zip| is True.
47
48 |default_extensions| is a list of file extensions which are queried when no
49 file extension is given to GetCanonicalPath/GetContentAndType. Typically
50 this will include .html.
43 ''' 51 '''
44 52
45 def __init__(self, 53 def __init__(self,
46 name, 54 name,
47 compiled_fs_factory, 55 compiled_fs_factory,
48 file_system, 56 file_system,
57 object_store_creator,
58 default_extensions=(),
49 supports_templates=False, 59 supports_templates=False,
50 supports_zip=False): 60 supports_zip=False):
51 # Public. 61 # Public.
52 self.name = name 62 self.name = name
53 self.file_system = file_system 63 self.file_system = file_system
54 # Private. 64 # Private.
55 self._content_cache = compiled_fs_factory.Create(file_system, 65 self._content_cache = compiled_fs_factory.Create(file_system,
56 self._CompileContent, 66 self._CompileContent,
57 ContentProvider) 67 ContentProvider)
58 self._path_canonicalizer = PathCanonicalizer(compiled_fs_factory, 68 self._path_canonicalizer = PathCanonicalizer(file_system,
59 file_system) 69 object_store_creator,
70 default_extensions)
71 self._default_extensions = default_extensions
60 self._supports_templates = supports_templates 72 self._supports_templates = supports_templates
61 if supports_zip: 73 if supports_zip:
62 self._directory_zipper = DirectoryZipper(compiled_fs_factory, file_system) 74 self._directory_zipper = DirectoryZipper(compiled_fs_factory, file_system)
63 else: 75 else:
64 self._directory_zipper = None 76 self._directory_zipper = None
65 77
66 @SingleFile 78 @SingleFile
67 def _CompileContent(self, path, text): 79 def _CompileContent(self, path, text):
68 assert text is not None, path 80 assert text is not None, path
69 _, ext = posixpath.splitext(path) 81 _, ext = posixpath.splitext(path)
(...skipping 13 matching lines...) Expand all
83 content = ToUnicode(text) 95 content = ToUnicode(text)
84 if self._supports_templates: 96 if self._supports_templates:
85 content = Handlebar(content, name=path) 97 content = Handlebar(content, name=path)
86 elif (mimetype.startswith('text/') or 98 elif (mimetype.startswith('text/') or
87 mimetype in ('application/javascript', 'application/json')): 99 mimetype in ('application/javascript', 'application/json')):
88 content = ToUnicode(text) 100 content = ToUnicode(text)
89 else: 101 else:
90 content = text 102 content = text
91 return ContentAndType(content, mimetype) 103 return ContentAndType(content, mimetype)
92 104
93 def _MaybeMarkdown(self, path):
94 base, ext = posixpath.splitext(path)
95 if ext != '.html':
96 return path
97 if self.file_system.Exists(path).Get():
98 return path
99 as_md = base + '.md'
100 if self.file_system.Exists(as_md).Get():
101 return as_md
102 return path
103
104 def GetCanonicalPath(self, path): 105 def GetCanonicalPath(self, path):
105 '''Gets the canonical location of |path|. This class is tolerant of 106 '''Gets the canonical location of |path|. This class is tolerant of
106 spelling errors and missing files that are in other directories, and this 107 spelling errors and missing files that are in other directories, and this
107 returns the correct/canonical path for those. 108 returns the correct/canonical path for those.
108 109
109 For example, the canonical path of "browseraction" is probably 110 For example, the canonical path of "browseraction" is probably
110 "extensions/browserAction.html". 111 "extensions/browserAction.html".
112
113 Note that the canonical path is relative to this content provider i.e.
114 given relative to |path|. It does not add the "serveFrom" prefix which
115 would have been pulled out in ContentProviders, callers must do that
116 themselves.
111 ''' 117 '''
112 AssertIsValid(path) 118 AssertIsValid(path)
113 _, ext = posixpath.splitext(path) 119 base, ext = posixpath.splitext(path)
114 if not ext or ext == '.html': 120 if self._directory_zipper and ext == '.zip':
115 return self._path_canonicalizer.Canonicalize(path) 121 # The canonical location of zip files is the canonical location of the
116 return path 122 # directory to zip + '.zip'.
123 return self._path_canonicalizer.Canonicalize(base + '/').rstrip('/') + ext
124 return self._path_canonicalizer.Canonicalize(path)
117 125
118 def GetContentAndType(self, path): 126 def GetContentAndType(self, path):
119 '''Returns the ContentAndType of the file at |path|. 127 '''Returns the ContentAndType of the file at |path|.
120 ''' 128 '''
121 AssertIsValid(path) 129 AssertIsValid(path)
122 base, ext = posixpath.splitext(path) 130 base, ext = posixpath.splitext(path)
123 131
124 # Check for a zip file first, if zip is enabled. 132 # Check for a zip file first, if zip is enabled.
125 if self._directory_zipper and ext == '.zip': 133 if self._directory_zipper and ext == '.zip':
126 zip_future = self._directory_zipper.Zip(ToDirectory(base)) 134 zip_future = self._directory_zipper.Zip(ToDirectory(base))
127 return Future(delegate=Gettable( 135 return Future(delegate=Gettable(
128 lambda: ContentAndType(zip_future.Get(), 'application/zip'))) 136 lambda: ContentAndType(zip_future.Get(), 'application/zip')))
129 137
130 return self._content_cache.GetFromFile(self._MaybeMarkdown(path)) 138 # If there is no file extension, look for a file with one of the default
139 # extensions.
140 #
141 # Note that it would make sense to guard this on Exists(path), since a file
142 # without an extension may actually exist, but it's such an uncommon case
143 # it hardly seems worth the potential performance hit.
144 if not ext:
145 for default_ext in self._default_extensions:
146 if self.file_system.Exists(path + default_ext).Get():
147 path += default_ext
148 break
149
150 return self._content_cache.GetFromFile(path)
131 151
132 def Cron(self): 152 def Cron(self):
133 # Running Refresh() on the file system is enough to pull GitHub content,
134 # which is all we need for now while the full render-every-page cron step
135 # is in effect.
136 futures = [] 153 futures = []
137 for root, _, files in self.file_system.Walk(''): 154 for root, _, files in self.file_system.Walk(''):
138 futures += [self.GetContentAndType(posixpath.join(root, filename)) 155 for f in files:
139 for filename in files] 156 futures.append(self.GetContentAndType(posixpath.join(root, f)))
157 # Also cache the extension-less version of the file if needed.
158 base, ext = posixpath.splitext(f)
159 if f != SITE_VERIFICATION_FILE and ext in self._default_extensions:
160 futures.append(self.GetContentAndType(posixpath.join(root, base)))
161 # TODO(kalman): Cache .zip files for each directory (if supported).
140 return Future(delegate=Gettable(lambda: [f.Get() for f in futures])) 162 return Future(delegate=Gettable(lambda: [f.Get() for f in futures]))
141 163
142 def __repr__(self): 164 def __repr__(self):
143 return 'ContentProvider of <%s>' % repr(self.file_system) 165 return 'ContentProvider of <%s>' % repr(self.file_system)
OLDNEW
« no previous file with comments | « chrome/common/extensions/docs/server2/app.yaml ('k') | chrome/common/extensions/docs/server2/content_provider_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698