diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index 75a4f829..bc8ed4a5 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -10,6 +10,19 @@ Add the `-s` option to the end of the above commands to suppress creating the `g See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation +### 6.75.04 + +Added a command to print user counts by OrgUnit. By default, all users in the workspace are counted; +you can specify a domain to only count users in that domain. +``` +gam print usercountsbyorgunit [todrive *] + [domain ] +``` + +Added option `uploadattachments []` to `gam show messages|threads` that +causes GAM to upload all message attachments to the user's `My Drive`, the default, or to a specific folder. +The existing option `attachmentnamepattern ` can be used to select attachments to upload. + ### 6.75.03 Fixed bug in `gam batch|tbatch` where the line `sleep ` in the batch file caused the error: diff --git a/docs/How-to-Upgrade-from-Standard-GAM.md b/docs/How-to-Upgrade-from-Standard-GAM.md index c1188477..b0e3962e 100644 --- a/docs/How-to-Upgrade-from-Standard-GAM.md +++ b/docs/How-to-Upgrade-from-Standard-GAM.md @@ -335,7 +335,7 @@ writes the credentials into the file oauth2.txt. admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found -GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -1009,7 +1009,7 @@ writes the credentials into the file oauth2.txt. C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt C:\GAMADV-XTD3>gam version WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found -GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final Windows-10-10.0.17134 AMD64 diff --git a/docs/Users-Gmail-Messages-Threads.md b/docs/Users-Gmail-Messages-Threads.md index fe39cd88..71c3e98d 100644 --- a/docs/Users-Gmail-Messages-Threads.md +++ b/docs/Users-Gmail-Messages-Threads.md @@ -23,6 +23,7 @@ - [Print only options](#print-only-options) - [Show only options](#show-only-options) - [Download attachments](#download-attachments) + - [Upload attachments](#upload-attachments) - [Display messages sent by delegates for delegator](#display-messages-sent-by-delegates-for-delegator) - [User attribute `replace ` processing](Tag-Replace) @@ -180,6 +181,20 @@ Block emails between specific user groups (gdoc|ghtml )| (gcsdoc|gcshtml )| (emlfile [charset ])) + + ::= + ::= + ::= + ::= + + ::= + (parentid )| + (parentname )| + (anyownerparentname )| + (teamdriveparentid )| + (teamdriveparent )| + (teamdriveparentid teamdriveparentname )| + (teamdriveparent teamdriveparentname ) ``` ## Message queries with dates ``` @@ -595,6 +610,16 @@ By default, when downloading attachments, an existing local file will not be ove * `overwrite true` - Overwite an existing file * `overwrite false` - Do not overwite an existing file; add a numeric prefix and create a new file +## Upload attachments +These options are valid with `show'. + +By default, message attachments are not uploaded to Google Drive. +* `uploadattachments` - Upload message attachments +* `attachmentnamepattern ` - Limit the attachments uploaded to those whose names match `` + +By default, message attachments are uploaded to the root of the user's My Drive. +* `` - Specify an alternate location for the uploaded attachments + ## Display messages sent by delegates for delegator Display messages sent by a particular delegate for a delegator; the message is from the delegator but sent by the delegate. diff --git a/docs/Users.md b/docs/Users.md index acdb52e4..3449df98 100644 --- a/docs/Users.md +++ b/docs/Users.md @@ -37,6 +37,7 @@ - [Print user domain counts](#print-user-domain-counts) - [Print domain counts for users in a specific domain and/or selected by a query](#print-domain-counts-for-users-in-a-specific-domain-and-or-selected-by-a-query) - [Print domain counts for users specified by ``](#print-domain-counts-for-users-specified-by-usertypeentity) +- [Print user counts by OrgUnit](print-user-counts-by-orgunit) - [Print user list](#print-user-list) - [Display user counts](#display-user-counts) - [Verify domain membership]($verify-domain-membership) @@ -1242,6 +1243,15 @@ $ more UsersList.csv ["testuser1@domain.org", "testuser2@domain.org", "testuser3@domain.org", "testuser4@domain.org"] ``` +## Print user counts by OrgUnit +Display the count of archived, suspended and total users in each OrgUnit; display a grand total. + +By default, all users in the workspace are counted; you can specify a domain to only count users in that domain. +``` +gam print usercountsbyorgunit [todrive *] + [domain ] +``` + ## Display user counts Display the number of users in an entity. ``` diff --git a/docs/Version-and-Help.md b/docs/Version-and-Help.md index a550a1a5..27bfcb79 100644 --- a/docs/Version-and-Help.md +++ b/docs/Version-and-Help.md @@ -3,7 +3,7 @@ Print the current version of Gam with details ``` gam version -GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00 Print the current version of Gam with details and time offset information ``` gam version timeoffset -GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second Print the current version of Gam with extended details and SSL information ``` gam version extended -GAMADV-XTD3 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64 Path: /Users/Admin/bin/gamadv-xtd3 Version Check: Current: 5.35.08 - Latest: 6.75.03 + Latest: 6.75.04 echo $? 1 ``` @@ -72,7 +72,7 @@ echo $? Print the current version number without details ``` gam version simple -6.75.03 +6.75.04 ``` In Linux/MacOS you can do: ``` @@ -82,7 +82,7 @@ echo $VER Print the current version of Gam and address of this Wiki ``` gam help -GAM 6.75.03 - https://github.com/taers232c/GAMADV-XTD3 +GAM 6.75.04 - https://github.com/taers232c/GAMADV-XTD3 Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 0375148f..5194eb85 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -5529,6 +5529,11 @@ gam print users [todrive *] [formatjson [quotechar ]] [countsonly|countonly] [issuspended ] +Print user counts by OrgUnits + +gam print usercountsbyorgunit [todrive *] + [domain ] + Print lists of users gam print userlist [todrive *] @@ -7202,9 +7207,10 @@ gam show messages|threads [countsonly|positivecountsonly] [useronly] [headers all|] [dateheaderformat iso|rfc2822|] [dateheaderconverttimezone []] [showlabels] [delimiter ] [showbody] [showdate] [showsize] [showsnippet] - [showattachments [attachmentnamepattern ] [noshowtextplain]] - [saveattachments [attachmentnamepattern ]] - [targetfolder ] [overwrite []] + [[attachmentnamepattern ] + [showattachments [noshowtextplain]] + [saveattachments [targetfolder ] [overwrite []]] + [uploadattachments []]] gam print messages|threads [todrive *] (((query [querytime ]*) (matchlabel ) [or|and])* [quick|notquick] [max_to_print ] [includespamtrash])|(ids ) @@ -7212,7 +7218,8 @@ gam print messages|threads [todrive *] [countsonly|positivecountsonly] [useronly] [headers all|] [dateheaderformat iso|rfc2822| [dateheaderconverttimezone []]] [showlabels] [delimiter ] [showbody] [showdate] [showsize] [showsnippet] - [showattachments [attachmentnamepattern ]] + [[attachmentnamepattern ] + [showattachments [noshowtextplain]]] [convertcrnl] # Users - Gmail - Profile diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index 1224e40f..3ca9ac93 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -2,6 +2,19 @@ Merged GAM-Team version +6.75.04 + +Added a command to print user counts by OrgUnit. By default, all users in the workspace are counted; +you can specify a domain to only count users in that domain. +``` +gam print usercountsbyorgunit [todrive *] + [domain ] +``` + +Added option `uploadattachments []` to `gam show messages|threads` that +causes GAM to upload all message attachments to the user's `My Drive`, the default, or to a specific folder. +The existing option `attachmentnamepattern ` can be used to select attachments to upload. + 6.75.03 Fixed bug in `gam batch|tbatch` where the line `sleep ` in the batch file caused the error: diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 964967b9..b4b1d39f 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -11659,6 +11659,25 @@ def doCreateProject(): elif 'error' in status: systemErrorExit(2, status['error']+'\n') break +# Try to set policy on project to allow Service Account Key Upload +# orgp = getAPIService(API.ORGPOLICY, httpObj) +# projectParent = f"projects/{projectInfo['projectId']}" +# policyName = f'{projectParent}/policies/iam.disableServiceAccountKeyUpload' +# try: +# result = callGAPI(orgp.projects().policies(), 'get', +# throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED], +# name=policyName) +# if result['spec']['rules'][0]['enforce']: +# callGAPI(orgp.projects().policies(), 'patch', +# throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED], +# name=policyName, body={'spec': {'rules': [{'enforce': False}]}}, updateMask='policy.spec') +# except GAPI.notFound: +# callGAPI(orgp.projects().policies(), 'create', +# throwReasons=[GAPI.BAD_REQUEST, GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED], +# parent=projectParent, body={'name': policyName, 'spec': {'rules': [{'enforce': False}]}}) +# except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.permissionDenied): +# pass +# Create client_secrets.json and oauth2service.json _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo) # gam use project [] [] @@ -43033,6 +43052,7 @@ def doPrintUsers(entityList=None): _writeUserEntity(userEntity) def _updateDomainCounts(emailAddress): + nonlocal domainCounts atLoc = emailAddress.find('@') if atLoc == -1: dom = UNKNOWN @@ -43408,6 +43428,67 @@ def doPrintUserList(entityList): csvPF.WriteRow({'title': title, 'count': count, 'users': json.dumps(cleanJSON(entityList), ensure_ascii=False, sort_keys=True)}) csvPF.writeCSVfile('User List') +# gam print usercountsbyorgunit [todrive *] +# [domain ] +def doPrintUserCountsByOrgUnit(): + def _printUserCounts(title, v): + csvPF.WriteRow({'orgUnitPath': title, 'archived': v['archived'], 'active': v['active'], 'suspended': v['suspended'], 'total': v['total']}) + + USER_COUNTS_FIELDS = ['archived', 'active', 'suspended', 'total'] + USER_COUNTS_ZERO_FIELDS = {'archived': 0, 'active': 0, 'suspended': 0, 'total': 0} + cd = buildGAPIObject(API.DIRECTORY) + csvPF = CSVPrintFile(['orgUnitPath']+USER_COUNTS_FIELDS) + FJQC = FormatJSONQuoteChar(csvPF) + kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]} + while Cmd.ArgumentsRemaining(): + myarg = getArgument() + if myarg == 'todrive': + csvPF.GetTodriveParameters() + elif myarg == 'domain': + kwargs = {'domain': getString(Cmd.OB_DOMAIN_NAME)} + else: + FJQC.GetFormatJSONQuoteChar(myarg, False) + if 'domain' in kwargs: + printGettingAllEntityItemsForWhom(Ent.USER, kwargs['domain'], entityType=Ent.DOMAIN) + pageMessage = getPageMessageForWhom() + title = f"Total({kwargs['domain']})" + else: + printGettingAllAccountEntities(Ent.USER) + pageMessage = getPageMessage() + title = f"Total({kwargs['customer']})" + userCounts = {} + try: + result = callGAPIpages(cd.users(), 'list', 'users', + pageMessage=pageMessage, + throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND, GAPI.FORBIDDEN], + orderBy='email', fields='nextPageToken,users(orgUnitPath,archived,suspended)', + maxResults=GC.Values[GC.USER_MAX_RESULTS], **kwargs) + except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.domainNotFound): + if 'domain' in kwargs: + checkEntityDNEorAccessErrorExit(cd, Ent.DOMAIN, kwargs['domain']) + else: + checkEntityDNEorAccessErrorExit(cd, Ent.CUSTOMER_ID, kwargs['customer']) + return + for user in result: + orgUnitPath = user['orgUnitPath'] + if orgUnitPath not in userCounts: + userCounts[orgUnitPath] = USER_COUNTS_ZERO_FIELDS.copy() + if user['suspended'] or user['archived']: + if user['archived']: + userCounts[orgUnitPath]['archived'] += 1 + if user['suspended']: + userCounts[orgUnitPath]['suspended'] += 1 + else: + userCounts[orgUnitPath]['active'] += 1 + userCounts[orgUnitPath]['total'] += 1 + totalCounts = USER_COUNTS_ZERO_FIELDS.copy() + for k, v in sorted(iter(userCounts.items())): + _printUserCounts(k, v) + for f in USER_COUNTS_FIELDS: + totalCounts[f] += v[f] + _printUserCounts(title, totalCounts) + csvPF.writeCSVfile('User Counts by OrgUnit') + def isolateCIUserInvitatonsEmail(name): ''' converts long name into email address''' return name.split('/')[-1] @@ -67891,7 +67972,7 @@ def printShowMessagesThreads(users, entityType): if charset: printKeyValueList(['charset', charset]) - def _showSaveAttachments(messageId, payload, attachmentNamePattern): + def _showSaveAttachments(messageId, payload, attachmentNamePattern, j, jcount): for part in payload.get('parts', []): if 'attachmentId' in part['body']: for header in part['headers']: @@ -67906,7 +67987,7 @@ def printShowMessagesThreads(users, entityType): mg = CHARSET_NAME_PATTERN.match(header['value']) if mg: charset = mg.group(1) - if (part['mimeType'] == 'text/plain' and not noshow_text_plain) or save_attachments: + if (part['mimeType'] == 'text/plain' and not noshow_text_plain) or save_attachments or upload_attachments: try: result = callGAPI(gmail.users().messages().attachments(), 'get', throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND], @@ -67933,6 +68014,34 @@ def printShowMessagesThreads(users, entityType): else: entityActionFailedWarning([Ent.ATTACHMENT, filename], str(e)) Act.Set(action) + if upload_attachments: + filename = cleanFilename(attachmentName) + uploadAttachmentBody.update({'name': filename, 'mimeType': part['mimeType']}) + action = Act.Get() + Act.Set(Act.CREATE) + media_body = googleapiclient.http.MediaIoBaseUpload(io.BytesIO(base64.urlsafe_b64decode(str(result['data']))), mimetype=part['mimeType'], resumable=True) + try: + result = callGAPI(drive.files(), 'create', + throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS, + GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT, + GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR, GAPI.INTERNAL_ERROR, + GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED, + GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP, + GAPI.UPLOAD_TOO_LARGE, GAPI.TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED], + media_body=media_body, body=uploadAttachmentBody, fields='id,name', supportsAllDrives=True) + entityModifierItemValueListActionPerformed([Ent.DRIVE_FILE, f"{result['name']}({result['id']})"], + Act.MODIFIER_WITH_CONTENT_FROM, [Ent.ATTACHMENT, filename], j, jcount) + except (GAPI.forbidden, GAPI.insufficientPermissions, GAPI.insufficientParentPermissions, + GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent, + GAPI.fileNotFound, GAPI.unknownError, GAPI.internalError, + GAPI.storageQuotaExceeded, GAPI.teamdrivesSharingRestrictionNotAllowed, + GAPI.teamdrivefileLimitExceeded, GAPI.teamdriveHierarchyTooDeep, + GAPI.uploadTooLarge, GAPI.teamdrivesShortcutFileNotSupported) as e: + entityModifierItemValueListActionFailedWarning([Ent.DRIVE_FILE, None], + Act.MODIFIER_WITH_CONTENT_FROM, [Ent.ATTACHMENT, filename], str(e), j, jcount) + except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e: + userSvcNotApplicableOrDriveDisabled(user, str(e), i, count) + Act.Set(action) except (GAPI.serviceNotAvailable, GAPI.badRequest, GAPI.notFound): pass elif show_attachments: @@ -67942,7 +68051,7 @@ def printShowMessagesThreads(users, entityType): Ind.Decrement() break else: - _showSaveAttachments(messageId, part, attachmentNamePattern) + _showSaveAttachments(messageId, part, attachmentNamePattern, j, jcount) def _initSenderLabelsMap(sender): if sender not in senderLabelsMaps: @@ -68047,8 +68156,8 @@ def printShowMessagesThreads(users, entityType): Ind.Increment() printKeyValueList([Ind.MultiLineText(_getMessageBody(result['payload']))]) Ind.Decrement() - if show_attachments or save_attachments: - _showSaveAttachments(result['id'], result['payload'], attachmentNamePattern) + if show_attachments or save_attachments or upload_attachments: + _showSaveAttachments(result['id'], result['payload'], attachmentNamePattern, j, jcount) Ind.Decrement() parameters['messagesProcessed'] += 1 @@ -68217,7 +68326,7 @@ def printShowMessagesThreads(users, entityType): try: response = callGAPI(service, 'get', throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_MESSAGE_ID], - userId='me', id=ri[RI_ITEM], format=['metadata', 'full'][show_size or show_body or show_attachments or save_attachments]) + userId='me', id=ri[RI_ITEM], format=['metadata', 'full'][show_size or show_body or show_attachments or save_attachments or upload_attachments]) if countsOnly: _callbacks['process'](ri[RI_ENTITY], response) else: @@ -68254,7 +68363,7 @@ def printShowMessagesThreads(users, entityType): _handleGmailError(exception, ri) def _batchPrintShowMessagesThreads(service, user, jcount, messageIds): - svcargs = dict([('userId', 'me'), ('id', None), ('format', ['metadata', 'full'][show_body or show_attachments or save_attachments])]+GM.Globals[GM.EXTRA_ARGS_LIST]) + svcargs = dict([('userId', 'me'), ('id', None), ('format', ['metadata', 'full'][show_body or show_attachments or save_attachments or upload_attachments])]+GM.Globals[GM.EXTRA_ARGS_LIST]) if countsOnly: if show_labels: if not senderMatchPattern: @@ -68290,7 +68399,7 @@ def printShowMessagesThreads(users, entityType): parameters = _initMessageThreadParameters(entityType, True, 0) convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL] delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] - countsOnly = positiveCountsOnly = includeSpamTrash = onlyUser = overwrite = save_attachments = False + countsOnly = positiveCountsOnly = includeSpamTrash = onlyUser = overwrite = save_attachments = upload_attachments = False show_all_headers = show_attachments = show_body = show_date = show_labels = show_size = show_snippet = False noshow_text_plain = False attachmentNamePattern = None @@ -68301,6 +68410,8 @@ def printShowMessagesThreads(users, entityType): showMode = Act.Get() == Act.SHOW dateHeaderFormat = '' dateHeaderConvertTimezone = False + uploadAttachmentBody = {} + parentParms = initDriveFileAttributes() while Cmd.ArgumentsRemaining(): myarg = getArgument() if csvPF and myarg == 'todrive': @@ -68336,6 +68447,10 @@ def printShowMessagesThreads(users, entityType): targetFolderPattern = os.path.expanduser(getString(Cmd.OB_FILE_PATH)) elif showMode and myarg == 'overwrite': overwrite = getBoolean() + elif showMode and myarg == 'uploadattachments': + upload_attachments = True + elif showMode and getDriveFileParentAttribute(myarg, parentParms): + pass elif myarg == 'includespamtrash': includeSpamTrash = True elif myarg == 'countsonly': @@ -68400,6 +68515,12 @@ def printShowMessagesThreads(users, entityType): if not gmail: continue service = gmail.users().messages() if entityType == Ent.MESSAGE else gmail.users().threads() + if upload_attachments: + _, drive = buildGAPIServiceObject(API.DRIVE3, user, i, count) + if not drive: + continue + if not _getDriveFileParentInfo(drive, user, i, count, uploadAttachmentBody, parentParms): + continue if show_labels or labelMatchPattern: labels = _getUserGmailLabels(gmail, user, i, count, 'labels(id,name,type)') if not labels: @@ -68548,17 +68669,20 @@ def printShowMessagesThreads(users, entityType): # [labelmatchpattern ] [sendermatchpattern ] # [headers all|] [dateheaderformat iso|rfc2822|] [dateheaderconverttimezone []] # [showlabels] [showbody] [showdate] [showsize] [showsnippet] -# [showattachments [attachmentnamepattern ]] # [convertcrnl] [delimiter ] [todrive *] # [countsonly|positivecountsonly] [useronly] +# [[attachmentnamepattern ] +# [showattachments [noshowtextplain]]] # gam show message|messages # (((query [querytime ]*) (matchlabel ) [or|and])* [quick|notquick] [max_to_show ] [includespamtrash])|(ids ) # [labelmatchpattern ] [sendermatchpattern ] # [headers all|] [dateheaderformat iso|rfc2822|] [dateheaderconverttimezone []] # [showlabels] [showbody] [showdate] [showsize] [showsnippet] -# [showattachments [attachmentnamepattern ] [noshowtextplain]] # [countsonly|positivecountsonly] [useronly] -# [saveattachments [attachmentnamepattern ]] [targetfolder ] [overwrite []] +# [[attachmentnamepattern ] +# [showattachments [noshowtextplain]] +# [saveattachments [targetfolder ] [overwrite []]] +# [uploadattachments []]] def printShowMessages(users): printShowMessagesThreads(users, Ent.MESSAGE) @@ -68567,17 +68691,20 @@ def printShowMessages(users): # [labelmatchpattern ] # [headers all|] [dateheaderformat iso|rfc2822|] [dateheaderconverttimezone []] # [showlabels] [showbody] [showdate] [showsize] [showsnippet] -# [showattachments [attachmentnamepattern ]] # [convertcrnl] [delimiter ] [todrive *] # [countsonly|positivecountsonly] [useronly] +# [[attachmentnamepattern ] +# [showattachments [noshowtextplain]]] # gam show thread|threads # (((query [querytime ]*) (matchlabel ) [or|and])* [quick|notquick] [max_to_show ] [includespamtrash])|(ids ) # [labelmatchpattern ] # [headers all|] [dateheaderformat iso|rfc2822|] [dateheaderconverttimezone []] # [showlabels] [showbody] [showdate] [showsize] [showsnippet] -# [showattachments [attachmentnamepattern ] [noshowtextplain]] # [countsonly|positivecountsonly] [useronly] -# [saveattachments [attachmentnamepattern ]] [targetfolder ] [overwrite []] +# [[attachmentnamepattern ] +# [showattachments [noshowtextplain]] +# [saveattachments [targetfolder ] [overwrite []]] +# [uploadattachments []]] def printShowThreads(users): printShowMessagesThreads(users, Ent.THREAD) @@ -73048,6 +73175,7 @@ MAIN_COMMANDS_WITH_OBJECTS = { Cmd.ARG_TRANSFERAPPS: doShowTransferApps, Cmd.ARG_USER: doPrintUsers, Cmd.ARG_USERS: doPrintUsers, + Cmd.ARG_USERCOUNTSBYORGUNIT: doPrintUserCountsByOrgUnit, Cmd.ARG_USERINVITATION: doPrintShowCIUserInvitations, Cmd.ARG_VAULTCOUNT: doPrintVaultCounts, Cmd.ARG_VAULTEXPORT: doPrintShowVaultExports, diff --git a/src/gam/gamlib/glapi.py b/src/gam/gamlib/glapi.py index 92846d7f..8edb7055 100644 --- a/src/gam/gamlib/glapi.py +++ b/src/gam/gamlib/glapi.py @@ -72,6 +72,7 @@ KEEP = 'keep' LICENSING = 'licensing' LOOKERSTUDIO = 'datastudio' OAUTH2 = 'oauth2' +ORGPOLICY = 'orgpolicy' PEOPLE = 'people' PEOPLE_DIRECTORY = 'peopledirectory' PEOPLE_OTHERCONTACTS = 'peopleothercontacts' @@ -112,7 +113,7 @@ REQUIRED_SCOPES_SET = set(REQUIRED_SCOPES) JWT_APIS = { ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE], CHAT: ['https://www.googleapis.com/auth/chat.bot'], - CLOUDRESOURCEMANAGER: [CLOUD_PLATFORM_SCOPE] + ORGPOLICY: [CLOUD_PLATFORM_SCOPE], } # APIS_NEEDING_ACCESS_TOKEN = { @@ -236,6 +237,7 @@ _INFO = { LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True}, LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True}, OAUTH2: {'name': 'OAuth2 API', 'version': 'v2', 'v2discovery': False}, + ORGPOLICY: {'name': 'Organization Policy API', 'version': 'v2', 'v2discovery': True}, PEOPLE: {'name': 'People API', 'version': 'v1', 'v2discovery': True}, PEOPLE_DIRECTORY: {'name': 'People Directory API', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE}, PEOPLE_OTHERCONTACTS: {'name': 'People API - Other Contacts', 'version': 'v1', 'v2discovery': True, 'mappedAPI': PEOPLE}, diff --git a/src/gam/gamlib/glclargs.py b/src/gam/gamlib/glclargs.py index a073700f..e246c673 100644 --- a/src/gam/gamlib/glclargs.py +++ b/src/gam/gamlib/glclargs.py @@ -776,6 +776,7 @@ class GamCLArgs(): ARG_TRUSTEDAPPS = 'trustedapps' ARG_USER = 'user' ARG_USERS = 'users' + ARG_USERCOUNTSBYORGUNIT = 'usercountsbyorgunit' ARG_USERINVITATION = 'userinvitation' ARG_USERINVITATIONS = 'userinvitations' ARG_USERLIST = 'userlist'