| 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 |