def test_population_and_value_getting(root_page, django_assert_num_queries): domestic_homepage = HomePageFactory(parent=root_page) topic_page = TopicLandingPageFactory(parent=domestic_homepage, slug='topic') article_list_page = ArticleListingPageFactory(parent=topic_page, slug='list') article_page = ArticlePageFactory(parent=article_list_page, slug='article') Site.objects.all().delete() site = Site.objects.create( site_name='Great Domestic', hostname='domestic.trade.great', root_page=domestic_homepage, ) # Trigger population of site root paths cache Site.get_site_root_paths() # Prefetch content type for this page root_page.specific_class # With the two potential queries above out of the way, # population should only use as single datatbase query with django_assert_num_queries(1): result = PageIDCache.populate() # Check result looks as expected assert result == { 'by-path': { f'{site.id}:/': domestic_homepage.id, f'{site.id}:/topic/': topic_page.id, f'{site.id}:/topic/list/': article_list_page.id, f'{site.id}:/topic/list/article/': article_page.id }, 'by-slug': { 'EXPORT_READINESS:great-domestic-home': domestic_homepage.id, 'EXPORT_READINESS:topic': topic_page.id, 'EXPORT_READINESS:list': article_list_page.id, 'EXPORT_READINESS:article': article_page.id, }, } # Check get_for_path() result_1 = PageIDCache.get_for_path('/', site.id) assert result_1 == domestic_homepage.id result_2 = PageIDCache.get_for_path('/topic/list/article/', site.id) assert result_2 == article_page.id # Check invalid get_for_path() assert PageIDCache.get_for_path('123', 99) is None # Check get_for_slug() result_1 = PageIDCache.get_for_slug('topic', 'EXPORT_READINESS') assert result_1 == topic_page.id result_2 = PageIDCache.get_for_slug('article', 'EXPORT_READINESS') assert result_2 == article_page.id # Check invalid get_for_slug() assert PageIDCache.get_for_slug('abc', 'IMPORT_NOT_READINESS') is None
def populate(cls, *args, **kwargs): ids_by_path = {} ids_by_slug = {} # This value should be cached by Wagtail site_root_paths = Site.get_site_root_paths() for page in cls.get_population_queryset(): # Path lookup keys must include the site id and url_path, minus # the site root path, which Page.get_url_parts() can give us # setting this prevents repeat cache lookups page._wagtail_cached_site_root_paths = site_root_paths url_parts = page.get_url_parts() if url_parts: key = cls.build_path_lookup_key(url_parts[0], url_parts[2]) ids_by_path[key] = page.id # Slug lookup keys must include the service name, as well as the # slug, which we need to work out service_name = cls.get_service_name_for_page(page) if service_name: key = cls.build_slug_lookup_key(service_name, page.slug) ids_by_slug[key] = page.id page_ids = { cls.by_path_map_key: ids_by_path, cls.by_slug_map_key: ids_by_slug, } cls.cache.set(cls.cache_key, page_ids, timeout=settings.API_CACHE_EXPIRE_SECONDS) return page_ids
def test_result_order_when_multiple_sites_share_the_same_root_page(self): result = Site.get_site_root_paths() # An entry for the default site should come first self.assertEqual(result[0][0], self.default_site.id) # Followed by entries for others in 'host' alphabetical order self.assertEqual(result[1][0], self.abc_site.id) self.assertEqual(result[2][0], self.def_site.id)
def test_result_order_when_multiple_sites_share_the_same_root_page(self): result = Site.get_site_root_paths() # An entry for the default site should come first self.assertEqual(result[0][0], self.default_site.id) # Followed by entries for others in 'host' alphabetical order self.assertEqual(result[1][0], self.abc_site.id) self.assertEqual(result[2][0], self.def_site.id)
def _run_test( self, url, expected_page, expected_num_queries, full_url_match_expected, accept_best_match=True, max_subsequent_route_failures=3 ): request = self.rf.get(url) # Set these to improve efficiency request.site = self.site request._wagtail_cached_site_root_paths = Site.get_site_root_paths() # Run tests with self.assertNumQueries(expected_num_queries): page, full_url_match = derive_page( request, self.site, accept_best_match, max_subsequent_route_failures, ) self.assertEqual(page, expected_page) self.assertIs(full_url_match, full_url_match_expected)
def external_link(request): initial_data = { 'url': request.GET.get('link_url', ''), 'link_text': request.GET.get('link_text', ''), } if request.method == 'POST': form = ExternalLinkChooserForm(request.POST, initial=initial_data, prefix='external-link-chooser') if form.is_valid(): submitted_url = form.cleaned_data['url'] result = { 'url': submitted_url, 'title': form.cleaned_data['link_text'].strip() or form.cleaned_data['url'], # If the user has explicitly entered / edited something in the link_text field, # always use that text. If not, we should favour keeping the existing link/selection # text, where applicable. # (Normally this will match the link_text passed in the URL here anyhow, # but that won't account for non-text content such as images.) 'prefer_this_title_as_link_text': ('link_text' in form.changed_data), } link_conversion = getattr(settings, 'WAGTAILADMIN_EXTERNAL_LINK_CONVERSION', LINK_CONVERSION_ALL).lower() if link_conversion not in [ LINK_CONVERSION_ALL, LINK_CONVERSION_EXACT, LINK_CONVERSION_CONFIRM ]: # We should not attempt to convert external urls to page links return render_modal_workflow(request, None, None, None, json_data={ 'step': 'external_link_chosen', 'result': result }) # Next, we should check if the url matches an internal page # Strip the url of its query/fragment link parameters - these won't match a page url_without_query = re.split(r"\?|#", submitted_url)[0] # Start by finding any sites the url could potentially match sites = getattr(request, '_wagtail_cached_site_root_paths', None) if sites is None: sites = Site.get_site_root_paths() match_relative_paths = submitted_url.startswith('/') and len( sites) == 1 # We should only match relative urls if there's only a single site # Otherwise this could get very annoying accidentally matching coincidentally # named pages on different sites if match_relative_paths: possible_sites = [(pk, url_without_query) for pk, path, url, language_code in sites] else: possible_sites = [(pk, url_without_query[len(url):]) for pk, path, url, language_code in sites if submitted_url.startswith(url)] # Loop over possible sites to identify a page match for pk, url in possible_sites: try: route = Site.objects.get(pk=pk).root_page.specific.route( request, [ component for component in url.split('/') if component ]) matched_page = route.page.specific internal_data = { 'id': matched_page.pk, 'parentId': matched_page.get_parent().pk, 'adminTitle': matched_page.draft_title, 'editUrl': reverse('wagtailadmin_pages:edit', args=(matched_page.pk, )), 'url': matched_page.url } # Let's check what this page's normal url would be normal_url = matched_page.get_url_parts( request=request )[-1] if match_relative_paths else matched_page.get_full_url( request=request) # If that's what the user provided, great. Let's just convert the external # url to an internal link automatically unless we're set up tp manually check # all conversions if normal_url == submitted_url and link_conversion != LINK_CONVERSION_CONFIRM: return render_modal_workflow( request, None, None, None, json_data={ 'step': 'external_link_chosen', 'result': internal_data }) # If not, they might lose query parameters or routable page information if link_conversion == LINK_CONVERSION_EXACT: # We should only convert exact matches continue # Let's confirm the conversion with them explicitly else: return render_modal_workflow( request, 'wagtailadmin/chooser/confirm_external_to_internal.html', None, { 'submitted_url': submitted_url, 'internal_url': normal_url, 'page': matched_page.draft_title, }, json_data={ 'step': 'confirm_external_to_internal', 'external': result, 'internal': internal_data }) except Http404: continue # Otherwise, with no internal matches, fall back to an external url return render_modal_workflow(request, None, None, None, json_data={ 'step': 'external_link_chosen', 'result': result }) else: form = ExternalLinkChooserForm(initial=initial_data, prefix='external-link-chooser') return render_modal_workflow(request, 'wagtailadmin/chooser/external_link.html', None, shared_context(request, { 'form': form, }), json_data={'step': 'external_link'})