|
15 | 15 | from readthedocs.search import tasks
|
16 | 16 | from readthedocs.search.faceted_search import PageSearch
|
17 | 17 |
|
18 |
| -from .serializers import PageSearchSerializer |
| 18 | +from .serializers import PageSearchSerializer, VersionData |
19 | 19 |
|
20 | 20 | log = logging.getLogger(__name__)
|
21 | 21 |
|
@@ -185,67 +185,82 @@ def _validate_query_params(self):
|
185 | 185 |
|
186 | 186 | def _get_all_projects_data(self):
|
187 | 187 | """
|
188 |
| - Return a dict containing the project slug and its version URL and version's doctype. |
189 |
| -
|
190 |
| - The dictionary contains the project and its subprojects. Each project's |
191 |
| - slug is used as a key and a tuple with the documentation URL and doctype |
192 |
| - from the version. Example: |
193 |
| -
|
194 |
| - { |
195 |
| - "requests": ( |
196 |
| - "https://requests.readthedocs.io/en/latest/", |
197 |
| - "sphinx", |
198 |
| - ), |
199 |
| - "requests-oauth": ( |
200 |
| - "https://requests-oauth.readthedocs.io/en/latest/", |
201 |
| - "sphinx_htmldir", |
202 |
| - ), |
203 |
| - } |
| 188 | + Return a dictionary of the project itself and all its subprojects. |
204 | 189 |
|
205 |
| - :rtype: dict |
206 |
| - """ |
207 |
| - all_projects = self._get_all_projects() |
208 |
| - version_slug = self._get_version().slug |
209 |
| - project_urls = {} |
210 |
| - for project in all_projects: |
211 |
| - project_urls[project.slug] = project.get_docs_url(version_slug=version_slug) |
212 |
| - |
213 |
| - versions_doctype = ( |
214 |
| - Version.objects |
215 |
| - .filter(project__slug__in=project_urls.keys(), slug=version_slug) |
216 |
| - .values_list('project__slug', 'documentation_type') |
217 |
| - ) |
| 190 | + Example: |
218 | 191 |
|
219 |
| - projects_data = { |
220 |
| - project_slug: (project_urls[project_slug], doctype) |
221 |
| - for project_slug, doctype in versions_doctype |
222 |
| - } |
223 |
| - return projects_data |
| 192 | + .. code:: |
224 | 193 |
|
225 |
| - def _get_all_projects(self): |
226 |
| - """ |
227 |
| - Returns a list of the project itself and all its subprojects the user has permissions over. |
| 194 | + { |
| 195 | + "requests": VersionData( |
| 196 | + "latest", |
| 197 | + "sphinx", |
| 198 | + "https://requests.readthedocs.io/en/latest/", |
| 199 | + ), |
| 200 | + "requests-oauth": VersionData( |
| 201 | + "latest", |
| 202 | + "sphinx_htmldir", |
| 203 | + "https://requests-oauth.readthedocs.io/en/latest/", |
| 204 | + ), |
| 205 | + } |
228 | 206 |
|
229 |
| - :rtype: list |
| 207 | + .. note:: The response is cached into the instance. |
| 208 | +
|
| 209 | + :rtype: A dictionary of project slugs mapped to a `VersionData` object. |
230 | 210 | """
|
| 211 | + cache_key = '__cached_projects_data' |
| 212 | + projects_data = getattr(self, cache_key, None) |
| 213 | + if projects_data is not None: |
| 214 | + return projects_data |
| 215 | + |
231 | 216 | main_version = self._get_version()
|
232 | 217 | main_project = self._get_project()
|
233 | 218 |
|
234 |
| - all_projects = [main_project] |
| 219 | + projects_data = { |
| 220 | + main_project.slug: VersionData( |
| 221 | + slug=main_version.slug, |
| 222 | + doctype=main_version.documentation_type, |
| 223 | + docs_url=main_project.get_docs_url(version_slug=main_version.slug), |
| 224 | + ) |
| 225 | + } |
235 | 226 |
|
236 | 227 | subprojects = Project.objects.filter(
|
237 | 228 | superprojects__parent_id=main_project.id,
|
238 | 229 | )
|
239 | 230 | for project in subprojects:
|
240 |
| - version = ( |
241 |
| - Version.internal |
242 |
| - .public(user=self.request.user, project=project, include_hidden=False) |
243 |
| - .filter(slug=main_version.slug) |
244 |
| - .first() |
| 231 | + version = self._get_subproject_version( |
| 232 | + version_slug=main_version.slug, |
| 233 | + subproject=project, |
245 | 234 | )
|
246 |
| - if version: |
247 |
| - all_projects.append(version.project) |
248 |
| - return all_projects |
| 235 | + if version and self._has_permission(self.request.user, version): |
| 236 | + url = project.get_docs_url(version_slug=version.slug) |
| 237 | + projects_data[project.slug] = VersionData( |
| 238 | + slug=version.slug, |
| 239 | + doctype=version.documentation_type, |
| 240 | + docs_url=url, |
| 241 | + ) |
| 242 | + |
| 243 | + setattr(self, cache_key, projects_data) |
| 244 | + return projects_data |
| 245 | + |
| 246 | + def _get_subproject_version(self, version_slug, subproject): |
| 247 | + """Get a version from the subproject.""" |
| 248 | + return ( |
| 249 | + Version.internal |
| 250 | + .public(user=self.request.user, project=subproject, include_hidden=False) |
| 251 | + .filter(slug=version_slug) |
| 252 | + .first() |
| 253 | + ) |
| 254 | + |
| 255 | + def _has_permission(self, user, version): |
| 256 | + """ |
| 257 | + Check if `user` is authorized to access `version`. |
| 258 | +
|
| 259 | + The queryset from `_get_subproject_version` already filters public |
| 260 | + projects. This is mainly to be overriden in .com to make use of |
| 261 | + the auth backends in the proxied API. |
| 262 | + """ |
| 263 | + return True |
249 | 264 |
|
250 | 265 | def _record_query(self, response):
|
251 | 266 | project_slug = self._get_project().slug
|
@@ -276,7 +291,7 @@ def get_queryset(self):
|
276 | 291 | is compatible with DRF's paginator.
|
277 | 292 | """
|
278 | 293 | filters = {}
|
279 |
| - filters['project'] = [p.slug for p in self._get_all_projects()] |
| 294 | + filters['project'] = list(self._get_all_projects_data().keys()) |
280 | 295 | filters['version'] = self._get_version().slug
|
281 | 296 |
|
282 | 297 | # Check to avoid searching all projects in case these filters are empty.
|
|
0 commit comments