prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Group administration: AttributeError: 'NoneType' object has no attribute 'getGroupTitleOrName'
  /Plone2/@@usergroup-groupmembership?groupname=None
  
  gives me
  
  Here is the full error message:
  
  Display traceback as text
  
  Traceback (innermost last):
  
  Module ZPublisher.Publish, line 138, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module ZPublisher.Publish, line 48, in call_object
  Module Products.CMFPlone.controlpanel.browser.usergroups_groupmembership, line 69, in **call**
  Module Products.CMFPlone.controlpanel.browser.usergroups_groupmembership, line 16, in update
  AttributeError: 'NoneType' object has no attribute 'getGroupTitleOrName'
  
  This happens when you click on "new group" and then on the "group members" tab.
  
  
  </issue>
  <code>
  [start of Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py]
  1 from Products.CMFPlone import PloneMessageFactory as _
  2 from zExceptions import Forbidden
  3 from Products.CMFCore.utils import getToolByName
  4 from Products.CMFPlone.controlpanel.browser.usergroups import \
  5     UsersGroupsControlPanelView
  6 from Products.CMFPlone.utils import normalizeString
  7 
  8 
  9 class GroupMembershipControlPanel(UsersGroupsControlPanelView):
  10 
  11     def update(self):
  12         self.groupname = getattr(self.request, 'groupname')
  13         self.gtool = getToolByName(self, 'portal_groups')
  14         self.mtool = getToolByName(self, 'portal_membership')
  15         self.group = self.gtool.getGroupById(self.groupname)
  16         self.grouptitle = self.group.getGroupTitleOrName() or self.groupname
  17 
  18         self.request.set('grouproles', self.group.getRoles() if self.group else [])
  19         self.canAddUsers = True
  20         if 'Manager' in self.request.get('grouproles') and not self.is_zope_manager:
  21             self.canAddUsers = False
  22 
  23         self.groupquery = self.makeQuery(groupname=self.groupname)
  24         self.groupkeyquery = self.makeQuery(key=self.groupname)
  25 
  26         form = self.request.form
  27         submitted = form.get('form.submitted', False)
  28 
  29         self.searchResults = []
  30         self.searchString = ''
  31         self.newSearch = False
  32 
  33         if submitted:
  34             # add/delete before we search so we don't show stale results
  35             toAdd = form.get('add', [])
  36             if toAdd:
  37                 if not self.canAddUsers:
  38                     raise Forbidden
  39 
  40                 for u in toAdd:
  41                     self.gtool.addPrincipalToGroup(u, self.groupname, self.request)
  42                 self.context.plone_utils.addPortalMessage(_(u'Changes made.'))
  43 
  44             toDelete = form.get('delete', [])
  45             if toDelete:
  46                 for u in toDelete:
  47                     self.gtool.removePrincipalFromGroup(u, self.groupname, self.request)
  48                 self.context.plone_utils.addPortalMessage(_(u'Changes made.'))
  49 
  50             search = form.get('form.button.Search', None) is not None
  51             edit = form.get('form.button.Edit', None) is not None and toDelete
  52             add = form.get('form.button.Add', None) is not None and toAdd
  53             findAll = form.get('form.button.FindAll', None) is not None and \
  54                 not self.many_users
  55             # The search string should be cleared when one of the
  56             # non-search buttons has been clicked.
  57             if findAll or edit or add:
  58                 form['searchstring'] = ''
  59             self.searchString = form.get('searchstring', '')
  60             if findAll or bool(self.searchString):
  61                 self.searchResults = self.getPotentialMembers(self.searchString)
  62 
  63             if search or findAll:
  64                 self.newSearch = True
  65 
  66         self.groupMembers = self.getMembers()
  67 
  68     def __call__(self):
  69         self.update()
  70         return self.index()
  71 
  72     def isGroup(self, itemName):
  73         return self.gtool.isGroup(itemName)
  74 
  75     def getMembers(self):
  76         searchResults = self.gtool.getGroupMembers(self.groupname)
  77 
  78         groupResults = [self.gtool.getGroupById(m) for m in searchResults]
  79         groupResults.sort(key=lambda x: x is not None and normalizeString(x.getGroupTitleOrName()))
  80 
  81         userResults = [self.mtool.getMemberById(m) for m in searchResults]
  82         userResults.sort(key=lambda x: x is not None and x.getProperty('fullname') is not None and normalizeString(x.getProperty('fullname')) or '')
  83 
  84         mergedResults = groupResults + userResults
  85         return filter(None, mergedResults)
  86 
  87     def getPotentialMembers(self, searchString):
  88         ignoredUsersGroups = [x.id for x in self.getMembers() + [self.group,] if x is not None]
  89         return self.membershipSearch(searchString, ignore=ignoredUsersGroups)
  90
  [end of Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py
  --- a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py
  +++ b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py
  @@ -13,6 +13,9 @@
           self.gtool = getToolByName(self, 'portal_groups')
           self.mtool = getToolByName(self, 'portal_membership')
           self.group = self.gtool.getGroupById(self.groupname)
  +        if self.group is None:
  +            return
  +
           self.grouptitle = self.group.getGroupTitleOrName() or self.groupname
   
           self.request.set('grouproles', self.group.getRoles() if self.group else [])
  
verification_info: {"golden_diff": "diff --git a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py\n--- a/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py\n+++ b/Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py\n@@ -13,6 +13,9 @@\n         self.gtool = getToolByName(self, 'portal_groups')\n         self.mtool = getToolByName(self, 'portal_membership')\n         self.group = self.gtool.getGroupById(self.groupname)\n+        if self.group is None:\n+            return\n+\n         self.grouptitle = self.group.getGroupTitleOrName() or self.groupname\n \n         self.request.set('grouproles', self.group.getRoles() if self.group else [])\n", "issue": "Group administration: AttributeError: 'NoneType' object has no attribute 'getGroupTitleOrName'\n/Plone2/@@usergroup-groupmembership?groupname=None\n\ngives me\n\nHere is the full error message:\n\nDisplay traceback as text\n\nTraceback (innermost last):\n\nModule ZPublisher.Publish, line 138, in publish\nModule ZPublisher.mapply, line 77, in mapply\nModule ZPublisher.Publish, line 48, in call_object\nModule Products.CMFPlone.controlpanel.browser.usergroups_groupmembership, line 69, in **call**\nModule Products.CMFPlone.controlpanel.browser.usergroups_groupmembership, line 16, in update\nAttributeError: 'NoneType' object has no attribute 'getGroupTitleOrName'\n\nThis happens when you click on \"new group\" and then on the \"group members\" tab.\n\n", "before_files": [{"content": "from Products.CMFPlone import PloneMessageFactory as _\nfrom zExceptions import Forbidden\nfrom Products.CMFCore.utils import getToolByName\nfrom Products.CMFPlone.controlpanel.browser.usergroups import \\\n    UsersGroupsControlPanelView\nfrom Products.CMFPlone.utils import normalizeString\n\n\nclass GroupMembershipControlPanel(UsersGroupsControlPanelView):\n\n    def update(self):\n        self.groupname = getattr(self.request, 'groupname')\n        self.gtool = getToolByName(self, 'portal_groups')\n        self.mtool = getToolByName(self, 'portal_membership')\n        self.group = self.gtool.getGroupById(self.groupname)\n        self.grouptitle = self.group.getGroupTitleOrName() or self.groupname\n\n        self.request.set('grouproles', self.group.getRoles() if self.group else [])\n        self.canAddUsers = True\n        if 'Manager' in self.request.get('grouproles') and not self.is_zope_manager:\n            self.canAddUsers = False\n\n        self.groupquery = self.makeQuery(groupname=self.groupname)\n        self.groupkeyquery = self.makeQuery(key=self.groupname)\n\n        form = self.request.form\n        submitted = form.get('form.submitted', False)\n\n        self.searchResults = []\n        self.searchString = ''\n        self.newSearch = False\n\n        if submitted:\n            # add/delete before we search so we don't show stale results\n            toAdd = form.get('add', [])\n            if toAdd:\n                if not self.canAddUsers:\n                    raise Forbidden\n\n                for u in toAdd:\n                    self.gtool.addPrincipalToGroup(u, self.groupname, self.request)\n                self.context.plone_utils.addPortalMessage(_(u'Changes made.'))\n\n            toDelete = form.get('delete', [])\n            if toDelete:\n                for u in toDelete:\n                    self.gtool.removePrincipalFromGroup(u, self.groupname, self.request)\n                self.context.plone_utils.addPortalMessage(_(u'Changes made.'))\n\n            search = form.get('form.button.Search', None) is not None\n            edit = form.get('form.button.Edit', None) is not None and toDelete\n            add = form.get('form.button.Add', None) is not None and toAdd\n            findAll = form.get('form.button.FindAll', None) is not None and \\\n                not self.many_users\n            # The search string should be cleared when one of the\n            # non-search buttons has been clicked.\n            if findAll or edit or add:\n                form['searchstring'] = ''\n            self.searchString = form.get('searchstring', '')\n            if findAll or bool(self.searchString):\n                self.searchResults = self.getPotentialMembers(self.searchString)\n\n            if search or findAll:\n                self.newSearch = True\n\n        self.groupMembers = self.getMembers()\n\n    def __call__(self):\n        self.update()\n        return self.index()\n\n    def isGroup(self, itemName):\n        return self.gtool.isGroup(itemName)\n\n    def getMembers(self):\n        searchResults = self.gtool.getGroupMembers(self.groupname)\n\n        groupResults = [self.gtool.getGroupById(m) for m in searchResults]\n        groupResults.sort(key=lambda x: x is not None and normalizeString(x.getGroupTitleOrName()))\n\n        userResults = [self.mtool.getMemberById(m) for m in searchResults]\n        userResults.sort(key=lambda x: x is not None and x.getProperty('fullname') is not None and normalizeString(x.getProperty('fullname')) or '')\n\n        mergedResults = groupResults + userResults\n        return filter(None, mergedResults)\n\n    def getPotentialMembers(self, searchString):\n        ignoredUsersGroups = [x.id for x in self.getMembers() + [self.group,] if x is not None]\n        return self.membershipSearch(searchString, ignore=ignoredUsersGroups)\n", "path": "Products/CMFPlone/controlpanel/browser/usergroups_groupmembership.py"}]}