#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (C) 2010-2017 Étienne Loks # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # See the file COPYING for details. """ Menus """ from copy import deepcopy import datetime from django.conf import settings from django.core.cache import cache from django.core.urlresolvers import reverse from django.contrib.auth.models import User _extra_menus = [] # collect menu from INSTALLED_APPS for app in settings.INSTALLED_APPS: mod = __import__(app, fromlist=['ishtar_menu']) if hasattr(mod, 'ishtar_menu'): menu = getattr(mod, 'ishtar_menu') _extra_menus += menu.MENU_SECTIONS # sort __section_items = [mnu for order, mnu in sorted(_extra_menus, key=lambda x:x[0])] # regroup menus _section_items, __keys = [], [] for section_item in __section_items: if section_item.idx not in __keys: __keys.append(section_item.idx) _section_items.append(section_item) continue section_childs = _section_items[__keys.index(section_item.idx)].childs childs_idx = [child.idx for child in section_childs] for child in section_item.childs: if child.idx not in childs_idx: section_childs.append(child) class Menu: ref_childs = _section_items def __init__(self, user, current_action=None, session=None): self.user = user self.initialized = False self.items = {} self.current_action = current_action self.current_section = None self.current_url = None self.current_section = "" self.current_sections = [] self.current_subsection = "" self.current_subsections = [] self.current_subsubsection = "" self.current_subsubsections = [] self.selected_idx = None self.session = session self.items_by_idx = {} def reinit_menu_for_all_user(self): """ Force cache deletion and reinitialization of menu for all """ lst_cache_key = u"{}-{}".format( settings.PROJECT_SLUG, 'menu_updated_list', ) lst_ids = cache.get(lst_cache_key) if not lst_ids: return for idx in lst_ids: self.init(idx, force=True) def set_menu_updated_key(self, cache_key, user_id): """ Stock updated information in cache """ lst_cache_key = u"{}-{}".format( settings.PROJECT_SLUG, 'menu_updated_list', ) lst_ids = cache.get(lst_cache_key) if not lst_ids: lst_ids = [] if not lst_ids or user_id not in lst_ids: lst_ids.append(user_id) cache.set(lst_cache_key, lst_ids, settings.CACHE_TIMEOUT) time = unicode(datetime.datetime.now().isoformat()) cache.set(cache_key, time, settings.CACHE_TIMEOUT) self.initialized = time def init(self, user_id=0, force=False): """ Get or init menu for a user """ if not user_id: if not self.user: return user_id = self.user.pk else: try: self.user = User.objects.get(pk=user_id) except User.DoesNotExist: return cache_key = u"{}-{}-{}".format( settings.PROJECT_SLUG, 'menu_updated', user_id ) menu_updated = cache.get(cache_key) if not force and menu_updated and self.initialized \ and self.initialized == menu_updated: return self.set_menu_updated_key(cache_key, user_id) self.items = {} self.items_by_idx = {} childs = deepcopy(self.ref_childs) for idx, main_menu in enumerate(reversed(childs)): if not main_menu.can_be_available(self.user, self.session): childs.pop(len(self.ref_childs) - idx - 1) continue self.items_by_idx[main_menu.idx] = main_menu sub_childs = main_menu.childs[:] for s_idx, child in enumerate(reversed(main_menu.childs)): if not child.can_be_available(self.user, self.session): sub_childs.pop(len(main_menu.childs) - s_idx - 1) continue self.items_by_idx[child.idx] = child if hasattr(child, 'childs'): sub_sub_childs = child.childs[:] for ss_idx, subchild in enumerate(reversed(child.childs)): if not subchild.can_be_available( self.user, self.session): sub_sub_childs.pop(len(child.childs) - ss_idx - 1) continue self.items_by_idx[subchild.idx] = subchild child.childs = sub_sub_childs main_menu.childs = sub_childs selected = main_menu.set_items( self.user, self.items, self.current_action, session=self.session) if selected: self.selected_idx = idx self.childs = childs self.initialized = True def get_current_selection(self, current_path): # current_section, current_subsection, etc. are current labels # current_sections, current_subsections, etc. are list of: # (label, url, has_children) self.current_section = '' self.current_sections = [] self.current_subsection = "" self.current_subsections = [] self.current_subsubsection = "" self.current_subsubsections = [] self.current_url = None for section in self.childs: if not section.available: continue section_url = None subsections = [] if not self.current_section: # initialize by default with the first section self.current_section = section.label selected_section = None for menu_item in section.childs: if not menu_item.available: continue if not hasattr(menu_item, 'childs') or not menu_item.childs: item_url = reverse('action', args=[menu_item.idx]) if not section_url: section_url = item_url subsections.append([menu_item.label, item_url, False]) if item_url in current_path: self.current_url = item_url self.current_section = section.label self.current_subsection = menu_item.label selected_section = True continue subsection_url = None selected_subsection = None subsubsections = [] for menu_subitem in menu_item.childs: if not menu_subitem.available: continue item_url = reverse('action', args=[menu_subitem.idx]) if not section_url: section_url = item_url if not subsection_url: subsection_url = item_url subsections.append([menu_item.label, item_url, True]) subsubsections.append([menu_subitem.label, item_url, False]) if item_url in current_path: self.current_url = item_url self.current_section = section.label self.current_subsection = menu_item.label self.current_subsubsection = menu_subitem.label selected_section = True selected_subsection = True if selected_subsection: self.current_subsubsections = subsubsections if selected_section: self.current_subsections = subsections if not section_url: section_url = "/" self.current_sections.append([section.label, section_url, bool(subsections)])