OLD | NEW |
1 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 # for details. All rights reserved. Use of this source code is governed by a | 2 # for details. All rights reserved. Use of this source code is governed by a |
3 # BSD-style license that can be found in the LICENSE file. | 3 # BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import cgi | 5 import cgi |
6 import json | 6 import json |
7 import logging | 7 import logging |
8 | 8 |
9 from google.appengine.api import memcache | 9 from google.appengine.api import memcache |
10 from google.appengine.api import users | 10 from google.appengine.api import users |
(...skipping 15 matching lines...) Expand all Loading... |
26 """ | 26 """ |
27 | 27 |
28 MAX_SIZE = 10 * 2**20 # 10MB | 28 MAX_SIZE = 10 * 2**20 # 10MB |
29 """The maximum package size, in bytes.""" | 29 """The maximum package size, in bytes.""" |
30 | 30 |
31 uploaders = db.ListProperty(users.User, validator=models.validate_not_empty) | 31 uploaders = db.ListProperty(users.User, validator=models.validate_not_empty) |
32 """The users who are allowed to upload new versions of the package. | 32 """The users who are allowed to upload new versions of the package. |
33 | 33 |
34 When this is set, invalidate_cache() must be called.""" | 34 When this is set, invalidate_cache() must be called.""" |
35 | 35 |
| 36 uploaderEmails = db.StringListProperty() |
| 37 """The user emails who are allowed to upload new versions of the package. |
| 38 |
| 39 When this is set, invalidate_cache() must be called.""" |
| 40 |
36 name = db.StringProperty(required=True) | 41 name = db.StringProperty(required=True) |
37 """The name of the package.""" | 42 """The name of the package.""" |
38 | 43 |
39 created = db.DateTimeProperty(auto_now_add=True) | 44 created = db.DateTimeProperty(auto_now_add=True) |
40 """When the package was created.""" | 45 """When the package was created.""" |
41 | 46 |
42 downloads = db.IntegerProperty(required=True, default=0) | 47 downloads = db.IntegerProperty(required=True, default=0) |
43 """The number of times any version of this package has been downloaded.""" | 48 """The number of times any version of this package has been downloaded.""" |
44 | 49 |
45 # This should only reference a PackageVersion, but cyclic imports aren't | 50 # This should only reference a PackageVersion, but cyclic imports aren't |
46 # allowed so we can't import PackageVersion here. | 51 # allowed so we can't import PackageVersion here. |
47 latest_version = db.ReferenceProperty() | 52 latest_version = db.ReferenceProperty() |
48 """The most recent non-prerelease version of this package. | 53 """The most recent non-prerelease version of this package. |
49 | 54 |
50 When this is set, invalidate_cache() must be called.""" | 55 When this is set, invalidate_cache() must be called.""" |
51 | 56 |
| 57 def temp_synchronize_uploaders_to_uploaderemails(self): |
| 58 """ Will synchronize self.uploaders -> self.uploaderEmails. """ |
| 59 if self.uploaders is None: |
| 60 self.uploaderEmails = None |
| 61 elif len(self.uploaders) == 0: |
| 62 self.uploaderEmails = self.uploaders |
| 63 else: |
| 64 self.uploaderEmails = [uploader.email() for uploader in self.uploaders] |
| 65 |
52 @property | 66 @property |
53 def description(self): | 67 def description(self): |
54 """The short description of the package.""" | 68 """The short description of the package.""" |
55 if self.latest_version is None: return None | 69 if self.latest_version is None: return None |
56 return self.latest_version.pubspec.get('description') | 70 return self.latest_version.pubspec.get('description') |
57 | 71 |
58 _MAX_DESCRIPTION_CHARS = 200 | 72 _MAX_DESCRIPTION_CHARS = 200 |
59 | 73 |
60 @property | 74 @property |
61 def ellipsized_description(self): | 75 def ellipsized_description(self): |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
107 cgi.escape(email), cgi.escape(author)) | 121 cgi.escape(email), cgi.escape(author)) |
108 | 122 |
109 return '<br/>'.join(map(author_html, self.latest_version.pubspec.authors
)) | 123 return '<br/>'.join(map(author_html, self.latest_version.pubspec.authors
)) |
110 | 124 |
111 @property | 125 @property |
112 def uploaders_title(self): | 126 def uploaders_title(self): |
113 """The title for the uploaders list of the package.""" | 127 """The title for the uploaders list of the package.""" |
114 return 'Uploader' if len(self.latest_version.pubspec.authors) == 1 \ | 128 return 'Uploader' if len(self.latest_version.pubspec.authors) == 1 \ |
115 else 'Uploaders' | 129 else 'Uploaders' |
116 | 130 |
| 131 # TODO(kustermann): When we have string emails, this needs to be changed |
| 132 # to read uploaderEmails instead of uploaders. |
117 @property | 133 @property |
118 def uploaders_html(self): | 134 def uploaders_html(self): |
119 """Inline HTML for the uploaders of this package.""" | 135 """Inline HTML for the uploaders of this package.""" |
120 return '<br/>'.join(cgi.escape(uploader.nickname()) | 136 return '<br/>'.join(cgi.escape(uploader.nickname()) |
121 for uploader in self.uploaders) | 137 for uploader in self.uploaders) |
122 | 138 |
123 @property | 139 @property |
124 def short_updated(self): | 140 def short_updated(self): |
125 """The short updated time of the package.""" | 141 """The short updated time of the package.""" |
126 return self.updated.strftime('%b %d, %Y') | 142 return self.updated.strftime('%b %d, %Y') |
(...skipping 25 matching lines...) Expand all Loading... |
152 """Determine whether a package with the given name exists.""" | 168 """Determine whether a package with the given name exists.""" |
153 return cls.get_by_key_name(name) is not None | 169 return cls.get_by_key_name(name) is not None |
154 | 170 |
155 def has_version(self, version): | 171 def has_version(self, version): |
156 """Determine whether this package has a given version uploaded.""" | 172 """Determine whether this package has a given version uploaded.""" |
157 from package_version import PackageVersion | 173 from package_version import PackageVersion |
158 version = PackageVersion.get_by_name_and_version( | 174 version = PackageVersion.get_by_name_and_version( |
159 self.name, str(version)) | 175 self.name, str(version)) |
160 return version is not None | 176 return version is not None |
161 | 177 |
| 178 # TODO(kustermann): When we have string emails, this needs to be changed |
| 179 # to read uploaderEmails instead of uploaders. |
162 def has_uploader(self, uploader): | 180 def has_uploader(self, uploader): |
163 """Determine whether the given user is an uploader for this package. | 181 """Determine whether the given user is an uploader for this package. |
164 | 182 |
165 This compares users via case-insensitive email comparison. | 183 This compares users via case-insensitive email comparison. |
166 | 184 |
167 Although admins have uploader privileges for all packages, this will not | 185 Although admins have uploader privileges for all packages, this will not |
168 return True for admins. | 186 return True for admins. |
169 """ | 187 """ |
170 return uploader.email().lower() in \ | 188 return uploader.email().lower() in \ |
171 [u.email().lower() for u in self.uploaders] | 189 [u.email().lower() for u in self.uploaders] |
172 | 190 |
173 @property | 191 @property |
174 def url(self): | 192 def url(self): |
175 """The API URL for this package.""" | 193 """The API URL for this package.""" |
176 return models.url( | 194 return models.url( |
177 controller='api.packages', action='show', id=self.name) | 195 controller='api.packages', action='show', id=self.name) |
178 | 196 |
| 197 # TODO(kustermann): When we have string emails, this needs to be changed |
| 198 # to read uploaderEmails instead of uploaders. |
179 def as_dict(self, full=False): | 199 def as_dict(self, full=False): |
180 """Returns the dictionary representation of this package. | 200 """Returns the dictionary representation of this package. |
181 | 201 |
182 This is used to represent the package in API responses. Normally this | 202 This is used to represent the package in API responses. Normally this |
183 just includes URLs and some information about the latest version, but if | 203 just includes URLs and some information about the latest version, but if |
184 full is True, it will include all available information about the | 204 full is True, it will include all available information about the |
185 package. | 205 package. |
186 """ | 206 """ |
187 | 207 |
188 value = { | 208 value = { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
224 description of the package changes. This isn't often since most package | 244 description of the package changes. This isn't often since most package |
225 data is immutable, but when the uploader list changes or new versions | 245 data is immutable, but when the uploader list changes or new versions |
226 of the package are uploaded, the data will change. | 246 of the package are uploaded, the data will change. |
227 """ | 247 """ |
228 memcache.delete(self._package_json_cache_key) | 248 memcache.delete(self._package_json_cache_key) |
229 | 249 |
230 @property | 250 @property |
231 def _package_json_cache_key(self): | 251 def _package_json_cache_key(self): |
232 """The memcache key for the cached JSON for this package.""" | 252 """The memcache key for the cached JSON for this package.""" |
233 return 'package_json_' + self.name | 253 return 'package_json_' + self.name |
OLD | NEW |