- {% for item in section_item.sorted_items %}
+ {% for item in section_item.items %}
{% if advanced_mode or not item.advanced %}
{% include "card.html" %}
{% endif %}
diff --git a/plinth/views.py b/plinth/views.py
index 56c70e6d4..97128ad1f 100644
--- a/plinth/views.py
+++ b/plinth/views.py
@@ -174,13 +174,30 @@ def index(request):
def _pick_menu_items(menu_items, selected_tags):
"""Return a sorted list of menu items filtered by tags."""
+ class MenuProxy:
+ """A proxy for the menu item to hold filtered children."""
+
+ def __init__(self, menu_item: menu.Menu):
+ """Initialize a menu proxy object."""
+ self.menu_item = menu_item
+ self.items: list[menu.Menu] = []
+ tags = menu_item.tags or []
+ for item in menu_item.items:
+ tags += item.tags or []
+
+ self.tags = list(tags)
+
+ def __getattr__(self, name: str):
+ """Return attributed from proxied object."""
+ return getattr(self.menu_item, name)
+
def _mismatch_map(menu_item) -> list[bool]:
"""Return a list of mismatches for selected tags.
A mismatch is when a selected tag is *not* present in the list of
tags for menu item.
"""
- menu_tags = set(menu_item.tags)
+ menu_tags = set(menu_item.tags or [])
return [tag not in menu_tags for tag in selected_tags]
def _sort_key(menu_item):
@@ -194,17 +211,39 @@ def _pick_menu_items(menu_items, selected_tags):
return (_mismatch_map(menu_item).count(True), _mismatch_map(menu_item),
menu_item.order, menu_item.name.lower())
+ proxied_menu_items = []
+ for menu_item in menu_items:
+ proxied_item = MenuProxy(menu_item)
+ proxied_item.items = _pick_menu_items(menu_item.items, selected_tags)
+ proxied_menu_items.append(proxied_item)
+
# Filter out menu items that don't match any of the selected tags. If
# no tags are selected, return all menu items. Otherwise, return all
# menu items that have at least one matching tag.
filtered_menu_items = [
- menu_item for menu_item in menu_items
+ menu_item for menu_item in proxied_menu_items
if (not selected_tags) or (not all(_mismatch_map(menu_item)))
]
return sorted(filtered_menu_items, key=_sort_key)
+def _get_all_tags(menu_items: list[menu.Menu]) -> list[str]:
+ """Return a sorted list of all tags present in the given menu items."""
+
+ def get_tags(menu_items: list[menu.Menu]) -> set[str]:
+ """Return a list of tags, unsorted."""
+ all_tags = set()
+ for menu_item in menu_items:
+ all_tags.update(menu_item.tags or [])
+ all_tags |= get_tags(menu_item.items)
+
+ return all_tags
+
+ # Sort tags by localized string
+ return sorted(get_tags(menu_items), key=_)
+
+
class AppsIndexView(TemplateView):
"""View for apps index.
@@ -223,12 +262,7 @@ class AppsIndexView(TemplateView):
menu_items = menu.main_menu.active_item(self.request).items
context['tags'] = tags
- # Sorted tags by localized string
- all_tags = set()
- for menu_item in menu_items:
- all_tags.update(menu_item.tags or [])
-
- context['all_tags'] = sorted(all_tags, key=_)
+ context['all_tags'] = _get_all_tags(menu_items)
context['menu_items'] = _pick_menu_items(menu_items, tags)
return context
@@ -236,11 +270,16 @@ class AppsIndexView(TemplateView):
def system_index(request):
"""Serve the system index page."""
- menu_items = menu.main_menu.active_item(request).sorted_items()
- return TemplateResponse(request, 'system.html', {
- 'advanced_mode': get_advanced_mode(),
- 'menu_items': menu_items
- })
+ tags = request.GET.getlist('tag', [])
+ menu_items = menu.main_menu.active_item(request).items
+
+ return TemplateResponse(
+ request, 'system.html', {
+ 'advanced_mode': get_advanced_mode(),
+ 'menu_items': _pick_menu_items(menu_items, tags),
+ 'tags': tags,
+ 'all_tags': _get_all_tags(menu_items)
+ })
class LanguageSelectionView(FormView):
@@ -471,9 +510,8 @@ class SetupView(TemplateView):
context['setup_state'] = setup_state
context['operations'] = operation.manager.filter(app.app_id)
context['show_rerun_setup'] = False
- context['show_uninstall'] = (
- not app.info.is_essential
- and setup_state != app_module.App.SetupState.NEEDS_SETUP)
+ context['show_uninstall'] = (not app.info.is_essential and setup_state
+ != app_module.App.SetupState.NEEDS_SETUP)
# Perform expensive operation only if needed.
if not context['operations']: