• Remember Mailing List

  • Re: Overriding the id/username generation

    from rossp on Jun 04, 2009 05:11 AM
    Hedley Roos <hedley@...>
    writes:
    
    >> As I'm using betahaus.emaillogin[1] in my application I'm wanting to
    >> override the generation of the username as this is a little confusing
    >> for users as they expect to login with their email address but we're
    >> still asking them to enter a username.
    >
    > Hi
    >
    > I haven't been paying attention to this thread but since Ross asked
    > others for help I'll try.
    
    Thank you, thank you!  :)
    
    > Assuming you simply want to let users log in with their email
    > addresses I can help. I do that exact thing with remember in a site,
    > and I'll provide code. The key to everything is the correct use of
    > getUserName instead of getId. There are a few methods in membrane and
    > remember that need to be monkey patched. The monkey patching is *not*
    > a hack - there are errors in these methods but I haven't had the time
    > to jump in and fix them. Sorry Ross!
    
    Hey, thanks for reporting them!  Can you summarize what the errors are
    as you see them so your observations are at least documented here?
    Thanks!
    
    > If you subclass from member.py then you must override getUserName as
    > such:
    >
    >     def getUserName(self):
    >         """
    >         Override
    >         """
    >         return self.getEmail()
    >
    > If you do not have your own class then you'll have to monkey member.py
    > (I don't think that method is ZCA aware yet).
    
    To be clear, I think the right thing to do here (and in almost any OO
    situation) is to subclass rather than monkey-patch and membrane/remember
    are explicitly oriented to be extended in this way.  In fact the
    argument should probably be made that this is exactly the right level
    for using class inheritance and *not* the ZCA.  IOW, please don't monkey
    patch, or over-use the ZCA, use basic OO and subclass to create your own
    member content type.  This is not, of course, a membrane/remember issue
    or even a ZCA issue but simply a Python/OO best practice issue.
    
    > I attach the patches. Individual methods have comments explaining what
    > has been done. Roche Compaan had a big hand in this.
    >
    > NB:
    > 1. You must update your membrane_tool catalog once you decide that
    > users are going to use email address as login
    > 2. You must of course enforce unique email addresses in a validator.
    > 3. Leave the username field on all forms since that is going to be the
    > id of the member object. Change the help text for the username to
    > indicate that it is a nickname.
    >
    > Hope this helps
    > Hedley
    >
    >
    >
    > from Products.PlonePAS.tools.memberdata import MemberDataTool \
    >      as BaseTool
    > from Products.CMFCore.utils import getToolByName
    > from Products.membrane.tools.membrane import MembraneTool
    > from Products.membrane.plugins.usermanager import MembraneUserManager
    > from Products.remember.content.member import BaseMember
    > from Products.remember.tools.memberdata import MemberDataContainer
    > from Products.remember.config import ALLOWED_MEMBER_ID_PATTERN
    >
    > # Make call to getUserName instead of getId
    > def wrapUser(self, user):
    >         """
    >         If possible, returns the Member object that corresponds to the
    >         given User object.
    >         """
    >         mbtool = getToolByName(self, 'membrane_tool')
    >         mem = mbtool.getUserAuthProvider(user.getUserName())
    >         if mem is None:
    >             return BaseTool.wrapUser(self, user)
    >         return mem.__of__(self).__of__(user)
    >
    > # Make call to getId instead of getUserName
    > def register(self):
    >         """
    >         perform any registration information necessary after a member is registered
    >         """
    >         rtool = getToolByName(self, 'portal_registration')
    >         site_props = getToolByName(self, 'portal_properties').site_properties
    >         
    >         # XXX unicode names break sending the email
    >         unicode_name = self.getFullname()
    >         self.setFullname(str(unicode_name))
    >         if site_props.validate_email or self.getMail_me():
    >             rtool.registeredNotify(self.getId())
    >
    >         self.setFullname(unicode_name)
    >
    > # Patch validate_id to getMemberById instead of mbtool.getUserAuthProvider
    > # since the latter does lookups on login, not id
    > def validate_id(self, id):
    >     # we can't always trust the id argument, b/c the autogen'd
    >     # id will be passed in if the reg form id field is blank
    >     form = self.REQUEST.form
    >     if form.has_key('id') and not form['id']:
    >         return self.translate('Input is required but no input given.',
    >                               default='You did not enter a login name.'),
    >     elif self.id and id != self.id:
    >         # we only validate if we're changing the id
    >         mtool = getToolByName(self, 'portal_membership')
    >         if mtool.getMemberById(id) is not None or \
    >                not ALLOWED_MEMBER_ID_PATTERN.match(id) or \
    >                id == 'Anonymous User':
    >             msg = "The login name you selected is already " + \
    >                   "in use or is not valid. Please choose another."
    >             return self.translate(msg, default=msg)
    >
    >
    > MemberDataContainer.wrapUser = wrapUser
    > BaseMember.register = register
    > BaseMember.validate_id = validate_id
    
    From this, it looks like the only thing that *might* need
    monkey-patching would be the wrapUser method.  Am I missing anything?
    
    I look forward to hearing more about the errors elsewhere that
    necessitate monkey patching.
    
    Ross
    
    
    Thread Outline:
  • Re: Re: Overriding the id/username generation

    from hedley on Jun 04, 2009 06:22 AM
    Ross Patterson wrote:
    
    > To be clear, I think the right thing to do here (and in almost any OO
    > situation) is to subclass rather than monkey-patch and membrane/remember
    > are explicitly oriented to be extended in this way.  In fact the
    > argument should probably be made that this is exactly the right level
    > for using class inheritance and *not* the ZCA.  IOW, please don't monkey
    > patch, or over-use the ZCA, use basic OO and subclass to create your own
    > member content type.  This is not, of course, a membrane/remember issue
    > or even a ZCA issue but simply a Python/OO best practice issue.
    
    Many of our clients want to be able to login using their email address. 
    It would be nice if remember could support this without requiring a 
    subclass.
    
    >>From this, it looks like the only thing that *might* need
    > monkey-patching would be the wrapUser method.  Am I missing anything?
    
    I believe that is the smallest set of changes that are required for 
    email logins to work correctly. The comments above every method explain 
    why they need to be modified.
    
    We run a few large sites which have a mix-match of username / email 
    logins in the same site. All member accounts are either pure remember or 
    a subclass and it all works with these patches.
    
    Hedley