Adattare dinamicamente la vista degli utenti: un esempio della potenza degli adattatori in Plone applicato ad un caso reale
Supponiamo di dover implementare in Plone un sistema per la gestione di aree ad accesso riservato e di dover filtrare dalla vista dei permessi locali degli oggetti degli utenti secondo alcuni criteri (magari in base al contesto).
In maniera elegante, generica, facilmente mantenibile nel tempo e con poche righe di codice, senza modificare direttamente il codice originale!
Prerequisiti
- Plone 3
Passo passo
Dove viene costruita la lista dei Member?
Se dobbiamo filtrare la lista degli utenti del portale per prima cosa dobbiamo scoprire DOVE e COME viene costruita la lista degli utenti.
La lista degli utenti viene costruita istanziando il multiadapter pas_search, il quale espone una serie di metodi (fare riferimento a Products.PlonePAS.interfaces.browser.IPASSearchView per ulteriori informazioni), tra cui anche searchUsers.
Nel nostro caso dovremmo adattare alle nostre esigenze proprio il metodo searchUsers, richiamato dalle viste che si occupano di gestire i local roles sui singoli oggetti o per la gestione degli utenti.
Ecco come viene richiamato nel codice originale di Plone il multiadapter per fare le query sugli utenti (plone/app/workflow/browser/sharing.py):
...
hunter = getMultiAdapter((context, self.request), name='pas_search')
for userinfo in hunter.searchUsers(fullname=search_term):
userid = userinfo['userid']
...
Da notare: le nostre modifiche NON impattano sul codice di Plone!
Adattiamo alle nostre esigenze la query degli utenti
Quello che dobbiamo fare è scrivere una classe che estenda la classe Products.PlonePAS.browser.search import PASSearchView e faccia l'overloading del solo metodo searchUsers, aggiungendo le logiche per il filtraggio degli utenti che vogliamo implementare.
browser/search.py:
from zope.interface import implements
from Products.PlonePAS.interfaces.browser import IPASSearchView
from Products.PlonePAS.browser.search import PASSearchView as PASDefaultSearchView
from Products.CMFPlone.interfaces import IPloneSiteRoot
from redomino.workgroup.interfaces import IWorkgroup
from redomino.workgrouptool.content.workgroup_tool import WORKGROUP_MEMBERDATA
class PASSearchView(PASDefaultSearchView):
implements(IPASSearchView)
def searchUsers(self, sort_by=None, **criteria):
# chiamo il metodo searchUsers della classe originale
results = PASDefaultSearchView.searchUsers(self, sort_by, **criteria)
# results è un dizionario python
if IPloneSiteRoot.providedBy(self.context):
# per il plone site, tutti i risultati NON filtrati
return results
elif IWorkgroup.providedBy(self.context):
# solo i risultati relativi al workgroup su cui mi trovo, piu'gli utenti globali
return filter(lambda x: condizione1 su x, results)
else:
# in tutti gli altri contesti, filtro via TUTTI gli elementi non globali
return filter(lambda x: condizione2 su x, results)
return results
Registriamo il nostro adapter per tutte le interfacce (browser/overrides.zcml):
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="redomino.workgroup">
<!-- overrides -->
<browser:page
for="*"
name="pas_search"
class=".search.PASSearchView"
permission="zope2.View"
allowed_interface="Products.PlonePAS.interfaces.browser.IPASSearchView"
/>
</configure>
Registrazione dell'override
Facciamo l'override del nostro zcml per evitare errori al riavvio del nostro Zope (buildout.cfg), supponendo di lavorare su un proprio buildout:
...
zcml =
...
redomino.workgroup.browser-overrides
...
ALTERNATIVA AL PASSO PRECEDENTE: l'override in questo caso riguarderà tutti i portali Plone della nostra istanza; alternativamente, è possibile seguire la strada seguente: customizzazione utilizzando plone.browserlayer.
Da questo momento in poi verrà chiamato il NOSTRO adapter per ricercare gli utenti!
Ulteriori informazioni
Per ulteriori approfondimenti fare riferimento direttamente al codice sorgente di Plone.