from django.conf import settings from django.urls import Resolver404, resolve, reverse from django.utils.encoding import force_text from menus.menu_pool import menu_pool from djangocms_spa.content_helpers import (get_frontend_data_dict_for_cms_page, get_frontend_data_dict_for_partials, get_partial_names_for_template) from djangocms_spa.utils import get_frontend_component_name_by_template, get_view_from_url from .router_helpers import get_vue_js_router_name_for_cms_page def get_vue_js_router(context=None, request=None): """ Returns a list of all routes (CMS pages, projects, team members, etc.) that are in the menu of django CMS. The list contains a dict structure that is used by the Vue JS router. """ vue_routes = [] menu_renderer = get_menu_renderer(context=context, request=request) # For the usage of template tags inside our menu modifier we need to make it available on our menu_renderer. # The `set_context` method is a monkey patch and no part of the original class. menu_renderer.set_context(context) menu_nodes = menu_renderer.get_nodes() for node in menu_nodes: if node.attr.get('vue_js_route'): vue_routes.append(node.attr.get('vue_js_route')) return {'routes': vue_routes} def get_menu_renderer(context=None, request=None): menu_renderer = None if context: menu_renderer = context.get('cms_menu_renderer') if not menu_renderer: menu_renderer = menu_pool.get_renderer(request) return menu_renderer def get_node_template_name(node): try: view = get_view_from_url(node.get_absolute_url()) except (AttributeError, Resolver404): return settings.DJANGOCMS_SPA_VUE_JS_ERROR_404_TEMPLATE if view.__module__ == 'cms.views': template = node.attr.get('template') if template: return template else: try: return node.attr.get('cms_page').get_template() except: return settings.DJANGOCMS_SPA_VUE_JS_ERROR_404_TEMPLATE else: try: return view.template_name except AttributeError: return settings.DJANGOCMS_SPA_DEFAULT_TEMPLATE def get_node_route(request, node, renderer, template=''): # Initial data of the route. route_data = { 'api': {}, } if node.attr.get('is_page'): route = get_node_route_for_cms_page(request, node, route_data) else: route = get_node_route_for_app_model(request, node, route_data) if not node.attr.get('use_cache', True): route['api']['fetch']['useCache'] = False if node.selected and node.get_absolute_url() == request.path: if not template: template = get_node_template_name(node) # Static CMS placeholders and other global page elements (e.g. menu) go into the `partials` dict. partial_names = get_partial_names_for_template(template=template) route['api']['fetched']['response']['partials'] = get_frontend_data_dict_for_partials( partials=partial_names, request=request, editable=request.user.has_perm('cms.edit_static_placeholder'), renderer=renderer, ) # Add query params template_path = get_node_template_name(node) try: partials = settings.DJANGOCMS_SPA_TEMPLATES[template_path]['partials'] except KeyError: partials = [] if partials: route_data['api']['fetch'].setdefault('query', {}).update({'partials': partials}) if node.attr.get('redirect_url'): del route_data['api'] return route def get_node_route_for_cms_page(request, node, route_data): cms_page = node.attr['cms_page'] cms_page_title = cms_page.title_set.get(language=request.LANGUAGE_CODE) cms_template = cms_page.get_template() # Set name and component of the route. route_data['name'] = get_vue_js_router_name_for_cms_page(cms_page.pk) if not node.attr.get('redirect_url'): route_data['component'] = get_frontend_component_name_by_template(cms_template) # Add the link to fetch the data from the API. if cms_page.application_urls not in settings.DJANGOCMS_SPA_VUE_JS_APPHOOKS_WITH_ROOT_URL: if not cms_page_title.path: # The home page does not have a path if hasattr(settings, 'DJANGOCMS_SPA_USE_SERIALIZERS') and settings.DJANGOCMS_SPA_USE_SERIALIZERS: fetch_url = reverse('api:cms_page_detail', kwargs={'path': settings.DJANGOCMS_SPA_HOME_PATH}) else: fetch_url = reverse('api:cms_page_detail_home') elif node.attr.get('named_route_path_pattern'): # Get the fetch_url of the parent node through the path of the parent node parent_node_path = cms_page_title.path.replace('/%s' % cms_page_title.slug, '') fetch_url_of_parent_node = reverse('api:cms_page_detail', kwargs={'path': parent_node_path}) fetch_url = '{parent_url}{path_pattern}/'.format(parent_url=fetch_url_of_parent_node, path_pattern=node.attr.get('named_route_path_pattern')) else: fetch_url = reverse('api:cms_page_detail', kwargs={'path': cms_page_title.path}) # Add redirect url if available. if node.attr.get('redirect_url'): route_data['redirect'] = node.attr['redirect_url'] else: # Apphooks use a view that has a custom API URL to fetch data from. view = get_view_from_url(node.get_absolute_url()) fetch_url = force_text(view().get_fetch_url()) route_data['api']['fetch'] = { 'url': fetch_url, } if cms_page.reverse_id: route_data['meta'] = { 'id': cms_page.reverse_id } # Add initial data for the selected page. if node.selected and node.get_absolute_url() == request.path: if hasattr(settings, 'DJANGOCMS_SPA_USE_SERIALIZERS') and settings.DJANGOCMS_SPA_USE_SERIALIZERS: from djangocms_spa.serializers import PageSerializer data = PageSerializer(instance=cms_page).data else: data = get_frontend_data_dict_for_cms_page( cms_page=cms_page, cms_page_title=cms_page_title, request=request, editable=request.user.has_perm('cms.change_page') ) fetched_data = { 'response': { 'data': data } } if node.attr.get('named_route_path_pattern'): url_param = node.attr['named_route_path_pattern'].replace(':', '') if url_param: fetched_data.update({ 'params': { url_param: cms_page_title.slug } }) route_data['api']['fetched'] = fetched_data if settings.DJANGOCMS_SPA_VUE_JS_USE_I18N_PATTERNS: route_data['path'] = '/%s/%s' % (request.LANGUAGE_CODE, cms_page_title.path) else: route_data['path'] = '/%s' % cms_page_title.path return route_data def get_node_route_for_app_model(request, node, route_data): # Set name and component of the route. route_data['component'] = node.attr.get('component') route_data['name'] = node.attr.get('vue_js_router_name') # Add the link to fetch the data from the API. route_data['api']['fetch'] = { 'url': node.attr.get('fetch_url'), } try: request_url_name = resolve(request.path).url_name node_url_name = resolve(node.get_absolute_url()).url_name except Resolver404: resolver_match = False else: resolver_match = request_url_name == node_url_name is_selected_node = request.path == node.get_absolute_url() or resolver_match if is_selected_node: # We need to prepare the initial structure of the fetched data. The actual data is added by the view. route_data['api']['fetched'] = { 'response': { 'data': {} } } route_data['params'] = node.attr.get('url_params', {}) meta_id = node.attr.get('id') if meta_id: route_data['meta'] = { 'id': meta_id } route_data['path'] = node.get_absolute_url() return route_data