* Updates

8278/8287, 10374/10394, 11391/11427 - Work around Google list members bug that returns owners/managers as members

10246/10274, 10589/10687 - add query <GroupQuery> to print groups/group-members

* Update documantation

* Update documentation
This commit is contained in:
Ross Scroggs
2018-07-04 10:03:09 -07:00
committed by Jay Lee
parent 8e7bd453f4
commit 74514b487d
2 changed files with 74 additions and 34 deletions

View File

@ -323,11 +323,13 @@ Named items
allowgooglecommunication| allowgooglecommunication|
allowwebposting| allowwebposting|
archiveonly| archiveonly|
collaborative|
customreplyto| customreplyto|
defaultmessagedenynotificationtext| defaultmessagedenynotificationtext|
description| description|
directmemberscount| directmemberscount|
email| email|
favoriterepliesontop|
id| id|
includeinglobaladdresslist|gal| includeinglobaladdresslist|gal|
isarchived| isarchived|
@ -342,11 +344,22 @@ Named items
showingroupdirectory| showingroupdirectory|
spammoderationlevel| spammoderationlevel|
whocanadd| whocanadd|
whocanaddreferences|
whocanassigntopics|
whocancontactowner| whocancontactowner|
whocanenterfreeformtags|
whocaninvite| whocaninvite|
whocanjoin| whocanjoin|
whocanleavegroup| whocanleavegroup|
whocanmarkduplicate|
whocanmarkfavoritereplyonanytopic|
whocanmarkfavoritereplyonowntopic|
whocanmarknoresponseneeded|
whocanmodifytagsandcategories|
whocanpostmessage| whocanpostmessage|
whocantaketopics|
whocanunassigntopic|
whocanunmarkfavoritereplyonanytopic
whocanviewgroup| whocanviewgroup|
whocanviewmembership whocanviewmembership
@ -603,10 +616,12 @@ Item attributes
(allowgooglecommunication <Boolean>)| (allowgooglecommunication <Boolean>)|
(allowwebposting <Boolean>)| (allowwebposting <Boolean>)|
(archiveonly <Boolean>)| (archiveonly <Boolean>)|
(collaborative ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(customfootertext <String>)| (customfootertext <String>)|
(customreplyto <EmailAddress>)| (customreplyto <EmailAddress>)|
(defaultmessagedenynotificationtext <String>)| (defaultmessagedenynotificationtext <String>)|
(description <String>)| (description <String>)|
(favoriterepliesontop <Boolean>)|
(gal|includeInGlobalAddressList <Boolean>)| (gal|includeInGlobalAddressList <Boolean>)|
(includecustomfooter <Boolean>)| (includecustomfooter <Boolean>)|
(isarchived <Boolean>)| (isarchived <Boolean>)|
@ -621,11 +636,22 @@ Item attributes
(showingroupdirectory <Boolean>)| (showingroupdirectory <Boolean>)|
(spammoderationlevel ALLOW|MODERATE|SILENTLY_MODERATE|REJECT)| (spammoderationlevel ALLOW|MODERATE|SILENTLY_MODERATE|REJECT)|
(whocanadd ALL_MEMBERS_CAN_ADD|ALL_MANAGERS_CAN_ADD|NONE_CAN_ADD)| (whocanadd ALL_MEMBERS_CAN_ADD|ALL_MANAGERS_CAN_ADD|NONE_CAN_ADD)|
(whocanaddreferences ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanassigntopics ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocancontactowner ANYONE_CAN_CONTACT|ALL_IN_DOMAIN_CAN_CONTACT|ALL_MEMBERS_CAN_CONTACT|ALL_MANAGERS_CAN_CONTACT)| (whocancontactowner ANYONE_CAN_CONTACT|ALL_IN_DOMAIN_CAN_CONTACT|ALL_MEMBERS_CAN_CONTACT|ALL_MANAGERS_CAN_CONTACT)|
(whocanenterfreeformtags ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocaninvite ALL_MEMBERS_CAN_INVITE|ALL_MANAGERS_CAN_INVITE|NONE_CAN_INVITE)| (whocaninvite ALL_MEMBERS_CAN_INVITE|ALL_MANAGERS_CAN_INVITE|NONE_CAN_INVITE)|
(whocanjoin ANYONE_CAN_JOIN|ALL_IN_DOMAIN_CAN_JOIN|INVITED_CAN_JOIN|CAN_REQUEST_TO_JOIN)| (whocanjoin ANYONE_CAN_JOIN|ALL_IN_DOMAIN_CAN_JOIN|INVITED_CAN_JOIN|CAN_REQUEST_TO_JOIN)|
(whocanleavegroup ALL_MANAGERS_CAN_LEAVE|ALL_MEMBERS_CAN_LEAVE|NONE_CAN_LEAVE)| (whocanleavegroup ALL_MANAGERS_CAN_LEAVE|ALL_MEMBERS_CAN_LEAVE|NONE_CAN_LEAVE)|
(whocanmarkduplicate ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanmarkfavoritereplyonanytopic ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanmarkfavoritereplyonowntopic ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanmarknoresponseneeded ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanmodifytagsandcategories ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocantaketopics ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanpostmessage NONE_CAN_POST|ALL_MANAGERS_CAN_POST|ALL_MEMBERS_CAN_POST|ALL_IN_DOMAIN_CAN_POST|ANYONE_CAN_POST)| (whocanpostmessage NONE_CAN_POST|ALL_MANAGERS_CAN_POST|ALL_MEMBERS_CAN_POST|ALL_IN_DOMAIN_CAN_POST|ANYONE_CAN_POST)|
(whocanunassigntopic ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanunmarkfavoritereplyonanytopic ALL_MEMBERS|OWNERS_AND_MANAGERS|MANAGERS_ONLY|OWNERS_ONLY|NONE)|
(whocanviewgroup ANYONE_CAN_VIEW|ALL_IN_DOMAIN_CAN_VIEW|ALL_MEMBERS_CAN_VIEW|ALL_MANAGERS_CAN_VIEW)| (whocanviewgroup ANYONE_CAN_VIEW|ALL_IN_DOMAIN_CAN_VIEW|ALL_MEMBERS_CAN_VIEW|ALL_MANAGERS_CAN_VIEW)|
(whocanviewmembership ALL_IN_DOMAIN_CAN_VIEW|ALL_MEMBERS_CAN_VIEW|ALL_MANAGERS_CAN_VIEW) (whocanviewmembership ALL_IN_DOMAIN_CAN_VIEW|ALL_MEMBERS_CAN_VIEW|ALL_MANAGERS_CAN_VIEW)
@ -828,10 +854,10 @@ gam calendar <CalendarItem> deleteevent (id|eventid <EventID>)* (query|eventquer
gam calendar <CalendarItem> wipe gam calendar <CalendarItem> wipe
<CalendarSettings> ::= <CalendarSettings> ::=
summary <String>| summary <String>|
description <String>| description <String>|
location <String>| location <String>|
timezone <String> timezone <String>
gam calendar <CalendarItem> modify <CalendarSettings>+ gam calendar <CalendarItem> modify <CalendarSettings>+
@ -907,12 +933,12 @@ gam update group <GroupItem> clear [member] [manager] [owner] [suspended]
gam delete group <GroupItem> gam delete group <GroupItem>
gam info group <GroupItem> [nousers] [noaliases] [groups] gam info group <GroupItem> [nousers] [noaliases] [groups]
gam print groups [todrive] ([domain <DomainName>] [member <UserItem>]) gam print groups [todrive] ([domain <DomainName>] ([member <UserItem>]|[query <QueryGroup>])
[maxresults <Number>] [allfields|([settings] <GroupFieldName>* [fields <GroupFieldNameList>])] [maxresults <Number>] [allfields|([settings] <GroupFieldName>* [fields <GroupFieldNameList>])]
[members|memberscount] [managers|managerscount] [owners|ownerscount] [members|memberscount] [managers|managerscount] [owners|ownerscount]
[delimiter <Character>] [sortheaders] [delimiter <Character>] [sortheaders]
gam print group-members|groups-members [todrive] ([domain <DomainName>] [member <UserItem>])|[group <GroupItem>] gam print group-members|groups-members [todrive] ([domain <DomainName>] ([member <UserItem>]|[query <QueryGroup>]))|[group <GroupItem>]
[roles <GroupRoleList>] [membernames] [fields <MembersFieldNameList>] [roles <GroupRoleList>] [membernames] [fields <MembersFieldNameList>]
gam print license|licenses|licence|licences [todrive] [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)] gam print license|licenses|licence|licences [todrive] [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]

View File

@ -8275,16 +8275,16 @@ def doUpdateGroup():
member_type_message = u'%ss' % roles.lower() member_type_message = u'%ss' % roles.lower()
sys.stderr.write(u"Getting %s of %s (may take some time for large groups)...\n" % (member_type_message, group)) sys.stderr.write(u"Getting %s of %s (may take some time for large groups)...\n" % (member_type_message, group))
page_message = u'Got %%%%total_items%%%% %s...' % member_type_message page_message = u'Got %%%%total_items%%%% %s...' % member_type_message
validRoles, listRoles, listFields = _getRoleVerification(roles, u'nextPageToken,members({0})'.format(u','.join(fields)))
try: try:
result = callGAPIpages(cd.members(), u'list', u'members', result = callGAPIpages(cd.members(), u'list', u'members',
page_message=page_message, page_message=page_message,
throw_reasons=GAPI_MEMBERS_THROW_REASONS, throw_reasons=GAPI_MEMBERS_THROW_REASONS,
groupKey=group, roles=roles, fields=u'nextPageToken,members({0})'.format(u','.join(fields)), groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
if not suspended: if not suspended:
users_email = [member.get(u'email', member[u'id']) for member in result] users_email = [member.get(u'email', member[u'id']) for member in result if not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles]
else: else:
users_email = [member.get(u'email', member[u'id']) for member in result if member[u'status'] == u'SUSPENDED'] users_email = [member.get(u'email', member[u'id']) for member in result if (not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles) and member[u'status'] == u'SUSPENDED']
if len(users_email) > 1: if len(users_email) > 1:
sys.stderr.write(u'Group: {0}, Will remove {1} {2}{3}s.\n'.format(group, len(users_email), [u'', u'suspended '][suspended], roles)) sys.stderr.write(u'Group: {0}, Will remove {1} {2}{3}s.\n'.format(group, len(users_email), [u'', u'suspended '][suspended], roles))
for user_email in users_email: for user_email in users_email:
@ -10243,7 +10243,7 @@ def doPrintGroups():
i = 3 i = 3
members = membersCountOnly = managers = managersCountOnly = owners = ownersCountOnly = False members = membersCountOnly = managers = managersCountOnly = owners = ownersCountOnly = False
customer = GC_Values[GC_CUSTOMER_ID] customer = GC_Values[GC_CUSTOMER_ID]
usedomain = usemember = None usedomain = usemember = usequery = None
aliasDelimiter = u' ' aliasDelimiter = u' '
memberDelimiter = u'\n' memberDelimiter = u'\n'
todrive = False todrive = False
@ -10267,7 +10267,11 @@ def doPrintGroups():
i += 2 i += 2
elif myarg == u'member': elif myarg == u'member':
usemember = normalizeEmailAddressOrUID(sys.argv[i+1]) usemember = normalizeEmailAddressOrUID(sys.argv[i+1])
customer = None customer = usequery = None
i += 2
elif myarg == u'query':
usequery = sys.argv[i+1]
usemember = None
i += 2 i += 2
elif myarg == u'maxresults': elif myarg == u'maxresults':
maxResults = int(sys.argv[i+1]) maxResults = int(sys.argv[i+1])
@ -10349,7 +10353,7 @@ def doPrintGroups():
page_message = u'Got %%num_items%% Groups: %%first_item%% - %%last_item%%\n' page_message = u'Got %%num_items%% Groups: %%first_item%% - %%last_item%%\n'
entityList = callGAPIpages(cd.groups(), u'list', u'groups', entityList = callGAPIpages(cd.groups(), u'list', u'groups',
page_message=page_message, message_attribute=u'email', page_message=page_message, message_attribute=u'email',
customer=customer, domain=usedomain, userKey=usemember, customer=customer, domain=usedomain, userKey=usemember, query=usequery,
fields=u'nextPageToken,groups({0})'.format(cdfields), fields=u'nextPageToken,groups({0})'.format(cdfields),
maxResults=maxResults) maxResults=maxResults)
i = 0 i = 0
@ -10367,11 +10371,11 @@ def doPrintGroups():
if roles: if roles:
sys.stderr.write(u' Getting %s for %s (%s/%s)\n' % (roles, groupEmail, i, count)) sys.stderr.write(u' Getting %s for %s (%s/%s)\n' % (roles, groupEmail, i, count))
page_message = u' Got %%num_items%% members: %%first_item%% - %%last_item%%\n' page_message = u' Got %%num_items%% members: %%first_item%% - %%last_item%%\n'
validRoles, listRoles, listFields = _getRoleVerification(roles, u'nextPageToken,members(email,id,role)')
groupMembers = callGAPIpages(cd.members(), u'list', u'members', groupMembers = callGAPIpages(cd.members(), u'list', u'members',
page_message=page_message, message_attribute=u'email', page_message=page_message, message_attribute=u'email',
soft_errors=True, soft_errors=True,
groupKey=groupEmail, roles=roles, fields=u'nextPageToken,members(email,id,role)', groupKey=groupEmail, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
if members: if members:
membersList = [] membersList = []
membersCount = 0 membersCount = 0
@ -10386,8 +10390,8 @@ def doPrintGroups():
if not member_email: if not member_email:
sys.stderr.write(u' Not sure what to do with: %s' % member) sys.stderr.write(u' Not sure what to do with: %s' % member)
continue continue
role = member.get(u'role', None) role = member.get(u'role', ROLE_MEMBER)
if role: if not validRoles or role in validRoles:
if role == ROLE_MEMBER: if role == ROLE_MEMBER:
if members: if members:
membersCount += 1 membersCount += 1
@ -10407,10 +10411,6 @@ def doPrintGroups():
membersCount += 1 membersCount += 1
if not membersCountOnly: if not membersCountOnly:
membersList.append(member_email) membersList.append(member_email)
elif members:
membersCount += 1
if not membersCountOnly:
membersList.append(member_email)
if members: if members:
group[u'MembersCount'] = membersCount group[u'MembersCount'] = membersCount
if not membersCountOnly: if not membersCountOnly:
@ -10586,8 +10586,7 @@ def doPrintGroupMembers():
todrive = False todrive = False
membernames = False membernames = False
customer = GC_Values[GC_CUSTOMER_ID] customer = GC_Values[GC_CUSTOMER_ID]
usedomain = None usedomain = usemember = usequery = None
usemember = None
roles = [] roles = []
fields = None fields = None
titles = [u'group'] titles = [u'group']
@ -10596,16 +10595,20 @@ def doPrintGroupMembers():
i = 3 i = 3
while i < len(sys.argv): while i < len(sys.argv):
myarg = sys.argv[i].lower() myarg = sys.argv[i].lower()
if myarg == u'domain': if myarg == u'todrive':
todrive = True
i += 1
elif myarg == u'domain':
usedomain = sys.argv[i+1].lower() usedomain = sys.argv[i+1].lower()
customer = None customer = None
i += 2 i += 2
elif myarg == u'todrive':
todrive = True
i += 1
elif myarg == u'member': elif myarg == u'member':
usemember = normalizeEmailAddressOrUID(sys.argv[i+1]) usemember = normalizeEmailAddressOrUID(sys.argv[i+1])
customer = None customer = usequery = None
i += 2
elif myarg == u'query':
usequery = sys.argv[i+1]
usemember = None
i += 2 i += 2
elif myarg == u'fields': elif myarg == u'fields':
memberFieldsList = sys.argv[i+1].replace(u',', u' ').lower().split() memberFieldsList = sys.argv[i+1].replace(u',', u' ').lower().split()
@ -10630,7 +10633,8 @@ def doPrintGroupMembers():
systemErrorExit(2, '%s is not a valid argument for "gam print group-members"' % sys.argv[i]) systemErrorExit(2, '%s is not a valid argument for "gam print group-members"' % sys.argv[i])
if not groups_to_get: if not groups_to_get:
groups_to_get = callGAPIpages(cd.groups(), u'list', u'groups', message_attribute=u'email', groups_to_get = callGAPIpages(cd.groups(), u'list', u'groups', message_attribute=u'email',
customer=customer, domain=usedomain, userKey=usemember, fields=u'nextPageToken,groups(email)') customer=customer, domain=usedomain, userKey=usemember, query=usequery,
fields=u'nextPageToken,groups(email)')
i = 0 i = 0
count = len(groups_to_get) count = len(groups_to_get)
for group in groups_to_get: for group in groups_to_get:
@ -10638,12 +10642,14 @@ def doPrintGroupMembers():
group_email = group[u'email'] group_email = group[u'email']
sys.stderr.write(u'Getting members for %s (%s/%s)\n' % (group_email, i, count)) sys.stderr.write(u'Getting members for %s (%s/%s)\n' % (group_email, i, count))
group_members = callGAPIpages(cd.members(), u'list', u'members', group_members = callGAPIpages(cd.members(), u'list', u'members',
soft_errors=True, roles=u','.join(roles), soft_errors=True,
groupKey=group_email, fields=fields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS]) groupKey=group_email, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
for member in group_members: for member in group_members:
for unwanted_item in [u'kind', u'etag']: for unwanted_item in [u'kind', u'etag']:
if unwanted_item in member: if unwanted_item in member:
del member[unwanted_item] del member[unwanted_item]
if validRoles and member.get(u'role', ROLE_MEMBER) not in validRoles:
continue
for title in member: for title in member:
if title not in titles: if title not in titles:
titles.append(title) titles.append(title)
@ -11382,6 +11388,12 @@ def getQueries(myarg, argstr):
else: else:
return shlexSplitList(argstr) return shlexSplitList(argstr)
def _getRoleVerification(memberRoles, fields):
if memberRoles and memberRoles.find(ROLE_MEMBER) != -1:
return (set(memberRoles.split(u',')), None, fields if fields.find(u'role') != -1 else fields[:-1]+u',role)')
else:
return (set(), memberRoles, fields)
def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=None, checkNotSuspended=False, groupUserMembersOnly=True): def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=None, checkNotSuspended=False, groupUserMembersOnly=True):
got_uids = False got_uids = False
if entity_type is None: if entity_type is None:
@ -11405,12 +11417,14 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
if not silent: if not silent:
sys.stderr.write(u"Getting %s of %s (may take some time for large groups)...\n" % (member_type_message, group)) sys.stderr.write(u"Getting %s of %s (may take some time for large groups)...\n" % (member_type_message, group))
page_message = u'Got %%%%total_items%%%% %s...' % member_type_message page_message = u'Got %%%%total_items%%%% %s...' % member_type_message
validRoles, listRoles, listFields = _getRoleVerification(member_type, u'nextPageToken,members(email,id,type,status)')
members = callGAPIpages(cd.members(), u'list', u'members', page_message=page_message, members = callGAPIpages(cd.members(), u'list', u'members', page_message=page_message,
groupKey=group, roles=member_type, fields=u'nextPageToken,members(email,id,type,status)', groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
users = [] users = []
for member in members: for member in members:
if ((not groupUserMembersOnly) or (member[u'type'] == u'USER')) and not (checkNotSuspended and (member[u'status'] == u'SUSPENDED')): if (((not groupUserMembersOnly) or (member[u'type'] == u'USER')) and
(not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles) and
not (checkNotSuspended and (member[u'status'] == u'SUSPENDED'))):
users.append(member.get(u'email', member[u'id'])) users.append(member.get(u'email', member[u'id']))
elif entity_type in [u'ou', u'org']: elif entity_type in [u'ou', u'org']:
got_uids = True got_uids = True