• Remember Mailing List

  • Overriding the id/username generation

    from duffyd on May 31, 2009 08:07 PM
    Hello,
    
    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.
    
    I've tried to override this as follows in my member schema:
    
        # Overriding the id field as we're using betahaus.emaillogin
        atapi.ComputedField('id',
                           expression = "context.setId()",
    
    index=('membrane_tool/ZCTextIndex,lexicon_id=member_lexicon,index_type=Cosine Measure|TextIndex:brains',
                                  'FieldIndex:brains'),
                     widget=atapi.ComputedWidget(label='User name'),
                     regfield=0,
                     user_property=True,
                     ),
    
    and I've setup the setId method on the member content type as follows
    (the getName method is an accessor for a StringField where users can
    enter their fullname):
    
        def setId(self, id=''):
            "Overriding the username generator"
            import pdb;pdb.set_trace()
            if self.getName():
                if not getattr(self, 'subscribememberid', None):
                    names = self.getName().split()
                    if len(names) > 1:
                        firstname = names[0]
                        surname = names[-1]
                        id = firstname[:1]+surname
                    else:
                        id = names[0]
                    catalog = getToolByName(self.context, 'portal_catalog')
                    existing = catalog(id=id, portal_type='Subscribemember')
                    if len(existing) != 0:
                        try:
                            i = int(id[-1])
                            id = id+str(i+1)
                        except:
                            id = id+'1'
                        return self.setId(id)
                    else:
                        setattr(self, 'subscribememberid', True)
                else:
                    return getattr(self, 'subscribememberid')
    
    As you can see I just grab the first initial of the firstname and the
    surname and then check that no user exists for that username.
    
    When I run my functional test, though, it doesn't create the user and
    doesn't even enter the setId method (as you can see I've got a pdb in
    there). Its supposed to return 'You have been registered as a member.'
    but instead I get 'Changes saved.' as a status message.
    
    Any tips on how I *should* be implementing this?
    
    Thanks,
    Tim
    
    
    Thread Outline:
  • Re: Overriding the id/username generation

    from ra on Jun 01, 2009 02:10 PM
    Tim Knapp wrote:
    > Hello,
    > 
    > 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.
    > 
    > I've tried to override this as follows in my member schema:
    > 
    >     # Overriding the id field as we're using betahaus.emaillogin
    >     atapi.ComputedField('id',
    >                        expression = "context.setId()",
    > 
    > index=('membrane_tool/ZCTextIndex,lexicon_id=member_lexicon,index_type=Cosine Measure|TextIndex:brains',
    >                               'FieldIndex:brains'),
    >                  widget=atapi.ComputedWidget(label='User name'),
    >                  regfield=0,
    >                  user_property=True,
    >                  ),
    
    it's worth noting that, even though the default member schema still has these 
    'index' settings littered throughout, the AT schema is no longer the correct 
    place to be setting up indexing behaviour.  in fact, i don't think it will 
    have any impact.  the GenericSetup profile is where this should be done.
    
    > and I've setup the setId method on the member content type as follows
    > (the getName method is an accessor for a StringField where users can
    > enter their fullname):
    > 
    >     def setId(self, id=''):
    >         "Overriding the username generator"
    >         import pdb;pdb.set_trace()
    >         if self.getName():
    >             if not getattr(self, 'subscribememberid', None):
    >                 names = self.getName().split()
    >                 if len(names) > 1:
    >                     firstname = names[0]
    >                     surname = names[-1]
    >                     id = firstname[:1]+surname
    >                 else:
    >                     id = names[0]
    >                 catalog = getToolByName(self.context, 'portal_catalog')
    >                 existing = catalog(id=id, portal_type='Subscribemember')
    >                 if len(existing) != 0:
    >                     try:
    >                         i = int(id[-1])
    >                         id = id+str(i+1)
    >                     except:
    >                         id = id+'1'
    >                     return self.setId(id)
    >                 else:
    >                     setattr(self, 'subscribememberid', True)
    >             else:
    >                 return getattr(self, 'subscribememberid')
    
    like ross, i'm not entirely sure how what you're doing here is all supposed to 
    fit together.  it's a bit confusing, too, b/c you're using 'setId' as the 
    computed field expression, when setId is the default name for the id field 
    mutator.
    
    i don't know why your approach isn't working, but i'd personally be a bit 
    reluctant to change id to a ComputedField.  i'd probably continue to have id 
    as a regular StringField, but i'd hide it from the edit forms and would take 
    steps to make sure that it was always set to the value that you wanted, 
    probably by overriding the 'update' method on the member object.  i'd also 
    maybe override the 'setId' mutator to do a bit of sanity checking, to reject 
    any attempts to set the id if it doesn't match the expected value (be sure to 
    make allowances for the auto-generated temporary ids, though).
    
    -r
    
    • Re: Overriding the id/username generation

      from duffyd on Jun 03, 2009 11:49 PM
      Hello again,
      
      On Mon, 2009-06-01 at 11:09 -0700, Rob Miller wrote:
      
      <snip />
      
      > 
      > like ross, i'm not entirely sure how what you're doing here is all supposed to 
      > fit together.  it's a bit confusing, too, b/c you're using 'setId' as the 
      > computed field expression, when setId is the default name for the id field 
      > mutator.
      > 
      > i don't know why your approach isn't working, but i'd personally be a bit 
      > reluctant to change id to a ComputedField.  i'd probably continue to have id 
      > as a regular StringField, but i'd hide it from the edit forms and would take 
      > steps to make sure that it was always set to the value that you wanted, 
      > probably by overriding the 'update' method on the member object.  i'd also 
      > maybe override the 'setId' mutator to do a bit of sanity checking, to reject 
      > any attempts to set the id if it doesn't match the expected value (be sure to 
      > make allowances for the auto-generated temporary ids, though).
      
      Ok, I've been trying to implement this today and have been hitting my
      head against a brick wall. It should be a simple exercise to override
      the 'update' method within my customised-Remember type but 'oh no' it
      doesn't wanna work.
      
      My current Remember-classes code is here[1]. As you can see I've put a
      pdb statement in there but it never gets called. As you can also see
      I've overridden the validate_id method as this was causing it to fail
      too as it was expecting a id to be entered into the form.
      
      Any tips please?
      
      -A very frustrated Plonista (Tim)
      
      [1] http://duffyd.pastebin.com/f1d41cb88
      
      > 
      > -r
      > 
      > 
      > --
      > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1243879831442
      > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
      > 
      
      
  • Re: Overriding the id/username generation

    from hedley on Jun 04, 2009 04:32 AM
    > 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.
    
    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!
    
    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).
    
    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
    
    
    
    
    
    • Re: Overriding the id/username generation

      from duffyd on Jun 04, 2009 05:34 AM
      Hi Hedley,
      
      Thanks a lot for responding to my message.
      
      On Thu, 2009-06-04 at 10:32 +0200, Hedley Roos wrote:
      > > 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.
      > 
      > 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!
      > 
      > 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).
      > 
      > 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.
      
      betahaus.emaillogin works just fine with remember so I don't have a need
      to change the remember code in any way to allow users to login with
      their email address. My main need is to be able to remove the id field
      from the reg_form and instead autogenerate this as it is confusing users
      who are wondering why they need to enter a username as they will be
      logging on with their email address.
      
      Your suggestion re. changing the help text is a novel one and I'll see
      if I can sell this to the client, though.
      
      Thanks again,
      Tim
      
      > 
      > Hope this helps
      > Hedley
      > 
      > 
      > 
      > plain text document attachment (patch.py)
      > 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
      
      
      • Re: Overriding the id/username generation

        from hedley on Jun 04, 2009 06:13 AM
        > betahaus.emaillogin works just fine with remember so I don't have a need
        > to change the remember code in any way to allow users to login with
        > their email address. My main need is to be able to remove the id field
        > from the reg_form and instead autogenerate this as it is confusing users
        > who are wondering why they need to enter a username as they will be
        > logging on with their email address.
        > 
        > Your suggestion re. changing the help text is a novel one and I'll see
        > if I can sell this to the client, though.
        
        Hi Tim
        
        If you autogenerate an id I assume it is going to be computed from the 
        email address. If that is the case then it means that users' email 
        addresses can easily be retrieved from an URL, which is breach of 
        privacy. That warning is usually enough to convince the client :)
        
        Hedley
        
        • Re: Overriding the id/username generation

          from duffyd on Jun 04, 2009 06:17 AM
          On Thu, 2009-06-04 at 12:13 +0200, Hedley Roos wrote:
          > > betahaus.emaillogin works just fine with remember so I don't have a need
          > > to change the remember code in any way to allow users to login with
          > > their email address. My main need is to be able to remove the id field
          > > from the reg_form and instead autogenerate this as it is confusing users
          > > who are wondering why they need to enter a username as they will be
          > > logging on with their email address.
          > > 
          > > Your suggestion re. changing the help text is a novel one and I'll see
          > > if I can sell this to the client, though.
          > 
          > Hi Tim
          > 
          > If you autogenerate an id I assume it is going to be computed from the 
          > email address. If that is the case then it means that users' email 
          > addresses can easily be retrieved from an URL, which is breach of 
          > privacy. That warning is usually enough to convince the client :)
          
          Good thinking 99 ;)
          but unfortunately the client has asked me to autogenerate the usernames
          based on a sequential 4 digit number (betahaus.emaillogin just
          authenticates directly against the member email addresses) so can't use
          that argument unfortunately :(
          
          -Tim
          
          > 
          > Hedley
          > 
          > 
          > --
          > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244110430264
          > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
          > 
          
          
          • Re: Overriding the id/username generation

            from hedley on Jun 04, 2009 06:34 AM
            > Good thinking 99 ;)
            > but unfortunately the client has asked me to autogenerate the usernames
            > based on a sequential 4 digit number (betahaus.emaillogin just
            > authenticates directly against the member email addresses) so can't use
            > that argument unfortunately :(
            > 
            
            For your case I'll use a formlib add form. You don't have to write any 
            UI! Roche came up with this neat approach.
            
            For this example the content type class is called AccountingFolder but 
            you'll see the pattern.
            
            ZCML:
            
              <browser:page
                   name="addAccountingFolder"   <<< name must match the factory
                   for="*"
                   class=".adding.AccountingFolderAddForm"
                   permission="zope.View"
                   />
            
            
            Class:
            
            class AccountingFolderAddForm(formbase.PageAddForm):
                 """
                 """
            
                 form_fields = form.FormFields(IAccountingFolder)
            
                 def create(self, data):
                     title = data['title']
                     id = <your algorithm here>
                     self.contentName = id
            
                     folder = AccountingFolder(id, **data)
                     folder.setTitle(title)
                     return folder
            
                 def nextURL(self):
                     obj = self.context.get(self.contentName)
                     return "%s/edit" % obj.absolute_url()
            
            I haven't used this myself but I've seen it in action and it is pretty 
            damn cool :)
            
            BTW you'll need to modify the member schema. So you can use 
            schemaextender or monkey it, but you'll need this:
            
            schema['id'].regfield = 0
            schema['id'].display_autogenerated = 1
            
            Hedley
            
            • Re: Overriding the id/username generation

              from duffyd on Jun 04, 2009 03:40 PM
              On Thu, 2009-06-04 at 12:33 +0200, Hedley Roos wrote:
              > > Good thinking 99 ;)
              > > but unfortunately the client has asked me to autogenerate the usernames
              > > based on a sequential 4 digit number (betahaus.emaillogin just
              > > authenticates directly against the member email addresses) so can't use
              > > that argument unfortunately :(
              > > 
              > 
              > For your case I'll use a formlib add form. You don't have to write any 
              > UI! Roche came up with this neat approach.
              > 
              > For this example the content type class is called AccountingFolder but 
              > you'll see the pattern.
              > 
              > ZCML:
              > 
              >   <browser:page
              >        name="addAccountingFolder"   <<< name must match the factory
              >        for="*"
              >        class=".adding.AccountingFolderAddForm"
              >        permission="zope.View"
              >        />
              > 
              > 
              > Class:
              > 
              > class AccountingFolderAddForm(formbase.PageAddForm):
              >      """
              >      """
              > 
              >      form_fields = form.FormFields(IAccountingFolder)
              > 
              >      def create(self, data):
              >          title = data['title']
              >          id = <your algorithm here>
              >          self.contentName = id
              > 
              >          folder = AccountingFolder(id, **data)
              >          folder.setTitle(title)
              >          return folder
              > 
              >      def nextURL(self):
              >          obj = self.context.get(self.contentName)
              >          return "%s/edit" % obj.absolute_url()
              > 
              > I haven't used this myself but I've seen it in action and it is pretty 
              > damn cool :)
              > 
              > BTW you'll need to modify the member schema. So you can use 
              > schemaextender or monkey it, but you'll need this:
              > 
              > schema['id'].regfield = 0
              > schema['id'].display_autogenerated = 1
              
              Thank you very much! This looks very promising. I'll give it a go today
              and report back.
              
              Thanks again,
              Tim
              
              > 
              > Hedley
              > 
              > 
              > --
              > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244111662728
              > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
              > 
              
              
            • Re: Overriding the id/username generation

              from duffyd on Jun 05, 2009 02:10 AM
              Hello again,
              
              I've implemented these changes as per your suggestion below and put a
              pdb statement in the create method of the PageAddForm class but it
              doesn't even get there and just fails. Are you sure you implemented this
              for a remember-based class?
              
              Thanks again,
              Tim
              
              On Thu, 2009-06-04 at 12:33 +0200, Hedley Roos wrote:
              > > Good thinking 99 ;)
              > > but unfortunately the client has asked me to autogenerate the usernames
              > > based on a sequential 4 digit number (betahaus.emaillogin just
              > > authenticates directly against the member email addresses) so can't use
              > > that argument unfortunately :(
              > > 
              > 
              > For your case I'll use a formlib add form. You don't have to write any 
              > UI! Roche came up with this neat approach.
              > 
              > For this example the content type class is called AccountingFolder but 
              > you'll see the pattern.
              > 
              > ZCML:
              > 
              >   <browser:page
              >        name="addAccountingFolder"   <<< name must match the factory
              >        for="*"
              >        class=".adding.AccountingFolderAddForm"
              >        permission="zope.View"
              >        />
              > 
              > 
              > Class:
              > 
              > class AccountingFolderAddForm(formbase.PageAddForm):
              >      """
              >      """
              > 
              >      form_fields = form.FormFields(IAccountingFolder)
              > 
              >      def create(self, data):
              >          title = data['title']
              >          id = <your algorithm here>
              >          self.contentName = id
              > 
              >          folder = AccountingFolder(id, **data)
              >          folder.setTitle(title)
              >          return folder
              > 
              >      def nextURL(self):
              >          obj = self.context.get(self.contentName)
              >          return "%s/edit" % obj.absolute_url()
              > 
              > I haven't used this myself but I've seen it in action and it is pretty 
              > damn cool :)
              > 
              > BTW you'll need to modify the member schema. So you can use 
              > schemaextender or monkey it, but you'll need this:
              > 
              > schema['id'].regfield = 0
              > schema['id'].display_autogenerated = 1
              > 
              > Hedley
              > 
              > 
              > --
              > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244111662728
              > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
              > 
              
              
              • Re: Overriding the id/username generation

                from hedley on Jun 05, 2009 03:33 AM
                Tim Knapp wrote:
                > Hello again,
                > 
                > I've implemented these changes as per your suggestion below and put a
                > pdb statement in the create method of the PageAddForm class but it
                > doesn't even get there and just fails. Are you sure you implemented this
                > for a remember-based class?
                > 
                
                Hi Tim
                
                It was not implemented for a remember based class, but since member is 
                basically a BaseObject subclass I assume it should work. Then again, 
                assumption is the mother of all... you know what.
                
                I'll try it myself a bit later if I have time.
                
                Hedley
                
                • Re: Overriding the id/username generation

                  from duffyd on Jun 05, 2009 04:15 AM
                  On Fri, 2009-06-05 at 09:32 +0200, Hedley Roos wrote:
                  > Tim Knapp wrote:
                  > > Hello again,
                  > > 
                  > > I've implemented these changes as per your suggestion below and put a
                  > > pdb statement in the create method of the PageAddForm class but it
                  > > doesn't even get there and just fails. Are you sure you implemented this
                  > > for a remember-based class?
                  > > 
                  > 
                  > Hi Tim
                  > 
                  > It was not implemented for a remember based class, but since member is 
                  > basically a BaseObject subclass I assume it should work. Then again, 
                  > assumption is the mother of all... you know what.
                  > 
                  > I'll try it myself a bit later if I have time.
                  
                  Well after some more hunting through the code it appears that the
                  creation of the remember objects is invoked via the createMember.cpy
                  script, which creates a member object via portal_factory in the
                  portal_memberdata tool, and returns reg_form.cpt (hence circumventing
                  the browser view), and then eventually calls another script
                  (do_register.cpy), which does:
                  
                  <code>
                  new_context = getToolByName(context, 'portal_factory').doCreate(context,
                  id)
                  new_context.processForm()
                  </code>
                  
                  so IOW it completely bypasses the formlib PageAddForm we created.
                  
                  Thanks,
                  Tim
                  
                  > 
                  > Hedley
                  > 
                  > 
                  > --
                  > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244187196967
                  > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
                  > 
                  
                  
                  • Re: Overriding the id/username generation

                    from duffyd on Jun 05, 2009 06:25 AM
                    On Fri, 2009-06-05 at 20:14 +1200, Tim Knapp wrote:
                    
                    <snip />
                    
                    > Well after some more hunting through the code it appears that the
                    > creation of the remember objects is invoked via the createMember.cpy
                    > script, which creates a member object via portal_factory in the
                    > portal_memberdata tool, and returns reg_form.cpt (hence circumventing
                    > the browser view), and then eventually calls another script
                    > (do_register.cpy), which does:
                    > 
                    > <code>
                    > new_context = getToolByName(context, 'portal_factory').doCreate(context,
                    > id)
                    > new_context.processForm()
                    > </code>
                    > 
                    > so IOW it completely bypasses the formlib PageAddForm we created.
                    
                    After some 'more' hacking, I have got the portal_factory to create the
                    object using my auto-generated id by doing the following:
                         1. Removing 'validate_registration' from reg_form.cpt.metadata
                            (validators) - I think the better way would possibly be to
                            ensure validate_id method in the member class is returning None
                            (still need to confirm this).
                         2. Modifying do_register.cpy as follows[1].
                    
                    Unfortunately it still errors as follows[2]. I can confirm that it has
                    definitely renamed the object but it appears to be trying to traverse to
                    the old id. Any ideas on how to get around this?
                    
                    Thanks again,
                    Tim
                    
                    [1] http://duffyd.pastebin.com/f63609c7e
                    [2] http://duffyd.pastebin.com/f540d2840
                    
                    > 
                    > Thanks,
                    > Tim
                    > 
                    > > 
                    > > Hedley
                    > > 
                    > > 
                    > > --
                    > > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244187196967
                    > > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
                    > > 
                    > 
                    > 
                    > 
                    > --
                    > Archive: http://www.openplans.org/projects/remember/lists/remember/archive/2009/06/1244189723212
                    > To unsubscribe send an email with subject "unsubscribe" to remember@....  Please contact remember-manager@... for questions.
                    >