Skip to content

Instantly share code, notes, and snippets.

@rodfersou
Last active August 15, 2017 20:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rodfersou/ccf56c4ef4057c6cfc72261d53bcd73c to your computer and use it in GitHub Desktop.
Save rodfersou/ccf56c4ef4057c6cfc72261d53bcd73c to your computer and use it in GitHub Desktop.
# -*- coding: utf-8 -*-
""" bin/instance -O Plone run reorder_menu.py """
from collections import OrderedDict
from plone import api
from plone.dexterity.interfaces import IDexterityContent
import transaction
class EasyMenu(OrderedDict):
""" Using ideas from Beyond PEP8 talk from Raymond Hettinger to easily reorder Plone Menu """
def __init__(self, obj=None, parent=None, depth=1, max_depth=2):
super(EasyMenu, self).__init__()
if obj is None:
obj = api.portal.get()
self.obj = obj
self.parent = parent
self.depth = depth
self.max_depth = max_depth
if depth > max_depth:
return
# Create internal structure
for o in obj.listFolderContents():
if self._exclude_from_nav(o):
continue
if api.content.get_state(o, '') != 'published':
continue
if 'folder' in o.portal_type.lower():
self[o.id] = MenuFolder(obj=o, parent=self, depth=depth+1, max_depth=max_depth)
else:
self[o.id] = MenuItem(obj=o, parent=self, depth=depth+1, max_depth=max_depth)
def _exclude_from_nav(self, obj):
"""Check DX and AT way if is a menu item."""
if IDexterityContent.providedBy(obj):
try:
# Dexterity
exclude_from_nav = obj.exclude_from_nav
if callable(exclude_from_nav):
return True
return exclude_from_nav
except AttributeError:
pass
else:
try:
# Archetypes
return obj.getExcludeFromNav()
except AttributeError:
pass
return True # For some content type that can't be a Menu
def sort(self, key=None, folder_first=False, walk_tree=True, child=False):
"""Sort menu"""
if key is None:
# by default we sort by Title if exists or by ID
key = lambda x: x.title if x.title != '' else x.id.decode('utf-8')
# map tuple to value object
if not child:
the_key = key
key = lambda x: the_key(x[1].obj)
if not folder_first:
menu = OrderedDict(sorted(self.iteritems(), key=key))
self.clear()
for k, v in menu.iteritems():
self[k] = v
if walk_tree:
for folder in self.values():
if isinstance(folder, MenuItem):
continue
folder.sort(
key=key, folder_first=folder_first, walk_tree=walk_tree, child=True)
return
# Keep folders first
folders = {k: v for k, v in self.iteritems() if isinstance(v, MenuFolder)}
items = {k: v for k, v in self.iteritems() if isinstance(v, MenuItem)}
folders = OrderedDict(sorted(folders.iteritems(), key=key))
items = OrderedDict(sorted(items.iteritems(), key=key))
if walk_tree:
for folder in folders.values():
folder.sort(key=key, folder_first=folder_first, walk_tree=walk_tree, child=True)
self.clear()
for k, v in folders.iteritems():
self[k] = v
for k, v in items.iteritems():
self[k] = v
def __repr__(self):
return 'EasyMenu(\n' + '\n'.join(' {}: {},'.format(k, v._repr()) for k, v in self.iteritems()) + '\n)'
def __enter__(self):
return self
def __exit__(self, exctype, excinst, exctb):
"""Map dict reordering into Plone folder."""
if exctype is not None:
return
def walk_tree(item):
keys = item.keys()
if len(keys) == 0:
return
if len(keys) > 1:
item.obj.moveObjectsToTop(keys)
for subitem in item.itervalues():
if not isinstance(subitem, MenuFolder):
continue
walk_tree(subitem)
walk_tree(self)
class MenuFolder(EasyMenu):
def __init__(self, obj, parent, depth, max_depth):
super(MenuFolder, self).__init__(obj, parent, depth, max_depth)
def _repr(self):
return 'MenuFolder({})'.format(self.obj.id)
class MenuItem(object):
def __init__(self, obj, parent, depth, max_depth):
self.obj = obj
self.parent = parent
self.depth = depth
self.max_depth = max_depth
def _repr(self):
return 'MenuItem({})'.format(self.obj.id)
def __repr__(self):
return self._repr()
# Sort by Title if possible or by ID
with EasyMenu() as em:
em.sort()
## Sort by ID
#with EasyMenu() as em:
# em.sort(key=lambda x: x.id)
## Sort with folders first
#with EasyMenu() as em:
# em.sort(folders_first=True)
## Sort specific part of menu
#obj = api.content.get('/my/folder/path')
#with EasyMenu(obj=obj) as em:
# em.sort(walk_tree=False)
## Uncomment to save changes
#transaction.commit()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment