[gen] Users have now a workflow allowing them to deactivated. An inactive user can't log in anymore. This is useful if the User is tied to other objects and cannot be removed without braking the data model, but does not correspond anymore to an active user that is allowed to log in.
This commit is contained in:
parent
4fc74b36e7
commit
400158a0a1
|
@ -546,23 +546,28 @@ class WritePermission(Permission): pass
|
||||||
# Standard workflows -----------------------------------------------------------
|
# Standard workflows -----------------------------------------------------------
|
||||||
class WorkflowAnonymous:
|
class WorkflowAnonymous:
|
||||||
'''One-state workflow allowing anyone to consult and Manager to edit.'''
|
'''One-state workflow allowing anyone to consult and Manager to edit.'''
|
||||||
mgr = 'Manager'
|
ma = 'Manager'
|
||||||
o = 'Owner'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, 'Anonymous', 'Authenticated'), w:(mgr,o),d:(mgr,o)},
|
everyone = (ma, 'Anonymous', 'Authenticated')
|
||||||
initial=True)
|
active = State({r:everyone, w:(ma, o), d:(ma, o)}, initial=True)
|
||||||
|
|
||||||
class WorkflowAuthenticated:
|
class WorkflowAuthenticated:
|
||||||
'''One-state workflow allowing authenticated users to consult and Manager
|
'''One-state workflow allowing authenticated users to consult and Manager
|
||||||
to edit.'''
|
to edit.'''
|
||||||
mgr = 'Manager'
|
ma = 'Manager'
|
||||||
o = 'Owner'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, 'Authenticated'), w:(mgr,o), d:(mgr,o)},
|
authenticated = (ma, 'Authenticated')
|
||||||
initial=True)
|
active = State({r:authenticated, w:(ma, o), d:(ma, o)}, initial=True)
|
||||||
|
|
||||||
class WorkflowOwner:
|
class WorkflowOwner:
|
||||||
'''One-state workflow allowing only manager and owner to consult and
|
'''One-state workflow allowing only manager and owner to consult and
|
||||||
edit.'''
|
edit.'''
|
||||||
mgr = 'Manager'
|
ma = 'Manager'
|
||||||
o = 'Owner'
|
o = 'Owner'
|
||||||
active = State({r:(mgr, o), w:(mgr, o), d:mgr}, initial=True)
|
# States
|
||||||
|
active = State({r:(ma, o), w:(ma, o), d:ma}, initial=True)
|
||||||
|
inactive = State({r:(ma, o), w:ma, d:ma})
|
||||||
|
# Transitions
|
||||||
|
deactivate = Transition( (active, inactive), condition=ma)
|
||||||
|
reactivate = Transition( (inactive, active), condition=ma)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -1072,7 +1072,7 @@ class ToolMixin(BaseMixin):
|
||||||
if not user: return
|
if not user: return
|
||||||
# Authentify the user if required.
|
# Authentify the user if required.
|
||||||
if authentify:
|
if authentify:
|
||||||
if not user.checkPassword(password):
|
if (user.state == 'inactive') or (not user.checkPassword(password)):
|
||||||
# Disable the authentication cookie.
|
# Disable the authentication cookie.
|
||||||
req.RESPONSE.expireCookie('_appy_', path='/')
|
req.RESPONSE.expireCookie('_appy_', path='/')
|
||||||
return
|
return
|
||||||
|
|
|
@ -9,7 +9,7 @@ from appy.px import Px
|
||||||
from appy.fields.workflow import UiTransition
|
from appy.fields.workflow import UiTransition
|
||||||
import appy.gen as gen
|
import appy.gen as gen
|
||||||
from appy.gen.utils import *
|
from appy.gen.utils import *
|
||||||
from appy.gen.layout import Table, defaultPageLayouts
|
from appy.gen.layout import Table
|
||||||
from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor
|
from appy.gen.descriptors import WorkflowDescriptor, ClassDescriptor
|
||||||
from appy.shared import utils as sutils
|
from appy.shared import utils as sutils
|
||||||
from appy.shared.data import rtlLanguages
|
from appy.shared.data import rtlLanguages
|
||||||
|
@ -1596,8 +1596,9 @@ class BaseMixin:
|
||||||
# Fallback to 'en'.
|
# Fallback to 'en'.
|
||||||
translation = getattr(tool, 'en').appy()
|
translation = getattr(tool, 'en').appy()
|
||||||
res = getattr(translation, label, '')
|
res = getattr(translation, label, '')
|
||||||
# If still no result, put the label instead of a translated message
|
# If still no result, put a nice name derived from the label instead of
|
||||||
if not res: res = label
|
# a translated message.
|
||||||
|
if not res: res = produceNiceMessage(label.rsplit('_', 1)[-1])
|
||||||
else:
|
else:
|
||||||
# Perform replacements, according to p_format.
|
# Perform replacements, according to p_format.
|
||||||
res = self.formatText(res, format)
|
res = self.formatText(res, format)
|
||||||
|
@ -1609,14 +1610,9 @@ class BaseMixin:
|
||||||
|
|
||||||
def getPageLayout(self, layoutType):
|
def getPageLayout(self, layoutType):
|
||||||
'''Returns the layout corresponding to p_layoutType for p_self.'''
|
'''Returns the layout corresponding to p_layoutType for p_self.'''
|
||||||
appyClass = self.wrapperClass.__bases__[-1]
|
res = self.wrapperClass.getPageLayouts()[layoutType]
|
||||||
if hasattr(appyClass, 'layouts'):
|
if isinstance(res, basestring): res = Table(res)
|
||||||
layout = appyClass.layouts[layoutType]
|
return res
|
||||||
if isinstance(layout, basestring):
|
|
||||||
layout = Table(layout)
|
|
||||||
else:
|
|
||||||
layout = defaultPageLayouts[layoutType]
|
|
||||||
return layout
|
|
||||||
|
|
||||||
def download(self, name=None):
|
def download(self, name=None):
|
||||||
'''Downloads the content of the file that is in the File field whose
|
'''Downloads the content of the file that is in the File field whose
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy.gen import WorkflowOwner
|
from appy.gen import WorkflowOwner
|
||||||
|
from appy.gen.layout import summaryPageLayouts
|
||||||
from appy.gen.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen import utils as gutils
|
from appy.gen import utils as gutils
|
||||||
|
|
||||||
|
@ -7,6 +8,7 @@ from appy.gen import utils as gutils
|
||||||
class UserWrapper(AbstractWrapper):
|
class UserWrapper(AbstractWrapper):
|
||||||
workflow = WorkflowOwner
|
workflow = WorkflowOwner
|
||||||
specialUsers = ('system', 'anon', 'admin')
|
specialUsers = ('system', 'anon', 'admin')
|
||||||
|
layouts = summaryPageLayouts
|
||||||
|
|
||||||
def showLogin(self):
|
def showLogin(self):
|
||||||
'''When must we show the login field?'''
|
'''When must we show the login field?'''
|
||||||
|
|
|
@ -6,6 +6,7 @@ import os, os.path, mimetypes
|
||||||
import appy.pod
|
import appy.pod
|
||||||
from appy.gen import Field, Search, Ref, String, WorkflowAnonymous
|
from appy.gen import Field, Search, Ref, String, WorkflowAnonymous
|
||||||
from appy.gen.indexer import defaultIndexes
|
from appy.gen.indexer import defaultIndexes
|
||||||
|
from appy.gen.layout import defaultPageLayouts
|
||||||
from appy.gen.utils import createObject
|
from appy.gen.utils import createObject
|
||||||
from appy.px import Px
|
from appy.px import Px
|
||||||
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
||||||
|
@ -681,6 +682,14 @@ class AbstractWrapper(object):
|
||||||
if not res: res = WorkflowAnonymous
|
if not res: res = WorkflowAnonymous
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getPageLayouts(klass):
|
||||||
|
'''Returns the page layouts for p_klass.'''
|
||||||
|
res = klass._getParentAttr('layouts')
|
||||||
|
# Return the default page layout if no layout was found.
|
||||||
|
if not res: res = defaultPageLayouts
|
||||||
|
return res
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def getIndexes(klass, includeDefaults=True):
|
def getIndexes(klass, includeDefaults=True):
|
||||||
'''Returns a dict whose keys are the names of the indexes that are
|
'''Returns a dict whose keys are the names of the indexes that are
|
||||||
|
|
Loading…
Reference in a new issue