mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-08 16:21:38 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8d68e8eb0 | ||
|
|
2196e70e7b | ||
|
|
4f79a29155 | ||
|
|
0290f0d0ce | ||
|
|
b7c96a8172 | ||
|
|
fed1662eb4 | ||
|
|
077747be99 | ||
|
|
9eee475b4a | ||
|
|
2636dd9827 | ||
|
|
e18a70e55b | ||
|
|
e11123fccd | ||
|
|
536ea84321 | ||
|
|
4d953ac09b | ||
|
|
d9ce0acd83 | ||
|
|
f735e5e268 | ||
|
|
97358d15ef | ||
|
|
ea0886229e | ||
|
|
4e5462c704 | ||
|
|
ed2291bedf | ||
|
|
44ff2eba62 | ||
|
|
77fb2d0693 | ||
|
|
cea5a33856 | ||
|
|
3a7f84e49d | ||
|
|
5136c0a268 | ||
|
|
8e877a938e | ||
|
|
ed695b234f | ||
|
|
83179be184 | ||
|
|
8e105091d4 | ||
|
|
428be889a2 | ||
|
|
758519b1b3 | ||
|
|
47e2ba95eb | ||
|
|
881cbbea61 | ||
|
|
3ebfb98a98 | ||
|
|
0dbd95832f | ||
|
|
78acf64f7b | ||
|
|
4c7f03da7d | ||
|
|
3ac28ef231 | ||
|
|
7100abab12 | ||
|
|
1d531d13e4 | ||
|
|
df5af20b49 | ||
|
|
cfe6376c28 | ||
|
|
e271e18feb | ||
|
|
33d579985a | ||
|
|
887628ef87 | ||
|
|
4a969e716e | ||
|
|
363c041bbb | ||
|
|
2e151eb81b | ||
|
|
b7fe258aaf | ||
|
|
7a10f6627c | ||
|
|
613cae987f | ||
|
|
cd34c3d1e2 | ||
|
|
899200399c | ||
|
|
46c3f88280 | ||
|
|
3ddef79cbf | ||
|
|
546f31c123 | ||
|
|
443fca3f00 | ||
|
|
720d667b61 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -153,7 +153,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20251218
|
||||
key: gam-${{ matrix.jid }}-20260129
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
|
||||
@@ -12,7 +12,7 @@ authors = [
|
||||
dependencies = [
|
||||
"arrow>=1.3.0",
|
||||
"chardet>=5.2.0",
|
||||
"cryptography>=44.0.2",
|
||||
"cryptography==46.0.3",
|
||||
"distro; sys_platform=='linux'",
|
||||
"filelock>=3.18.0",
|
||||
"google-api-python-client>=2.167.0",
|
||||
|
||||
@@ -2472,13 +2472,17 @@ gam <CrOSTypeEntity> update action <CrOSAction> [acknowledge_device_touch_requir
|
||||
take_a_screenshot
|
||||
|
||||
gam issuecommand cros <CrOSEntity> command <CrOSCommand>
|
||||
[times_to_check_status <Integer>] [csv] [doit]
|
||||
[times_to_check_status <Integer>] [doit]
|
||||
[csv (addcsvdata <FieldName> <String>)*]
|
||||
gam <CrOSTypeEntity> issuecommand command <CrOSCommand>
|
||||
[times_to_check_status <Integer>] [csv] [doit]
|
||||
[times_to_check_status <Integer>] [doit]
|
||||
[csv (addcsvdata <FieldName> <String>)*]
|
||||
gam getcommand cros <CrOSEntity> commandid <CommandID>
|
||||
[times_to_check_status <Integer>] [csv]
|
||||
[times_to_check_status <Integer>]
|
||||
[csv (addcsvdata <FieldName> <String>)*]
|
||||
gam <CrOSTypeEntity> getcommand commandid <CommandID>
|
||||
[times_to_check_status <Integer>] [csv]
|
||||
[times_to_check_status <Integer>]
|
||||
[csv (addcsvdata <FieldName> <String>)*]
|
||||
|
||||
<CrOSAttribute> ::=
|
||||
(asset|assetid|tag <String>)|
|
||||
@@ -2785,21 +2789,23 @@ gam create chromepolicyimage <ChromePolicyImageSchemaName> <FileName>
|
||||
|
||||
gam update chromepolicy [convertcrnl]
|
||||
(<SchemaName> ((<Field> <Value>)+ | <JSONData>))+
|
||||
((ou|orgunit <OrgUnitItem>)|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
gam delete chromepolicy
|
||||
(<SchemaName> [<JSONData>])+
|
||||
((ou|orgunit <OrgUnitItem>)|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
gam show chromepolicies
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
(filter <StringList>)* (namespace <NamespaceList>)*
|
||||
[show all|direct|inherited]
|
||||
[formatjson]
|
||||
gam print chromepolicies [todrive <ToDriveAttribute>*]
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(cigroup <GroupItem>))
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
(filter <StringList>)* (namespace <NamespaceList>)*
|
||||
[show all|direct|inherited] [shownopolicy]
|
||||
[[formatjson [quotechar <Character>]]
|
||||
|
||||
<ChromePolicySchemaFieldName> ::=
|
||||
@@ -6162,6 +6168,7 @@ Display a users calendar settings
|
||||
defaulteventlength|
|
||||
format24hourtime|
|
||||
hideinvitations|
|
||||
hideinvitationssetting|
|
||||
hideweekends|
|
||||
locale|
|
||||
remindonrespondedeventsonly|
|
||||
@@ -6884,6 +6891,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
[skipids <DriveFileEntity>]
|
||||
[copysubfiles [<Boolean>]] [filenamematchpattern <REMatchPattern>]
|
||||
[filemimetype [not] <MimeTypeList>] [filemimetype category <MimeTypeNameList>]
|
||||
[([start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>])|(range <Date>|<Time> <Date>|<Time>)]|
|
||||
[copysubfilesownedby
|
||||
any|me|others|
|
||||
users <EmailAddressList>|
|
||||
@@ -6969,6 +6977,7 @@ gam <UserTypeEntity> info drivefile <DriveFileEntity>
|
||||
[returnidonly]
|
||||
[filepath|fullpath] [folderpathonly [<Boolean>]] [pathdelimiter <Character>]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[includepermissionsforview published]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
@@ -7452,6 +7461,7 @@ gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
[returnidonly]
|
||||
[filepath|fullpath] [folderpathonly [<Boolean>]] [pathdelimiter <Character>]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[includepermissionsforview published]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
@@ -7565,6 +7575,7 @@ gam <UserTypeEntity> print filelist [todrive <ToDriveAttribute>*]
|
||||
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
|
||||
[filenamematchpattern <REMatchPattern>]
|
||||
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>] [pmfilter] [oneitemperrow]
|
||||
[includepermissionsforview published]
|
||||
[excludetrashed]
|
||||
[maxfiles <Integer>] [nodataheaders <String>]
|
||||
[countsonly [summary none|only|plus] [summaryuser <String>]
|
||||
@@ -8036,7 +8047,7 @@ gam <UserTypeEntity> signature|sig
|
||||
[html [<Boolean>]] [replyto <EmailAddress>] [default] [treatasalias <Boolean>]
|
||||
[name <String>]
|
||||
[primary]
|
||||
gam <UserTypeEntity> show signature|sig [compact|format|html]
|
||||
gam <UserTypeEntity> show signature|sig [compact|format|html|template]
|
||||
[primary|default] [verifyonly]
|
||||
gam <UserTypeEntity> print signature [compact]
|
||||
[primary|default] [verifyonly] [todrive <ToDriveAttribute>*]
|
||||
|
||||
@@ -1,3 +1,130 @@
|
||||
7.34.01
|
||||
|
||||
Updated `gam create|update adminrole` to handle the following errors:
|
||||
```
|
||||
ERROR: 400: invalid - Invalid Role privileges
|
||||
ERROR: 400: required - Required parameter: [resource.privileges[n].service_id]
|
||||
```
|
||||
|
||||
7.34.00
|
||||
|
||||
Added variable `csv_output_header_required` to `gam.cfg` that is a comma separated list of `<Strings>`
|
||||
that are required to be in the list of column headers in the CSV file written by a gam print command.
|
||||
This will typically be used to specify headers that are required in subsequent commands that process
|
||||
the CSV file even if the API didn't return any data for those columns.
|
||||
|
||||
Updated the following commands to not require the `Directory API - Domains` scope
|
||||
unless the `internal` or `external` options are used to request the member category.
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
|
||||
7.33.03
|
||||
|
||||
Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>`
|
||||
where an `<EmailAddress>` of the form `Text <user@domain.com>` had the `Text` removed.
|
||||
|
||||
7.33.02
|
||||
|
||||
Added `hideinvitationssetting` to `<UserCalendarSettingsField>` used by
|
||||
`gam <UserTypeEntity> print|show calsettings`.
|
||||
|
||||
7.33.01
|
||||
|
||||
Added option `shownopolicy` to `gam print chromepolicies` that will display output like the following
|
||||
if no policies apply to the selected OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
7.32.05
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print messages|threads ... headers <SMTPHeaderList>` where
|
||||
headers other than those specified in `<SMTPHeaderList>` were displayed.
|
||||
|
||||
Updated `gam info users <UserTypeEntity>` to display the following data when the Licensing API
|
||||
does not return data due to quota limits. Previously, no License data was displayed and
|
||||
there was no way to know if it was omitted due to API quota limits vs the user has no license?
|
||||
```
|
||||
Licenses: (1)
|
||||
Not available/incomplete
|
||||
```
|
||||
If a user has no licenses, this will be displayed.
|
||||
```
|
||||
Licenses: (0)
|
||||
```
|
||||
|
||||
You should use `license_skus = <SKUIDList>` in `gam.cfg` to list all of the licensing SKUs
|
||||
used in your workspace. Without this list, GAM has to make 70+ API calls to get the licenses
|
||||
for a user; this can cause quota limit errors.
|
||||
|
||||
7.32.04
|
||||
|
||||
Support for student groups in Google Classroom no longer requires Developer Preview membership.
|
||||
|
||||
Upgraded to OpenSSL 3.6.1.
|
||||
|
||||
7.32.03
|
||||
|
||||
Added option `template` as an additional formating option for `gam <UserTypeEntity> show signature`
|
||||
that displays just the HTML data; this simplifies capturing the data for use as input to GAM.
|
||||
```
|
||||
$ gam redirect stdout ./SigTemplate.html user user@domain.com show signature template
|
||||
$ more SigTemplate.html
|
||||
<div dir="ltr"><div dir="ltr"><div dir="ltr">
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Email: {Email}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Phone: {Phone}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Mobile: {Mobile}{/RT}</span></p>
|
||||
</div><br></div>\n</div>
|
||||
|
||||
```
|
||||
|
||||
7.32.02
|
||||
|
||||
Added variable `oauth2_txt_lock_mode` to `gam.cfg`, the default is 644 and valid values are: 644, 664, 666.
|
||||
This value is used to set the file permissions on the `oauth2.txt.lock` file. In very special cases where
|
||||
daemon processes, e.g. Apache/httpd, are running GAM, the value 666 may have to be used.
|
||||
|
||||
7.32.01
|
||||
|
||||
Added option `(addcsvdata <FieldName> <String>)*` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> csv`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID> csv` that adds additional columns of data to the CSV file output.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/ChromeOS-Devices#bulk-action-example
|
||||
|
||||
7.32.00
|
||||
|
||||
Added option `verifyallowexternal` to `gam print cigroup-members|group-members` that causes
|
||||
@@ -33,7 +160,7 @@ Fixed bug in `gam sendemail ... replyto <EmailAddress>` that caused a message de
|
||||
|
||||
Added support for users's chat sections.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-users-sections
|
||||
* This is in Deveoper Preview.
|
||||
* This is in Deveoper Preview; you must have a `developer_preview_api_key` in `gam.cfg` to use these commands.
|
||||
|
||||
7.31.06
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.32.00'
|
||||
__version__ = '7.34.01'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
@@ -1331,6 +1331,8 @@ def validateEmailAddressOrUID(emailAddressOrUID, checkPeople=True, ciGroupsAPI=F
|
||||
return emailAddressOrUID
|
||||
return emailAddressOrUID.find('@') != 0 and emailAddressOrUID.count('@') <= 1
|
||||
|
||||
NAME_EMAIL_ADDRESS_PATTERN = re.compile(r'^(.*)<(.+)>$')
|
||||
|
||||
# Normalize user/group email address/uid
|
||||
# uid:12345abc -> 12345abc
|
||||
# foo -> foo@domain
|
||||
@@ -1349,6 +1351,10 @@ def normalizeEmailAddressOrUID(emailAddressOrUID, noUid=False, checkForCustomerI
|
||||
return cg.group(1)
|
||||
if ciGroupsAPI and emailAddressOrUID.startswith('groups/'):
|
||||
return emailAddressOrUID
|
||||
if emailAddressOrUID.find('<') >= 0:
|
||||
match = NAME_EMAIL_ADDRESS_PATTERN.match(emailAddressOrUID)
|
||||
if match:
|
||||
emailAddressOrUID = match.group(2)
|
||||
atLoc = emailAddressOrUID.find('@')
|
||||
if atLoc == 0:
|
||||
return emailAddressOrUID[1:].lower() if not noLower else emailAddressOrUID[1:]
|
||||
@@ -2308,10 +2314,10 @@ def getMatchSkipFields(fieldNames):
|
||||
|
||||
def checkMatchSkipFields(row, fieldnames, matchFields, skipFields):
|
||||
for matchField, matchPattern in matchFields.items():
|
||||
if (matchField not in row) or not matchPattern.search(row[matchField]):
|
||||
if (matchField not in row) or not matchPattern.search(str(row[matchField])):
|
||||
return False
|
||||
for skipField, matchPattern in skipFields.items():
|
||||
if (skipField in row) and matchPattern.search(row[skipField]):
|
||||
if (skipField in row) and matchPattern.search(str(row[skipField])):
|
||||
return False
|
||||
if fieldnames and (GC.Values[GC.CSV_INPUT_ROW_FILTER] or GC.Values[GC.CSV_INPUT_ROW_DROP_FILTER]):
|
||||
return RowFilterMatch(row, fieldnames,
|
||||
@@ -3752,6 +3758,15 @@ def SetGlobalVariables():
|
||||
if (productId, sku) not in GM.Globals[GM.LICENSE_SKUS]:
|
||||
GM.Globals[GM.LICENSE_SKUS].append((productId, sku))
|
||||
|
||||
def _validateDeveloperPreviewAPIs(sectionName, itemName, apiList):
|
||||
GM.Globals[GM.DEVELOPER_PREVIEW_APIS] = set()
|
||||
validAPIs = API.getAPIsList()
|
||||
for api in apiList.split(','):
|
||||
if api in validAPIs:
|
||||
GM.Globals[GM.DEVELOPER_PREVIEW_APIS].add(api)
|
||||
else:
|
||||
_printValueError(sectionName, itemName, api, f'{Msg.EXPECTED}: {",".join(sorted(validAPIs))}')
|
||||
|
||||
def _getCfgString(sectionName, itemName):
|
||||
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName))
|
||||
if itemName == GC.DOMAIN:
|
||||
@@ -3760,6 +3775,8 @@ def SetGlobalVariables():
|
||||
if ((minLen is None) or (len(value) >= minLen)) and ((maxLen is None) or (len(value) <= maxLen)):
|
||||
if itemName == GC.LICENSE_SKUS and value:
|
||||
_validateLicenseSKUs(sectionName, itemName, value)
|
||||
elif itemName == GC.DEVELOPER_PREVIEW_APIS and value:
|
||||
_validateDeveloperPreviewAPIs(sectionName, itemName, value.lower())
|
||||
return value
|
||||
_printValueError(sectionName, itemName, f'"{value}"', f'{Msg.EXPECTED}: {integerLimits(minLen, maxLen, Msg.STRING_LENGTH)}')
|
||||
return ''
|
||||
@@ -4136,7 +4153,7 @@ def SetGlobalVariables():
|
||||
GC.Values[itemName] = _getCfgPassword(sectionName, itemName)
|
||||
elif varType == GC.TYPE_STRING:
|
||||
GC.Values[itemName] = _getCfgString(sectionName, itemName)
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER}:
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCEREQUIRED, GC.TYPE_HEADERORDER}:
|
||||
GC.Values[itemName] = _getCfgStringList(sectionName, itemName)
|
||||
elif varType == GC.TYPE_FILE:
|
||||
GC.Values[itemName] = _getCfgFile(sectionName, itemName)
|
||||
@@ -4160,6 +4177,7 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_DROP_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_ORDER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_REQUIRED)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = _getCfgChoice(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER_MODE)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_DROP_FILTER)
|
||||
@@ -4279,7 +4297,7 @@ def SetGlobalVariables():
|
||||
if GM.Globals[GM.PID] == 0:
|
||||
for itemName, itemEntry in sorted(GC.VAR_INFO.items()):
|
||||
varType = itemEntry[GC.VAR_TYPE]
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER, GC.TYPE_ROWFILTER}:
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCEREQUIRED, GC.TYPE_HEADERORDER, GC.TYPE_ROWFILTER}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '')
|
||||
elif (varType == GC.TYPE_INTEGER) and itemName in {GC.CSV_INPUT_ROW_LIMIT, GC.CSV_OUTPUT_ROW_LIMIT}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '0')
|
||||
@@ -4294,6 +4312,8 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE] = GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_ORDER]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED] = GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_ROW_FILTER]:
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER][:]
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE]
|
||||
@@ -4748,7 +4768,7 @@ def getClientCredentials(forceRefresh=False, forceWrite=False, filename=None, ap
|
||||
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid.
|
||||
Locks during read and possible write so that only one process will
|
||||
attempt refresh/write when running in parallel. """
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
|
||||
with lock:
|
||||
writeCreds, credentials = getOauth2TxtCredentials(api=api, noDASA=noDASA, refreshOnly=refreshOnly, noScopes=noScopes)
|
||||
if not credentials:
|
||||
@@ -4820,11 +4840,12 @@ def getService(api, httpObj):
|
||||
triesLimit = 3
|
||||
for n in range(1, triesLimit+1):
|
||||
try:
|
||||
if api not in {API.CHAT, API.CLASSROOM} or not GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]:
|
||||
if api not in GM.Globals[GM.DEVELOPER_PREVIEW_APIS] or not GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]:
|
||||
discoveryServiceUrl = DISCOVERY_URIS[v2discovery]
|
||||
developerKey = ''
|
||||
else:
|
||||
discoveryServiceUrl = DEVELOPER_PREVIEW_DISCOVERY_URI
|
||||
developerKey = GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]
|
||||
developerKey = GC.Values[GC.DEVELOPER_PREVIEW_API_KEY]
|
||||
service = googleapiclient.discovery.build(api, version, http=httpObj, cache_discovery=False,
|
||||
discoveryServiceUrl=discoveryServiceUrl, developerKey=developerKey, static_discovery=False)
|
||||
GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {})
|
||||
@@ -6066,7 +6087,7 @@ def _checkMemberCategory(member, memberDisplayOptions):
|
||||
if memberDisplayOptions['showCategory']:
|
||||
member['category'] = category
|
||||
if memberDisplayOptions['checkCategory']:
|
||||
return True if memberDisplayOptions[category] else False
|
||||
return bool(memberDisplayOptions[category])
|
||||
return True
|
||||
|
||||
def _checkCIMemberCategory(member, memberDisplayOptions):
|
||||
@@ -6079,7 +6100,7 @@ def _checkCIMemberCategory(member, memberDisplayOptions):
|
||||
if memberDisplayOptions['showCategory']:
|
||||
member['category'] = category
|
||||
if memberDisplayOptions['checkCategory']:
|
||||
return True if memberDisplayOptions[category] else False
|
||||
return bool(memberDisplayOptions[category])
|
||||
return True
|
||||
|
||||
def getCIGroupMemberRoleFixType(member):
|
||||
@@ -7316,8 +7337,6 @@ def _addEmbeddedImagesToMessage(message, embeddedImages):
|
||||
except (IOError, UnicodeDecodeError) as e:
|
||||
usageErrorExit(f'{imageFilename}: {str(e)}')
|
||||
|
||||
NAME_EMAIL_ADDRESS_PATTERN = re.compile(r'^.*<(.+)>$')
|
||||
|
||||
# Send an email
|
||||
def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msgFrom=None, msgReplyTo=None,
|
||||
html=False, charset=UTF8, attachments=None, embeddedImages=None,
|
||||
@@ -7339,8 +7358,11 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
def cleanAddr(emailAddr):
|
||||
match = NAME_EMAIL_ADDRESS_PATTERN.match(emailAddr)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return emailAddr
|
||||
emailName = match.group(1)
|
||||
emailAddr = normalizeEmailAddressOrUID(match.group(2), noUid=True, noLower=True)
|
||||
return (f'{emailName} <{emailAddr}>', emailAddr)
|
||||
emailAddr = normalizeEmailAddressOrUID(emailAddr, noUid=True, noLower=True)
|
||||
return (emailAddr, emailAddr)
|
||||
|
||||
if msgFrom is None:
|
||||
msgFrom = _getAdminEmail()
|
||||
@@ -7360,9 +7382,9 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
if embeddedImages:
|
||||
_addEmbeddedImagesToMessage(message, embeddedImages)
|
||||
message['Subject'] = msgSubject
|
||||
message['From'] = normalizeEmailAddressOrUID(cleanAddr(msgFrom), noUid=True, noLower=True)
|
||||
message['From'], msgFromAddr = cleanAddr(msgFrom)
|
||||
if msgReplyTo is not None:
|
||||
message['Reply-To'] = normalizeEmailAddressOrUID(cleanAddr(msgReplyTo), noUid=True, noLower=True)
|
||||
message['Reply-To'], _ = cleanAddr(msgReplyTo)
|
||||
if ccRecipients:
|
||||
message['Cc'] = ccRecipients.lower()
|
||||
if bccRecipients:
|
||||
@@ -7372,8 +7394,8 @@ def send_email(msgSubject, msgBody, msgTo, i=0, count=0, clientAccess=False, msg
|
||||
if header not in {'Subject', 'From', 'To', 'Reply-To', 'Cc', 'Bcc'}:
|
||||
message[header] = value
|
||||
if mailBox is None:
|
||||
mailBox = msgFrom
|
||||
mailBoxAddr = normalizeEmailAddressOrUID(cleanAddr(mailBox), noUid=True, noLower=True)
|
||||
mailBox = msgFromAddr
|
||||
_, mailBoxAddr = cleanAddr(mailBox)
|
||||
action = Act.Get()
|
||||
Act.Set(Act.SENDEMAIL)
|
||||
if not GC.Values[GC.SMTP_HOST]:
|
||||
@@ -7903,6 +7925,7 @@ class CSVPrintFile():
|
||||
self.JSONtitlesList = []
|
||||
self.sortHeaders = []
|
||||
self.SetHeaderForce(GC.Values[GC.CSV_OUTPUT_HEADER_FORCE])
|
||||
self.SetHeaderRequired(GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED])
|
||||
if not self.headerForce and titles is not None:
|
||||
self.SetTitles(titles)
|
||||
self.SetJSONTitles(titles)
|
||||
@@ -8619,6 +8642,9 @@ class CSVPrintFile():
|
||||
self.SetTitles(headerForce)
|
||||
self.SetJSONTitles(headerForce)
|
||||
|
||||
def SetHeaderRequired(self, headerRequired):
|
||||
self.headerRequired = headerRequired
|
||||
|
||||
def SetHeaderOrder(self, headerOrder):
|
||||
self.headerOrder = headerOrder
|
||||
|
||||
@@ -9062,6 +9088,8 @@ class CSVPrintFile():
|
||||
extrasaction = 'raise'
|
||||
if not self.formatJSON:
|
||||
if not self.headerForce:
|
||||
if self.headerRequired:
|
||||
self.AddTitles(self.headerRequired)
|
||||
self.SortTitles()
|
||||
self.SortIndexedTitles(self.titlesList)
|
||||
if self.fixPaths:
|
||||
@@ -9079,6 +9107,17 @@ class CSVPrintFile():
|
||||
titlesList = self.titlesList
|
||||
else:
|
||||
if not self.headerForce:
|
||||
if self.headerRequired:
|
||||
for i, v in enumerate(self.JSONtitlesList):
|
||||
if v.startswith('JSON'):
|
||||
j = i
|
||||
for title in self.headerRequired:
|
||||
self.JSONtitlesList.insert(j, title)
|
||||
self.JSONtitlesSet.add(title)
|
||||
j += 1
|
||||
break
|
||||
else:
|
||||
self.AddJSONTitles(self.headerRequired)
|
||||
if self.fixPaths:
|
||||
self.FixPathsTitles(self.JSONtitlesList)
|
||||
if not self.rows and self.nodataFields is not None:
|
||||
@@ -9888,7 +9927,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
csvColumnDelimiter, csvNoEscapeChar, csvQuoteChar,
|
||||
csvSortHeaders, csvTimestampColumn,
|
||||
csvHeaderFilter, csvHeaderDropFilter,
|
||||
csvHeaderForce, csvHeaderOrder,
|
||||
csvHeaderForce, csvHeaderOrder, csvHeaderRequired,
|
||||
csvRowFilter, csvRowFilterMode, csvRowDropFilter, csvRowDropFilterMode,
|
||||
csvRowLimit,
|
||||
showGettings, showGettingsGotNL,
|
||||
@@ -9913,6 +9952,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FILTER] = csvHeaderFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE] = csvHeaderForce[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER] = csvHeaderOrder[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_REQUIRED] = csvHeaderRequired[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR] = csvQuoteChar
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER] = csvRowDropFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = csvRowDropFilterMode
|
||||
@@ -10137,6 +10177,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_REQUIRED],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER],
|
||||
@@ -10760,7 +10801,7 @@ Continue to authorization by entering a 'c'
|
||||
menu = oauth2_menu % tuple(range(numScopes))
|
||||
selectedScopes = ['*'] * numScopes
|
||||
if currentScopes is None and clientAccess:
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
|
||||
with lock:
|
||||
_, credentials = getOauth2TxtCredentials(exitOnError=False)
|
||||
if credentials and credentials.scopes is not None:
|
||||
@@ -11258,7 +11299,7 @@ def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
|
||||
access_type='offline',
|
||||
login_hint=login_hint,
|
||||
open_browser=not GC.Values[GC.NO_BROWSER])
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
|
||||
with lock:
|
||||
writeClientCredentials(credentials, GC.Values[GC.OAUTH2_TXT])
|
||||
entityActionPerformed([Ent.OAUTH2_TXT_FILE, GC.Values[GC.OAUTH2_TXT]])
|
||||
@@ -11308,7 +11349,7 @@ def exitIfNoOauth2Txt():
|
||||
def doOAuthDelete():
|
||||
checkForExtraneousArguments()
|
||||
exitIfNoOauth2Txt()
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], timeout=10)
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE], timeout=10)
|
||||
with lock:
|
||||
_, credentials = getOauth2TxtCredentials(noScopes=True)
|
||||
if not credentials:
|
||||
@@ -11392,7 +11433,7 @@ def doOAuthUpdate():
|
||||
login_hint = getEmailAddress(noUid=True, optional=True)
|
||||
checkForExtraneousArguments()
|
||||
exitIfNoOauth2Txt()
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
|
||||
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
|
||||
with lock:
|
||||
jsonData = readFile(GC.Values[GC.OAUTH2_TXT], continueOnError=True, displayError=False)
|
||||
if not jsonData:
|
||||
@@ -16951,12 +16992,13 @@ def doCreateUpdateAdminRoles():
|
||||
if not updateCmd:
|
||||
result = callGAPI(cd.roles(), 'insert',
|
||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.DUPLICATE],
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.DUPLICATE, GAPI.INVALID, GAPI.REQUIRED],
|
||||
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields=fieldsList)
|
||||
else:
|
||||
result = callGAPI(cd.roles(), 'patch',
|
||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.CONFLICT],
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION,
|
||||
GAPI.CONFLICT, GAPI.INVALID, GAPI.REQUIRED],
|
||||
customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, body=body, fields=fieldsList)
|
||||
if not csvPF:
|
||||
entityActionPerformed([Ent.ADMIN_ROLE, f"{result['roleName']}({result['roleId']})"])
|
||||
@@ -16974,7 +17016,7 @@ def doCreateUpdateAdminRoles():
|
||||
row.update(addCSVData)
|
||||
row['JSON'] = json.dumps(cleanJSON(result), ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
except GAPI.duplicate as e:
|
||||
except (GAPI.duplicate, GAPI.invalid, GAPI.required) as e:
|
||||
entityActionFailedWarning([Ent.ADMIN_ROLE, f"{body['roleName']}"], str(e))
|
||||
except (GAPI.notFound, GAPI.failedPrecondition, GAPI.conflict) as e:
|
||||
entityActionFailedWarning([Ent.ADMIN_ROLE, roleId], str(e))
|
||||
@@ -24022,7 +24064,7 @@ CROS_KIOSK_COMMANDS = {'REBOOT', 'SET_VOLUME', 'TAKE_A_SCREENSHOT'}
|
||||
CROS_COMMAND_FINAL_STATES = {'EXPIRED', 'CANCELLED', 'EXECUTED_BY_CLIENT'}
|
||||
CROS_COMMAND_TIME_OBJECTS = {'executeTime', 'issueTime', 'commandExpireTime'}
|
||||
|
||||
def displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF=None):
|
||||
def displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF, addCSVData):
|
||||
Ind.Increment()
|
||||
try:
|
||||
for _ in range(0, checkResultRetries):
|
||||
@@ -24032,6 +24074,8 @@ def displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, cou
|
||||
customerId=GC.Values[GC.CUSTOMER_ID], deviceId=deviceId, commandId=commandId)
|
||||
if csvPF:
|
||||
result['deviceId'] = deviceId
|
||||
if addCSVData:
|
||||
result.update(addCSVData)
|
||||
csvPF.WriteRowTitles(flattenJSON(result, timeObjects=CROS_COMMAND_TIME_OBJECTS))
|
||||
return
|
||||
showJSON(None, result, timeObjects=CROS_COMMAND_TIME_OBJECTS)
|
||||
@@ -24042,11 +24086,21 @@ def displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, cou
|
||||
entityActionFailedWarning([Ent.CROS_DEVICE, deviceId, Ent.COMMAND_ID, commandId], str(e), i, count)
|
||||
Ind.Decrement()
|
||||
|
||||
def writeCrOSCommandResults(csvPF, addCSVData):
|
||||
sortTitles = ['deviceId']
|
||||
if addCSVData:
|
||||
sortTitles.extend(sorted(addCSVData.keys()))
|
||||
sortTitles.append('commandId')
|
||||
csvPF.SetSortTitles(sortTitles)
|
||||
csvPF.writeCSVfile('CrOS Commands')
|
||||
|
||||
# gam <CrOSTypeEntity> issuecommand command <CrOSCommand>
|
||||
# [times_to_check_status <Integer>] [csv] [doit]
|
||||
# [times_to_check_status <Integer>] [doit]
|
||||
# [csv (addcsvdata <FieldName> <String>)*]
|
||||
def issueCommandCrOSDevices(entityList):
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
csvPF = None
|
||||
addCSVData = {}
|
||||
body = {}
|
||||
checkResultRetries = 1
|
||||
doit = False
|
||||
@@ -24059,7 +24113,9 @@ def issueCommandCrOSDevices(entityList):
|
||||
elif myarg == 'timestocheckstatus':
|
||||
checkResultRetries = getInteger(minVal=0)
|
||||
elif myarg == 'csv':
|
||||
csvPF = CSVPrintFile(['deviceId'], 'sortall')
|
||||
csvPF = CSVPrintFile(['deviceId'])
|
||||
elif csvPF and myarg == 'addcsvdata':
|
||||
getAddCSVData(addCSVData)
|
||||
elif myarg == 'doit':
|
||||
doit = True
|
||||
else:
|
||||
@@ -24078,7 +24134,7 @@ def issueCommandCrOSDevices(entityList):
|
||||
customerId=GC.Values[GC.CUSTOMER_ID], deviceId=deviceId, body=body)
|
||||
commandId = result.get('commandId')
|
||||
entityActionPerformed([Ent.CROS_DEVICE, deviceId, Ent.ACTION, body['commandType'], Ent.COMMAND_ID, commandId], i, count)
|
||||
displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF)
|
||||
displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF, addCSVData)
|
||||
except GAPI.invalidArgument as e:
|
||||
errMsg = str(e)
|
||||
if body['commandType'] in CROS_KIOSK_COMMANDS:
|
||||
@@ -24087,18 +24143,21 @@ def issueCommandCrOSDevices(entityList):
|
||||
except GAPI.notFound as e:
|
||||
entityActionFailedWarning([Ent.CROS_DEVICE, deviceId], str(e), i, count)
|
||||
if csvPF:
|
||||
csvPF.writeCSVfile('CrOS Commands')
|
||||
writeCrOSCommandResults(csvPF, addCSVData)
|
||||
|
||||
# gam issuecommand <CrOSEntity> command <CrOSCommand>
|
||||
# [times_to_check_status <Integer>] [csv] [doit]
|
||||
# [times_to_check_status <Integer>] [doit]
|
||||
# [csv (addcsvdata <FieldName> <String>)*]
|
||||
def doIssueCommandCrOSDevices():
|
||||
issueCommandCrOSDevices(getCrOSDeviceEntity())
|
||||
|
||||
# gam <CrOSTypeEntity> getcommand commandid <CommandID>
|
||||
# [times_to_check_status <Integer>] [csv]
|
||||
# [csv (addcsvdata <FieldName> <String>)*]
|
||||
def getCommandResultCrOSDevices(entityList):
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
csvPF = None
|
||||
addCSVData = {}
|
||||
commandId = ''
|
||||
checkResultRetries = 1
|
||||
while Cmd.ArgumentsRemaining():
|
||||
@@ -24108,7 +24167,9 @@ def getCommandResultCrOSDevices(entityList):
|
||||
elif myarg == 'timestocheckstatus':
|
||||
checkResultRetries = getInteger(minVal=0)
|
||||
elif myarg == 'csv':
|
||||
csvPF = CSVPrintFile(['deviceId'], 'sortall')
|
||||
csvPF = CSVPrintFile(['deviceId'])
|
||||
elif csvPF and myarg == 'addcsvdata':
|
||||
getAddCSVData(addCSVData)
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
if not commandId:
|
||||
@@ -24117,12 +24178,13 @@ def getCommandResultCrOSDevices(entityList):
|
||||
for deviceId in entityList:
|
||||
i += 1
|
||||
printEntity([Ent.CROS_DEVICE, deviceId, Ent.COMMAND_ID, commandId], i, count)
|
||||
displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF)
|
||||
displayCrOSCommandResult(cd, deviceId, commandId, checkResultRetries, i, count, csvPF, addCSVData)
|
||||
if csvPF:
|
||||
csvPF.writeCSVfile('CrOS Commands')
|
||||
writeCrOSCommandResults(csvPF, addCSVData)
|
||||
|
||||
# gam getcommand <CrOSEntity> commandid <CommandID>
|
||||
# [times_to_check_status <Integer>] [csv]
|
||||
# [csv (addcsvdata <FieldName> <String>)*]
|
||||
def doGetCommandResultCrOSDevices():
|
||||
getCommandResultCrOSDevices(getCrOSDeviceEntity())
|
||||
|
||||
@@ -27043,6 +27105,8 @@ def createUpdateChatSection(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> delete chatsection <ChatSection>
|
||||
def deleteChatSection(users):
|
||||
@@ -27073,6 +27137,8 @@ def deleteChatSection(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> show chatsections
|
||||
# [formatjson]
|
||||
@@ -27107,6 +27173,8 @@ def printShowChatSections(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
jcount = len(sections)
|
||||
if jcount == 0:
|
||||
setSysExitRC(NO_ENTITIES_FOUND_RC)
|
||||
@@ -27161,6 +27229,8 @@ def moveShowChatSectionItem(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
|
||||
# gam <UserTypeEntity> show chatsectionitems <ChatSection>
|
||||
# [space <ChatSpace>]
|
||||
@@ -27232,6 +27302,8 @@ def printShowChatSectionItems(users):
|
||||
except GAPI.failedPrecondition:
|
||||
userChatServiceNotEnabledWarning(user, i, count)
|
||||
continue
|
||||
except AttributeError:
|
||||
systemErrorExit(GOOGLE_API_ERROR_RC, Msg.DEVELOPER_PREVIEW_REQUIRED)
|
||||
jcount = len(sectionItems)
|
||||
if jcount == 0:
|
||||
setSysExitRC(NO_ENTITIES_FOUND_RC)
|
||||
@@ -30047,7 +30119,7 @@ CHROME_POLICY_SHOW_CHOICE_MAP = {
|
||||
# ((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
# [(printerid <PrinterID>)|(appid <AppID>)]
|
||||
# (filter <StringList>)* (namespace <NamespaceList>)*
|
||||
# [show all|direct|inherited]
|
||||
# [show all|direct|inherited] [shownopolicy]
|
||||
# [[formatjson [quotechar <Character>]]
|
||||
def doPrintShowChromePolicies():
|
||||
def normalizedPolicy(policy):
|
||||
@@ -30118,25 +30190,54 @@ def doPrintShowChromePolicies():
|
||||
showJSON(None, policy, sortDictKeys=False)
|
||||
Ind.Decrement()
|
||||
|
||||
def _printPolicyRow(policy):
|
||||
row = flattenJSON(policy)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'orgUnitPath': policy['orgUnitPath'],
|
||||
'parentOrgUnitPath': policy['parentOrgUnitPath'],
|
||||
'direct': policy['direct'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
else:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'group': policy['group'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
|
||||
|
||||
def _printPolicy(policy):
|
||||
nonlocal policiesShown
|
||||
policy = normalizedPolicy(policy)
|
||||
if (entityType == Ent.GROUP) or showPolicies in (CHROME_POLICY_SHOW_ALL, policy['direct']):
|
||||
row = flattenJSON(policy)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'orgUnitPath': policy['orgUnitPath'],
|
||||
'parentOrgUnitPath': policy['parentOrgUnitPath'],
|
||||
'direct': policy['direct'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
else:
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'group': policy['group'],
|
||||
'JSON': json.dumps(cleanJSON(policy),
|
||||
ensure_ascii=False, sort_keys=True)})
|
||||
policiesShown += 1
|
||||
_printPolicyRow(policy)
|
||||
|
||||
def _printNoPolicy():
|
||||
nonlocal targetName
|
||||
policy = {'name': 'noPolicy'}
|
||||
if app_id:
|
||||
policy['appId'] = app_id
|
||||
elif printer_id:
|
||||
policy['printerId'] = printer_id
|
||||
if entityType == Ent.ORGANIZATIONAL_UNIT:
|
||||
policy['orgUnitPath'] = targetName
|
||||
if targetName == '/':
|
||||
policy['parentOrgUnitPath'] = '/'
|
||||
else:
|
||||
targetName = makeOrgUnitPathRelative(targetName)
|
||||
policy['parentOrgUnitPath'] = callGAPI(cd.orgunits(), 'get',
|
||||
throwReasons=GAPI.ORGUNIT_GET_THROW_REASONS,
|
||||
customerId=GC.Values[GC.CUSTOMER_ID],
|
||||
orgUnitPath=encodeOrgUnitPath(targetName),
|
||||
fields='parentOrgUnitPath')['parentOrgUnitPath']
|
||||
policy['direct'] = False
|
||||
else:
|
||||
policy['group'] = targetName
|
||||
_printPolicyRow(policy)
|
||||
|
||||
cp = buildGAPIObject(API.CHROMEPOLICY)
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
@@ -30148,6 +30249,8 @@ def doPrintShowChromePolicies():
|
||||
app_id = groupEmail = orgUnit = printer_id = targetResource = None
|
||||
showPolicies = CHROME_POLICY_SHOW_ALL
|
||||
psFilters = []
|
||||
showNoPolicy = False
|
||||
policiesShown = 0
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if csvPF and myarg == 'todrive':
|
||||
@@ -30171,6 +30274,8 @@ def doPrintShowChromePolicies():
|
||||
psFilters.append(f'{psFilter}.*')
|
||||
elif myarg == 'show':
|
||||
showPolicies = getChoice(CHROME_POLICY_SHOW_CHOICE_MAP, mapChoice=True)
|
||||
elif csvPF and myarg == 'shownopolicy':
|
||||
showNoPolicy = True
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, False)
|
||||
checkPolicyArgs(targetResource, printer_id, app_id)
|
||||
@@ -30260,7 +30365,8 @@ def doPrintShowChromePolicies():
|
||||
else:
|
||||
for policy in policies:
|
||||
_printPolicy(policy)
|
||||
if csvPF:
|
||||
if showNoPolicy and policiesShown == 0:
|
||||
_printNoPolicy()
|
||||
csvPF.writeCSVfile(f'Chrome Policies - {targetName}')
|
||||
|
||||
CHROME_IMAGE_SCHEMAS_MAP = {
|
||||
@@ -34763,13 +34869,14 @@ def finalizeInternalDomains(cd, internalDomains):
|
||||
return internalDomains
|
||||
|
||||
def finalizeIPSGMGroupRolesMemberDisplayOptions(cd, memberDisplayOptions, verifyAllowExternal):
|
||||
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
|
||||
if verifyAllowExternal:
|
||||
memberDisplayOptions['external'] = memberDisplayOptions['checkCategory'] = memberDisplayOptions['showCategory'] = True
|
||||
memberDisplayOptions['internal'] = False
|
||||
if memberDisplayOptions['showCategory']:
|
||||
memberDisplayOptions['gs'] = buildGAPIObject(API.GROUPSSETTINGS)
|
||||
memberDisplayOptions['checkShowCategory'] = memberDisplayOptions['checkCategory'] or memberDisplayOptions['showCategory']
|
||||
if memberDisplayOptions['checkShowCategory']:
|
||||
memberDisplayOptions['internalDomains'] = finalizeInternalDomains(cd, memberDisplayOptions['internalDomains'])
|
||||
return memberDisplayOptions['showCategory'], memberDisplayOptions['checkShowCategory']
|
||||
|
||||
GROUP_FIELDS_CHOICE_MAP = {
|
||||
@@ -45885,6 +45992,20 @@ def verifyUserPrimaryEmail(cd, user, createIfNotFound, i, count):
|
||||
entityUnknownWarning(Ent.USER, user, i, count)
|
||||
return False
|
||||
|
||||
# gam create guestuser <EmailAddress>
|
||||
def doCreateGuestUser():
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
body = {'primaryGuestEmail': getEmailAddress(noUid=True),
|
||||
'customer': GC.Values[GC.CUSTOMER_ID]}
|
||||
checkForExtraneousArguments()
|
||||
try:
|
||||
result = callGAPI(cd.users(), 'createGuest',
|
||||
throwReasons=[GAPI.FAILED_PRECONDITION],
|
||||
body=body)
|
||||
entityActionPerformed([Ent.GUEST_USER, result['primaryGuestEmail']])
|
||||
except (GAPI.failedPrecondition) as e:
|
||||
entityActionFailedExit([Ent.GUEST_USER, body['primaryGuestEmail']], str(e))
|
||||
|
||||
# gam <UserTypeEntity> update user <UserAttribute>*
|
||||
# [verifynotinvitable|alwaysevict] [noactionifalias]
|
||||
# [updateprimaryemail <RESEarchPattern> <RESubstitution>]
|
||||
@@ -46364,6 +46485,7 @@ def getUserLicenses(lic, user, skus):
|
||||
if sku_calls:
|
||||
if try_count >= 5:
|
||||
# give up and return what we have
|
||||
licenses.append('Not available/incomplete')
|
||||
return licenses
|
||||
time.sleep(5)
|
||||
return licenses
|
||||
@@ -47016,7 +47138,7 @@ def infoUsers(entityList):
|
||||
if max(seen_group_count.values()) > 1:
|
||||
printLine(f'{Ind.Spaces()}* {Msg.USER_HAS_MULTIPLE_DIRECT_OR_INHERITED_MEMBERSHIPS_IN_GROUP}')
|
||||
Ind.Decrement()
|
||||
if licenses:
|
||||
if getLicenses:
|
||||
printEntitiesCount(Ent.LICENSE, licenses)
|
||||
Ind.Increment()
|
||||
for u_license in licenses:
|
||||
@@ -52812,7 +52934,6 @@ def doCreateCourseStudentGroups():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
**kwargs)
|
||||
kvList[-1] = f"{studentGroup['title']}({studentGroup['id']})"
|
||||
if not csvPF:
|
||||
@@ -52859,7 +52980,6 @@ def doUpdateCourseStudentGroups():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
**kwargs)
|
||||
kvList[-1] = f"{studentGroup['title']}({studentGroup['id']})"
|
||||
entityActionPerformed(kvList)
|
||||
@@ -52893,7 +53013,6 @@ def doDeleteCourseStudentGroups():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
**kwargs)
|
||||
entityActionPerformed(kvList, i, count)
|
||||
except GAPI.notFound as e:
|
||||
@@ -52938,7 +53057,6 @@ def doClearCourseStudentGroups():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
courseId=courseId)
|
||||
except GAPI.notFound as e:
|
||||
entityActionFailedWarning([Ent.COURSE, courseId], str(e))
|
||||
@@ -52959,7 +53077,6 @@ def doClearCourseStudentGroups():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
**kwargs)
|
||||
entityActionPerformed(kvList, j, jcount)
|
||||
except GAPI.notFound as e:
|
||||
@@ -53033,7 +53150,6 @@ def doPrintCourseStudentGroups(showMembers=False):
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
courseId=courseId)
|
||||
except GAPI.notFound as e:
|
||||
entityActionFailedWarning([Ent.COURSE, courseId], str(e))
|
||||
@@ -53068,7 +53184,6 @@ def doPrintCourseStudentGroups(showMembers=False):
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
courseId=courseId, studentGroupId=studentGroupId)
|
||||
if showItemCountOnly:
|
||||
itemCount += len(students)
|
||||
@@ -53136,7 +53251,6 @@ def doProcessCourseStudentGroupMembers():
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
courseId=courseId, studentGroupId=studentGroupId)
|
||||
except GAPI.notFound as e:
|
||||
entityActionFailedWarning([Ent.COURSE, courseId, Ent.COURSE_STUDENTGROUP, studentGroupId], str(e))
|
||||
@@ -53172,7 +53286,6 @@ def doProcessCourseStudentGroupMembers():
|
||||
GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
|
||||
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||
previewVersion='V1_20250630_PREVIEW',
|
||||
**kwargs)
|
||||
entityActionPerformed(kvList, i, count)
|
||||
except (GAPI.alreadyExists, GAPI.notFound) as e:
|
||||
@@ -54099,6 +54212,7 @@ USER_CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = {
|
||||
'defaulteventlength': 'defaultEventLength',
|
||||
'format24hourtime': 'format24HourTime',
|
||||
'hideinvitations': 'hideInvitations',
|
||||
'hideinvitationssetting': 'hideInvitationsSetting',
|
||||
'hideweekends': 'hideWeekends',
|
||||
'locale': 'locale',
|
||||
'remindonrespondedeventsonly': 'remindOnRespondedEventsOnly',
|
||||
@@ -57268,6 +57382,31 @@ FILEPATH_FIELDS = ','.join(FILEPATH_FIELDS_TITLES)
|
||||
|
||||
DRIVE_TIME_OBJECTS = {'createdTime', 'viewedByMeTime', 'modifiedByMeTime', 'modifiedTime', 'restrictionTime', 'sharedWithMeTime', 'trashedTime'}
|
||||
|
||||
def _getIncludeLabels(includeLabels):
|
||||
labelIds = getEntityList(Cmd.OB_CLASSIFICATION_LABEL_ID, shlexSplit=True)
|
||||
for labelId in labelIds:
|
||||
includeLabels.add(normalizeDriveLabelID(labelId))
|
||||
|
||||
def _finalizeIncludeLabels(includeLabels):
|
||||
if includeLabels:
|
||||
return ','.join(includeLabels)
|
||||
return None
|
||||
|
||||
DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES = ['published']
|
||||
|
||||
def _getIncludePermissionsForView(includePermissionsForView):
|
||||
ipfwList = getEntityList(Cmd.OB_STRING_LIST)
|
||||
for ipfw in ipfwList:
|
||||
if ipfw in DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES:
|
||||
includePermissionsForView.add(ipfw)
|
||||
else:
|
||||
invalidChoiceExit(ipfw, DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES, True)
|
||||
|
||||
def _finalizeIncludePermissionsForView(includePermissionsForView):
|
||||
if includePermissionsForView:
|
||||
return ','.join(includePermissionsForView)
|
||||
return None
|
||||
|
||||
def _getDriveFieldSubField(field, fieldsList, parentsSubFields):
|
||||
field, subField = field.split('.', 1)
|
||||
if field in DRIVE_SUBFIELDS_CHOICE_MAP:
|
||||
@@ -57294,7 +57433,8 @@ class DriveFileFields():
|
||||
self.allFields = False
|
||||
self.OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
||||
self.fieldsList = []
|
||||
self.includeLabels = []
|
||||
self.includeLabels = set()
|
||||
self.includePermissionsForView = set()
|
||||
self.parentsSubFields = {'id': False, 'isRoot': False, 'rootFolderId': None}
|
||||
|
||||
def SetAllParentsSubFields(self):
|
||||
@@ -57328,9 +57468,9 @@ class DriveFileFields():
|
||||
else:
|
||||
_getDriveFieldSubField(field, self.fieldsList, self.parentsSubFields)
|
||||
elif myarg == 'includelabels':
|
||||
labelIds = getEntityList(Cmd.OB_CLASSIFICATION_LABEL_ID, shlexSplit=True)
|
||||
for labelId in labelIds:
|
||||
self.includeLabels.append(normalizeDriveLabelID(labelId))
|
||||
_getIncludeLabels(self.includeLabels)
|
||||
elif myarg == 'includepermissionsforview':
|
||||
_getIncludePermissionsForView(self.includePermissionsForView)
|
||||
elif myarg.find('.') != -1:
|
||||
_getDriveFieldSubField(myarg, self.fieldsList, self.parentsSubFields)
|
||||
elif myarg == 'orderby':
|
||||
@@ -57390,6 +57530,7 @@ def _formatFileDriveLabels(showLabels, labels, result, printMode, delimiter):
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# [(showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
# [stripcrsfromname] [formatjson]
|
||||
# gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
@@ -57399,6 +57540,7 @@ def _formatFileDriveLabels(showLabels, labels, result, printMode, delimiter):
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# [(showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
# [stripcrsfromname] [formatjson]
|
||||
def showFileInfo(users):
|
||||
@@ -57461,7 +57603,8 @@ def showFileInfo(users):
|
||||
DFF.SetAllParentsSubFields()
|
||||
skipObjects = skipObjects.union(DEFAULT_SKIP_OBJECTS)
|
||||
showNoParents = True
|
||||
includeLabels = ','.join(DFF.includeLabels)
|
||||
includeLabels = _finalizeIncludeLabels(DFF.includeLabels)
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(DFF.includePermissionsForView)
|
||||
pathFields = FILEPATH_FIELDS
|
||||
i, count, users = getEntityArgument(users)
|
||||
for user in users:
|
||||
@@ -57500,12 +57643,14 @@ def showFileInfo(users):
|
||||
try:
|
||||
result = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if followShortcuts and result['mimeType'] == MIMETYPE_GA_SHORTCUT:
|
||||
fileId = result['shortcutDetails']['targetId']
|
||||
result = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if stripCRsFromName:
|
||||
result['name'] = _stripControlCharsFromName(result['name'])
|
||||
driveId = result.get('driveId')
|
||||
@@ -58833,6 +58978,7 @@ SIZE_FIELD_CHOICE_MAP = {
|
||||
# [allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
# [showdrivename] [showshareddrivepermissions]
|
||||
# (showlabels details|ids)|(includelabels <DriveLabelIDList>)]
|
||||
# [includepermissionsforview published]
|
||||
# [showparentsidsaslist] [showpermissionslast]
|
||||
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
||||
# [stripcrsfromname]
|
||||
@@ -59013,7 +59159,8 @@ def printFileList(users):
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
|
||||
GAPI.BAD_REQUEST],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR],
|
||||
q=q, orderBy=DFF.orderBy, includeLabels=includeLabels, fields=pagesFields,
|
||||
q=q, orderBy=DFF.orderBy, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=pagesFields,
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], includeItemsFromAllDrives=True, supportsAllDrives=True)
|
||||
for childEntryInfo in children:
|
||||
childFileId = childEntryInfo['id']
|
||||
@@ -59030,8 +59177,12 @@ def printFileList(users):
|
||||
_printFileInfo(drive, user, childEntryInfo.copy(), stripCRsFromName)
|
||||
if childEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER and (maxdepth == -1 or depth < maxdepth):
|
||||
_printChildDriveFolderContents(drive, childEntryInfo, user, i, count, depth+1)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest):
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], invalidQuery(selectSubQuery), i, count)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest) as e:
|
||||
errMsg = str(e)
|
||||
if 'Invalid field selection' in errMsg or "Only a 'published' value is supported." in errMsg:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], errMsg, i, count)
|
||||
else:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], invalidQuery(selectSubQuery), i, count)
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
||||
|
||||
@@ -59253,7 +59404,8 @@ def printFileList(users):
|
||||
if filepath and not countsOnly:
|
||||
csvPF.AddTitles('paths')
|
||||
csvPF.SetFixPaths(True)
|
||||
includeLabels = ','.join(DFF.includeLabels)
|
||||
includeLabels = _finalizeIncludeLabels(DFF.includeLabels)
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(DFF.includePermissionsForView)
|
||||
csvPF.RemoveTitles(['capabilities'])
|
||||
if DLP.queryTimes and selectSubQuery:
|
||||
for queryTimeName, queryTimeValue in DLP.queryTimes.items():
|
||||
@@ -59326,7 +59478,8 @@ def printFileList(users):
|
||||
GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND,
|
||||
GAPI.NOT_FOUND, GAPI.TEAMDRIVE_MEMBERSHIP_REQUIRED],
|
||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR],
|
||||
q=DLP.fileIdEntity['query'], orderBy=DFF.orderBy, includeLabels=includeLabels,
|
||||
q=DLP.fileIdEntity['query'], orderBy=DFF.orderBy,
|
||||
includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=pagesFields, pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **btkwargs)
|
||||
for files in feed:
|
||||
if showLabels is not None:
|
||||
@@ -59357,7 +59510,7 @@ def printFileList(users):
|
||||
DLP.GetLocationFileIdsFromTree(fileTree, fileIdEntity)
|
||||
except (GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest) as e:
|
||||
errMsg = str(e)
|
||||
if 'Invalid field selection' in errMsg:
|
||||
if 'Invalid field selection' in errMsg or "Only a 'published' value is supported." in errMsg:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], errMsg, i, count)
|
||||
break
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, None], invalidQuery(DLP.fileIdEntity['query']), i, count)
|
||||
@@ -59389,8 +59542,9 @@ def printFileList(users):
|
||||
else:
|
||||
try:
|
||||
fileEntryInfo = callGAPI(drive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
||||
fileId=fileId, includeLabels=includeLabels, fields=fields, supportsAllDrives=True)
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS+[GAPI.INVALID],
|
||||
fileId=fileId, includeLabels=includeLabels, includePermissionsForView=includePermissionsForView,
|
||||
fields=fields, supportsAllDrives=True)
|
||||
if stripCRsFromName:
|
||||
fileEntryInfo['name'] = _stripControlCharsFromName(fileEntryInfo['name'])
|
||||
if showLabels is not None:
|
||||
@@ -59401,6 +59555,9 @@ def printFileList(users):
|
||||
_formatFileDriveLabels(showLabels, labels, fileEntryInfo, True, delimiter)
|
||||
if filepath:
|
||||
fileTree[fileId] = {'info': fileEntryInfo}
|
||||
except GAPI.invalid as e:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], str(e), j, jcount)
|
||||
continue
|
||||
except GAPI.fileNotFound:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], Msg.NOT_FOUND, j, jcount)
|
||||
continue
|
||||
@@ -61928,6 +62085,8 @@ def initCopyMoveOptions(copyCmd):
|
||||
'copySubFilesOwnedBy': {},
|
||||
'copyPermissionRoles': set(DRIVEFILE_ACL_ROLES_MAP.values()),
|
||||
'copyPermissionTypes': set(DRIVEFILE_ACL_PERMISSION_TYPES),
|
||||
'checkModifiedTime': False,
|
||||
'startEndTime': StartEndTime(),
|
||||
}
|
||||
|
||||
DUPLICATE_FILE_CHOICES = {
|
||||
@@ -62086,6 +62245,9 @@ def getCopyMoveOptions(myarg, copyMoveOptions):
|
||||
copyMoveOptions['copySubFilesOwnedBy']['value'] = set(getString(Cmd.OB_EMAIL_ADDRESS_LIST).replace(',', ' ').lower().split())
|
||||
elif copyMoveOptions['copySubFilesOwnedBy']['mode'] in {'regex', 'notregex'}:
|
||||
copyMoveOptions['copySubFilesOwnedBy']['value'] = getREPattern(re.IGNORECASE)
|
||||
elif myarg in {'start', 'starttime', 'end', 'endtime', 'range'}:
|
||||
copyMoveOptions['startEndTime'].Get(myarg)
|
||||
copyMoveOptions['checkModifiedTime'] = True
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
@@ -62642,6 +62804,7 @@ copyReturnItemMap = {
|
||||
# notusers <EmailAddressList>|
|
||||
# regex <REMatchPattern>|
|
||||
# notregex <REMatchPattern>]
|
||||
# [([start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>])|(range <Date>|<Time> <Date>|<Time>)]|
|
||||
# [copysubfolders [<Boolean>]] [foldernamematchpattern <REMatchPattern>]
|
||||
# [copysubshortcuts [<Boolean>]] [shortcutnamematchpattern <REMatchPattern>]
|
||||
# [duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
@@ -62878,6 +63041,14 @@ def copyDriveFile(users):
|
||||
return False
|
||||
if not copyMoveOptions['mimeTypeCheck'].Check(childMimeType):
|
||||
return False
|
||||
if copyMoveOptions['checkModifiedTime']:
|
||||
childModifiedTime = child.get('modifiedTime', None)
|
||||
if not childModifiedTime:
|
||||
return False
|
||||
childModifiedTime = formatLocalTime(childModifiedTime)
|
||||
if ((copyMoveOptions['startEndTime'].startTime is not None and childModifiedTime < copyMoveOptions['startEndTime'].startTime) or
|
||||
(copyMoveOptions['startEndTime'].endTime is not None and childModifiedTime > copyMoveOptions['startEndTime'].endTime)):
|
||||
return False
|
||||
nameMatchPattern = copyMoveOptions['fileNameMatchPattern']
|
||||
return not nameMatchPattern or nameMatchPattern.match(childName)
|
||||
|
||||
@@ -67370,8 +67541,6 @@ def infoDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
def doInfoDriveFileACLs():
|
||||
infoDriveFileACLs([_getAdminEmail()], True)
|
||||
|
||||
DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES = ['published']
|
||||
|
||||
def getDriveFilePermissionsFields(myarg, fieldsList):
|
||||
if myarg in DRIVE_PERMISSIONS_SUBFIELDS_CHOICE_MAP:
|
||||
fieldsList.append(DRIVE_PERMISSIONS_SUBFIELDS_CHOICE_MAP[myarg])
|
||||
@@ -67426,7 +67595,7 @@ def printShowDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
addTitle = None
|
||||
roles = set()
|
||||
oneItemPerRow = pmselect = showTitles = False
|
||||
includePermissionsForView = None
|
||||
includePermissionsForView = set()
|
||||
fieldsList = []
|
||||
OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
||||
PM = PermissionMatch()
|
||||
@@ -67461,13 +67630,14 @@ def printShowDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
elif PM.ProcessArgument(myarg):
|
||||
pass
|
||||
elif myarg == 'includepermissionsforview':
|
||||
includePermissionsForView = getChoice(DRIVEFILE_PERMISSIONS_FOR_VIEW_CHOICES)
|
||||
_getIncludePermissionsForView(includePermissionsForView)
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
||||
_checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess)
|
||||
if fieldsList:
|
||||
if roles:
|
||||
fieldsList.append('role')
|
||||
includePermissionsForView = _finalizeIncludePermissionsForView(includePermissionsForView)
|
||||
fields = getItemFieldsFromFieldsList('permissions', fieldsList, True)
|
||||
printKeys, timeObjects = _getDriveFileACLPrintKeysTimeObjects()
|
||||
i, count, users = getEntityArgument(users)
|
||||
@@ -74793,7 +74963,10 @@ def printShowMessagesThreads(users, entityType):
|
||||
csvPF.SetTitles(sortTitles)
|
||||
else:
|
||||
sortTitles = ['User', 'threadId', 'id']
|
||||
sortTitles.extend(defaultHeaders)
|
||||
if show_all_headers:
|
||||
sortTitles.extend(defaultHeaders)
|
||||
elif headersToShow:
|
||||
sortTitles.extend([SMTP_HEADERS_MAP.get(header, header) for header in headersToShow])
|
||||
if show_size:
|
||||
sortTitles.append('SizeEstimate')
|
||||
if show_labels:
|
||||
@@ -76490,10 +76663,14 @@ def printShowLanguage(users):
|
||||
SIG_REPLY_HTML = 0
|
||||
SIG_REPLY_COMPACT = 1
|
||||
SIG_REPLY_FORMAT = 2
|
||||
SIG_REPLY_TEMPLATE = 3 # Does not go in MAP
|
||||
SIG_REPLY_OPTIONS_MAP = {'html': SIG_REPLY_HTML, 'compact': SIG_REPLY_COMPACT, 'format': SIG_REPLY_FORMAT}
|
||||
SMTPMSA_DISPLAY_FIELDS = ['host', 'port', 'securityMode']
|
||||
|
||||
def _showSendAs(result, j, jcount, sigReplyFormat, verifyOnly=False):
|
||||
if sigReplyFormat == SIG_REPLY_TEMPLATE:
|
||||
writeStdout(f"{escapeCRsNLs(result.get('signature', 'None'))}\n")
|
||||
return
|
||||
if result['displayName']:
|
||||
printEntity([Ent.SENDAS_ADDRESS, f'{result["displayName"]} <{result["sendAsEmail"]}>'], j, jcount)
|
||||
else:
|
||||
@@ -76758,7 +76935,7 @@ def printShowSendAs(users):
|
||||
|
||||
# gam <UserTypeEntity> print signature [compact]
|
||||
# [primary] [verifyonly] [todrive <ToDriveAttribute>*]
|
||||
# gam <UserTypeEntity> show signature|sig [compact|format|html]
|
||||
# gam <UserTypeEntity> show signature|sig [compact|format|html|template]
|
||||
# [primary] [verifyonly]
|
||||
def printShowSignature(users):
|
||||
csvPF = CSVPrintFile(['User', 'displayName', 'sendAsEmail', 'replyToAddress',
|
||||
@@ -76779,6 +76956,8 @@ def printShowSignature(users):
|
||||
verifyOnly = True
|
||||
elif (not csvPF and myarg in SIG_REPLY_OPTIONS_MAP) or (csvPF and myarg == 'compact'):
|
||||
sigReplyFormat = SIG_REPLY_OPTIONS_MAP[myarg]
|
||||
elif not csvPF and myarg == 'template':
|
||||
sigReplyFormat = SIG_REPLY_TEMPLATE
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
i, count, users = getEntityArgument(users)
|
||||
@@ -79465,6 +79644,7 @@ MAIN_ADD_CREATE_FUNCTIONS = {
|
||||
Cmd.ARG_GROUP: doCreateGroup,
|
||||
Cmd.ARG_GUARDIAN: doInviteGuardian,
|
||||
Cmd.ARG_GUARDIANINVITATION: doInviteGuardian,
|
||||
Cmd.ARG_GUESTUSER: doCreateGuestUser,
|
||||
Cmd.ARG_INBOUNDSSOASSIGNMENT: doCreateInboundSSOAssignment,
|
||||
Cmd.ARG_INBOUNDSSOCREDENTIAL: doCreateInboundSSOCredential,
|
||||
Cmd.ARG_INBOUNDSSOPROFILE: doCreateInboundSSOProfile,
|
||||
@@ -81198,6 +81378,7 @@ USER_COMMANDS_OBJ_ALIASES = {
|
||||
Cmd.ARG_GUARDIANINVITATIONS: Cmd.ARG_GUARDIANINVITATION,
|
||||
Cmd.ARG_GUARDIANINVITE: Cmd.ARG_GUARDIANINVITATION,
|
||||
Cmd.ARG_GUARDIANS: Cmd.ARG_GUARDIAN,
|
||||
Cmd.ARG_GUESTUSERS: Cmd.ARG_GUESTUSER,
|
||||
Cmd.ARG_HOLD: Cmd.ARG_VAULTHOLD,
|
||||
Cmd.ARG_HOLDS: Cmd.ARG_VAULTHOLD,
|
||||
Cmd.ARG_IMAP4: Cmd.ARG_IMAP,
|
||||
|
||||
@@ -812,6 +812,12 @@ def getVersion(api):
|
||||
api = _INFO[api].get('mappedAPI', api)
|
||||
return (api, version, v2discovery)
|
||||
|
||||
def getAPIsList():
|
||||
apisList = set()
|
||||
for api, value in _INFO.items():
|
||||
apisList.add(value.get('mappedAPI', api))
|
||||
return apisList
|
||||
|
||||
def getClientScopesSet(api):
|
||||
return {scope['scope'] for scope in _CLIENT_SCOPES if scope['api'] == api}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@@ -123,6 +123,8 @@ CSV_OUTPUT_HEADER_DROP_FILTER = 'csv_output_header_drop_filter'
|
||||
CSV_OUTPUT_HEADER_FORCE = 'csv_output_header_force'
|
||||
# Orde output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'csv_output_header_order'
|
||||
# Required output column headers
|
||||
CSV_OUTPUT_HEADER_REQUIRED = 'csv_output_header_required'
|
||||
# Line terminator in CSV output file
|
||||
CSV_OUTPUT_LINE_TERMINATOR = 'csv_output_line_terminator'
|
||||
# Quote character in CSV output file
|
||||
@@ -151,6 +153,8 @@ CUSTOMER_ID = 'customer_id'
|
||||
DEBUG_LEVEL = 'debug_level'
|
||||
# redact sensitive credentials from debug output
|
||||
DEBUG_REDACTION = 'debug_redaction'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'developer_preview_apis'
|
||||
# Developer Preview API Key
|
||||
DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
||||
# When retrieving lists of ChromeOS devices from API, how many should be retrieved in each chunk
|
||||
@@ -218,6 +222,8 @@ NUM_TBATCH_THREADS = 'num_tbatch_threads'
|
||||
NUM_THREADS = 'num_threads'
|
||||
# Path to oauth2.txt
|
||||
OAUTH2_TXT = 'oauth2_txt'
|
||||
# File permissions for oauth2.txt.lock
|
||||
OAUTH2_TXT_LOCK_MODE = 'oauth2_txt_lock_mode'
|
||||
# Path to oauth2service.json
|
||||
OAUTH2SERVICE_JSON = 'oauth2service_json'
|
||||
# Output date format, empty defalts to ISOFormat
|
||||
@@ -325,6 +331,7 @@ CSV_INPUT_ROW_FILTER_ITEMS = {CSV_INPUT_ROW_FILTER, CSV_INPUT_ROW_FILTER_MODE,
|
||||
|
||||
CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER,
|
||||
CSV_OUTPUT_HEADER_FORCE, CSV_OUTPUT_HEADER_ORDER,
|
||||
CSV_OUTPUT_HEADER_REQUIRED,
|
||||
CSV_OUTPUT_ROW_FILTER, CSV_OUTPUT_ROW_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER, CSV_OUTPUT_ROW_DROP_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_LIMIT}
|
||||
@@ -369,6 +376,7 @@ Defaults = {
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: '',
|
||||
CSV_OUTPUT_HEADER_FORCE: '',
|
||||
CSV_OUTPUT_HEADER_ORDER: '',
|
||||
CSV_OUTPUT_HEADER_REQUIRED: '',
|
||||
CSV_OUTPUT_LINE_TERMINATOR: 'lf',
|
||||
CSV_OUTPUT_QUOTE_CHAR: '\'"\'',
|
||||
CSV_OUTPUT_ROW_FILTER: '',
|
||||
@@ -383,6 +391,7 @@ Defaults = {
|
||||
CUSTOMER_ID: MY_CUSTOMER,
|
||||
DEBUG_LEVEL: '0',
|
||||
DEBUG_REDACTION: TRUE,
|
||||
DEVELOPER_PREVIEW_APIS: '',
|
||||
DEVELOPER_PREVIEW_API_KEY: '',
|
||||
DEVICE_MAX_RESULTS: '200',
|
||||
DOMAIN: '',
|
||||
@@ -416,6 +425,7 @@ Defaults = {
|
||||
NUM_TBATCH_THREADS: '2',
|
||||
NUM_THREADS: '5',
|
||||
OAUTH2_TXT: FN_OAUTH2_TXT,
|
||||
OAUTH2_TXT_LOCK_MODE: '644',
|
||||
OAUTH2SERVICE_JSON: FN_OAUTH2SERVICE_JSON,
|
||||
OUTPUT_DATEFORMAT: '',
|
||||
OUTPUT_TIMEFORMAT: '',
|
||||
@@ -481,7 +491,7 @@ TYPE_EMAIL_OPTIONAL = 'emao'
|
||||
TYPE_FILE = 'file'
|
||||
TYPE_FLOAT = 'floa'
|
||||
TYPE_HEADERFILTER = 'heaf'
|
||||
TYPE_HEADERFORCE = 'hefo'
|
||||
TYPE_HEADERFORCEREQUIRED = 'hefr'
|
||||
TYPE_HEADERORDER = 'heor'
|
||||
TYPE_INTEGER = 'inte'
|
||||
TYPE_LANGUAGE = 'lang'
|
||||
@@ -538,8 +548,9 @@ VAR_INFO = {
|
||||
CSV_OUTPUT_FIELD_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
|
||||
CSV_OUTPUT_HEADER_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCE},
|
||||
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
|
||||
CSV_OUTPUT_HEADER_ORDER: {VAR_TYPE: TYPE_HEADERORDER},
|
||||
CSV_OUTPUT_HEADER_REQUIRED: {VAR_TYPE: TYPE_HEADERFORCEREQUIRED},
|
||||
CSV_OUTPUT_LINE_TERMINATOR: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'cr': '\r', 'lf': '\n', 'crlf': '\r\n'}},
|
||||
CSV_OUTPUT_QUOTE_CHAR: {VAR_TYPE: TYPE_CHARACTER},
|
||||
CSV_OUTPUT_ROW_FILTER: {VAR_TYPE: TYPE_ROWFILTER},
|
||||
@@ -554,6 +565,7 @@ VAR_INFO = {
|
||||
CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'CUSTOMER_ID', VAR_LIMITS: (0, None)},
|
||||
DEBUG_LEVEL: {VAR_TYPE: TYPE_INTEGER, VAR_SIGFILE: 'debug.gam', VAR_LIMITS: (0, None), VAR_SFFT: ('0', '4')},
|
||||
DEBUG_REDACTION: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
DEVELOPER_PREVIEW_APIS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVELOPER_PREVIEW_API_KEY: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
||||
@@ -587,6 +599,7 @@ VAR_INFO = {
|
||||
NUM_TBATCH_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||
NUM_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_ENVVAR: 'GAM_THREADS', VAR_LIMITS: (1, 1000)},
|
||||
OAUTH2_TXT: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHFILE', VAR_ACCESS: os.R_OK | os.W_OK},
|
||||
OAUTH2_TXT_LOCK_MODE: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'644': 0o644, '664': 0o664, '666': 0o666}},
|
||||
OAUTH2SERVICE_JSON: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHSERVICEFILE', VAR_ACCESS: os.R_OK | os.W_OK},
|
||||
OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
|
||||
@@ -928,6 +928,8 @@ class GamCLArgs():
|
||||
ARG_GUARDIANINVITE = 'guardianinvite'
|
||||
ARG_GUARDIANINVITATION = 'guardianinvitation'
|
||||
ARG_GUARDIANINVITATIONS = 'guardianinvitations'
|
||||
ARG_GUESTUSER = 'guestuser'
|
||||
ARG_GUESTUSERS = 'guestusers'
|
||||
ARG_HOLD = 'hold'
|
||||
ARG_HOLDS = 'holds'
|
||||
ARG_IMAP = 'imap'
|
||||
|
||||
@@ -253,6 +253,7 @@ class GamEntity():
|
||||
GUARDIAN = 'guar'
|
||||
GUARDIAN_INVITATION = 'gari'
|
||||
GUARDIAN_AND_INVITATION = 'gani'
|
||||
GUEST_USER = 'gstu'
|
||||
IAM_POLICY = 'iamp'
|
||||
IMAP_ENABLED = 'imap'
|
||||
INBOUND_SSO_ASSIGNMENT = 'insa'
|
||||
@@ -358,7 +359,7 @@ class GamEntity():
|
||||
SNIPPETS_ENABLED = 'snip'
|
||||
SSO_KEY = 'ssok'
|
||||
SSO_SETTINGS = 'ssos'
|
||||
SOURCE_USER = 'src'
|
||||
SOURCE_USER = 'srcu'
|
||||
SPREADSHEET = 'sprd'
|
||||
SPREADSHEET_RANGE = 'ssrn'
|
||||
START_TIME = 'strt'
|
||||
@@ -620,6 +621,7 @@ class GamEntity():
|
||||
GROUP_MEMBERSHIP_TREE: ['Group Membership Trees', 'Group Membership Tree'],
|
||||
GROUP_SETTINGS: ['Group Settings', 'Group Settings'],
|
||||
GROUP_TREE: ['Group Trees', 'Group Tree'],
|
||||
GUEST_USER: ['Guest Users', 'Guest User'],
|
||||
GUARDIAN: ['Guardians', 'Guardian'],
|
||||
GUARDIAN_INVITATION: ['Guardian Invitations', 'Guardian Invitation'],
|
||||
GUARDIAN_AND_INVITATION: ['Guardians and Invitations', 'Guardian and Invitation'],
|
||||
|
||||
@@ -69,6 +69,8 @@ CSV_OUTPUT_HEADER_FILTER = 'cohf'
|
||||
CSV_OUTPUT_HEADER_FORCE = 'cofh'
|
||||
# Order output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'coho'
|
||||
# Required output column headers
|
||||
CSV_OUTPUT_HEADER_REQUIRED = 'corh'
|
||||
# No escape character in CSV output file
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR = 'cone'
|
||||
# Quote character in CSV output file
|
||||
@@ -111,6 +113,8 @@ DEBUG_LEVEL = 'dbgl'
|
||||
DEBUG_REDACTION = 'dbrd'
|
||||
# Decoded ID token
|
||||
DECODED_ID_TOKEN = 'didt'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'dapi'
|
||||
# Index of start of <UserTypeEntity> in command line
|
||||
ENTITY_CL_DELAY_START = 'ecld'
|
||||
ENTITY_CL_START = 'ecls'
|
||||
@@ -246,6 +250,7 @@ Globals = {
|
||||
CSV_OUTPUT_HEADER_FILTER: [],
|
||||
CSV_OUTPUT_HEADER_FORCE: [],
|
||||
CSV_OUTPUT_HEADER_ORDER: [],
|
||||
CSV_OUTPUT_HEADER_REQUIRED: [],
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR: None,
|
||||
CSV_OUTPUT_QUOTE_CHAR: None,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER: [],
|
||||
@@ -267,6 +272,7 @@ Globals = {
|
||||
DEBUG_LEVEL: 0,
|
||||
DEBUG_REDACTION: True,
|
||||
DECODED_ID_TOKEN: None,
|
||||
DEVELOPER_PREVIEW_APIS: set(),
|
||||
ENTITY_CL_DELAY_START: 1,
|
||||
ENTITY_CL_START: 1,
|
||||
EXTRA_ARGS_LIST: [],
|
||||
|
||||
@@ -236,6 +236,7 @@ DATA_TRANSFER_COMPLETED = 'Data Transfer completed: {0}\n'
|
||||
DATA_UPLOADED_TO_DRIVE_FILE = 'Data uploaded to Drive File'
|
||||
DEFAULT_SMIME = 'Default S/MIME'
|
||||
DELETED = 'Deleted'
|
||||
DEVELOPER_PREVIEW_REQUIRED = 'Developer Preview is required for this command\n'
|
||||
DEVICE_LIST_BUG = 'GAM hit Google internal bug 237397223. Please file a Google Support ticket stating that you are encountering this bug.'
|
||||
DEVICE_LIST_BUG_WORKAROUND_NOT_POSSIBLE = 'GAM workaround for this issue only works if orderby argument is not used and query does not contain \'register\'.'
|
||||
DEVICE_LIST_BUG_ATTEMPTING_WORKAROUND = 'GAM is attempting to work around the bug by filtering for devices created on or after the newest we\'ve seen ({0})...\n'
|
||||
|
||||
@@ -283,6 +283,8 @@
|
||||
<ChatEmoji> ::= emojiname <ChatEmojiName> | customemojis/<String>
|
||||
<ChatMember> ::= spaces/<String>/members/<String>
|
||||
<ChatMessage> ::= spaces/<String>/messages/<String>
|
||||
<ChatSection> ::= users/<String>/sections/<String> | sections/<String> | section <String>
|
||||
<ChatSectionItem> ::= users/<String>/sections/<String>/items/<String> | sections/<String>/items/<String>
|
||||
<ChatSpace> ::= spaces/<String> | space <String> | space spaces/<String>
|
||||
<ChatThread> ::= spaces/<String>/threads/<String>
|
||||
<ChromeProfilePermanentID> ::= <String>
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
There are seven values in `gam.cfg` that can be used to filter the output from `gam print` commands.
|
||||
* `csv_output_header_filter` - A list of `<RegularExpressions>` used to select specific column headers to include
|
||||
* `csv_output_header_drop_filter` - A list of `<RegularExpressions>` used to select specific column headers to exclude
|
||||
* `csv_output_header_force` - A list of <Strings> used to specify the exact column headers to include
|
||||
* `csv_output_header_order` - A list of <Strings> used to specify the column header order; any headers in the file but not in the list will appear after the headers in the list.
|
||||
* `csv_output_header_force` - A list of `<Strings>` used to specify the exact column headers to include
|
||||
* `csv_output_header_order` - A list of `<Strings>` used to specify the column header order; any headers in the file but not in the list will appear after the header* `csv_output_header_required` - A list of `<Strings>` used to specify column headers that are included even if the print command doesn't return them
|
||||
s in the list.
|
||||
* `csv_output_row_filter` - A list or JSON dictionary used to include specific rows based on column values
|
||||
* `csv_output_row_drop_filter` - A list or JSON dictionary used to exclude specific rows based on column values
|
||||
* `csv_output_row_limit` - A limit on the number of rows written
|
||||
|
||||
@@ -595,7 +595,7 @@ By default, Gam displays event details, use `countsonly` to display only the num
|
||||
|
||||
```
|
||||
gam calendar <CalendarEntity> print events [<EventEntity>] <EventDisplayProperty>*
|
||||
[fields <EventFieldNameList>] [showdayofweek]
|
||||
[fields <EventFieldNameList>] [showdayofweek] [attendeeslist]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[eventrowfilter]
|
||||
[countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
||||
@@ -609,6 +609,10 @@ option `singleevents` to display all instances of a recurring event.
|
||||
|
||||
`showdayofweek` displays columns `start.dayOfWeek` and `end.dayOfWeek` when event start and end times are displayed.
|
||||
|
||||
By default, each attendee is displayed in a separate column; `attendeeslist` causes GAM to display
|
||||
the attendee email addresses in a single column `attendeesList`; no attendee details are displayed.
|
||||
The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Add additional columns of data from the command line to the output after the calendarId.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
|
||||
@@ -282,10 +282,11 @@ gam show chromepolicies
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
[filter <StringList>] [namespace <NamespaceList>]
|
||||
[show all|direct|inherited]
|
||||
[formatjson]
|
||||
```
|
||||
By default, all Chrome policies for the OU or group are displayed.
|
||||
* `filter <String>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `filter <StringList>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `show all` - For OUs, display policies regardless of where set; this is the default
|
||||
* `show direct` - For OUs, display policies set directly in the OU
|
||||
* `show inherited` - For OUs, display policies set in a parent OU
|
||||
@@ -329,15 +330,25 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
gam print chromepolicies [todrive <ToDriveAttribute>*]
|
||||
((ou|orgunit <OrgUnitItem> [show all|direct|inherited])|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
[filter <String>] [namespace <NamespaceList>]
|
||||
[filter <StringList>] [namespace <NamespaceList>]
|
||||
[show all|direct|inherited] [shownopolicy]
|
||||
[[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all Chrome policies for the OU or group are displayed.
|
||||
* `filter <String>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `filter <StringList>` - Display policies based on fields like its resource name, description and additionalTargetKeyNames.
|
||||
* `show all` - For OUs, display policies regardless of where set; this is the default
|
||||
* `show direct` - For OUs, display policies set directly in the OU
|
||||
* `show inherited` - For OUs, display policies set in a parent OU
|
||||
|
||||
Use option `shownopolicy` to display output like the following if no policies apply to the OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
These are the default namespaces; use `namespace <NamespaceList>` to override.
|
||||
* `default`
|
||||
* chrome.users
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
- [Action ChromeOS devices](#action-chromeos-devices)
|
||||
- [Send remote commands to ChromeOS devices](#send-remote-commands-to-chromeos-devices)
|
||||
- [Action Examples](#action-examples)
|
||||
- [Bulk Action Example](#bulk-action-example)
|
||||
- [ChromeOS device lists](#chromeos-device-lists)
|
||||
- [Display information about ChromeOS devices](#display-information-about-chromeos-devices)
|
||||
- [Print ChromeOS devices](#print-chromeos-devices)
|
||||
@@ -445,12 +446,19 @@ is configurable from 0 to some large number. If the status reaches `EXPIRED`, `C
|
||||
wipe_users|
|
||||
take_a_screenshot
|
||||
|
||||
gam <CrOSTypeEntity> issuecommand command <CrOSCommand> [times_to_check_status <Integer>] [doit]
|
||||
gam <CrOSTypeEntity> issuecommand command <CrOSCommand>
|
||||
[times_to_check_status <Integer>] [csv] [doit]
|
||||
```
|
||||
By default, when a Chrome command is issued, GAM outputs details of the command status as indented keywords and values.
|
||||
* `csv` - Output the details in CSV format.
|
||||
|
||||
If the final status is not reached before GAM exits, you can issue the following commands to continue checking the status.
|
||||
```
|
||||
gam <CrOSTypeEntity> getcommand commandid <CommandID> [times_to_check_status <Integer>]
|
||||
gam <CrOSTypeEntity> getcommand commandid <CommandID>
|
||||
[times_to_check_status <Integer>] [csv]
|
||||
```
|
||||
By default, when a Chrome command status is read, GAM outputs details of the command status as indented keywords and values.
|
||||
* `csv` - Output the details in CSV format.
|
||||
|
||||
### Action Examples
|
||||
Remove user profile data from the device; the device will remain enrolled and connected.
|
||||
@@ -476,6 +484,40 @@ Use `wipe_users` if that's going to create too much work for you.
|
||||
```
|
||||
gam cros_ou /StudentCarts issuecommand command remote_powerwash times_to_check_status 0 doit
|
||||
```
|
||||
|
||||
### Bulk Action example
|
||||
You want to issue commands to many ChromeOS devices and monitor the results.
|
||||
|
||||
Assume a Google Sheet with two tabs: Commands and Results
|
||||
The Commands tab has at least two columns: serialNumber and command
|
||||
```
|
||||
serialNumber,command
|
||||
abc123def123,reboot
|
||||
abc123def456,remote_powerwash
|
||||
abc123def789,wipe_users
|
||||
```
|
||||
Here is the URL for the Commands tab
|
||||
```
|
||||
https://docs.google.com/spreadsheets/d/12349lNWvZwzJhwxyz/edit?gid=1588227640#gid=1588227640
|
||||
<SheetFileID> = 12349lNWvZwzJhwxyz
|
||||
<CommandTabID> = 1588227640
|
||||
Here is the URL for the Commands tab
|
||||
https://docs.google.com/spreadsheets/d/12349lNWvZwzJhwxyz/edit?gid=2102420937#gid=2102420937
|
||||
<SheetFileID> = 12349lNWvZwzJhwxyz
|
||||
<ResultsTabID> = 2102420937
|
||||
```
|
||||
Replace `user@domain.com` with the email address of the Google Sheet owner.
|
||||
|
||||
Issue the commands from the Commands tab and write the results to the Results tab; copy the `serialNumber` to the Results tab.
|
||||
```
|
||||
gam config num_threads 20 redirect csv - multiprocess sortheaders serialNumber todrive tduser user@domain.com tdfileID <SheetFileID> tdsheet id:<ResultsTabID> tdupdatesheet tdretaintitle redirect stderr - multiprocess csv gsheet user@domain.com <SheetFileID> id:<CommandsTabID> gam cros_sn "~serialNumber" issuecommand command "~command" doit csv addcsvdata serialNumber "~serialNumber"
|
||||
```
|
||||
Monitor the results by reading and updating the results from/to the Results tab; copy the `serialNumber` to the Results tab.
|
||||
```
|
||||
gam config num_threads 20 redirect csv - multiprocess sortheaders serialNumber todrive tduser user@domain.com tdfileID <SheetFileID> tdsheet id:<ResultsTabID> tdupdatesheet tdretaintitle redirect stderr - multiprocess csv gsheet user@domain.com <SheetFileID> id:<ResultsTabID> gam cros "~deviceId" getcommand commandid "~commandId" csv addcsvdata serialNumber "~serialNumber"
|
||||
```
|
||||
You can use additional `addcsvdata` options to copy device identifying information from the source to the destination.
|
||||
|
||||
## ChromeOS device lists
|
||||
ChromeOS devices have lists of data: `<CrOSListFieldName>`, `<CrOSActivityListFieldName>`, `<CrOSTelemetryListFieldName>`.
|
||||
All lists except `recentusers` are in ascending order (oldest to newest). As these lists can contain many entries,
|
||||
|
||||
@@ -12,33 +12,8 @@
|
||||
- [Display student group membership counts](#display-student-group-membership-counts)
|
||||
|
||||
## Notes
|
||||
These commands wera added in version 7.20.00.
|
||||
|
||||
To use these commands your project must be enrolled the Developer Preview program.
|
||||
* https://developers.google.com/workspace/preview
|
||||
|
||||
You will need your GAM project number.
|
||||
* Login as an existing super admin at console.cloud.google.com
|
||||
* In the upper left click the three lines to the left of Google Cloud and select IAM & Admin
|
||||
* Under IAM & Admin select IAM
|
||||
* Click in the box to the right of Google Cloud
|
||||
* Click the three dots at the right and select Manage Resources
|
||||
* Click the three dots at the end of the line for your GAM project
|
||||
* Click Settings
|
||||
* You will see the Project number; save it
|
||||
|
||||
You will need an API key
|
||||
* In the upper left click the three lines to the left of Google Cloud and select APIs & Services
|
||||
* Under APIs & Services select Credentials
|
||||
* If you already have an API key, click Show key at the end of the line and save the value
|
||||
* If you don't have an API key, click +Credentials and select API key
|
||||
* Save the displayed API key
|
||||
* Click close
|
||||
|
||||
Issue the following GAM command:
|
||||
`gam config developer_preview_api_key <API Key Value> save`
|
||||
|
||||
Once you get an email from Google saying that your project has been registered you can use these commands.
|
||||
These commands were added in version 7.20.00 and required enrollment in the Developer Preview program.
|
||||
As of 7.32.04 Developer Preview enrollment is no longer required.
|
||||
|
||||
## API documentation
|
||||
* [Google Classroom API](https://developers.google.com/classroom/reference/rest)
|
||||
@@ -122,7 +97,7 @@ gam clear course-studentgroups
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
```
|
||||
|
||||
When creating student groups, the API does not check for duplicate titles; you can have multiple student grpups
|
||||
When creating student groups, the API does not check for duplicate titles; you can have multiple student groups
|
||||
with the same title; they will have unique `<StudentGroupID>s`.
|
||||
|
||||
The `update|delete course-studentgroups` commands manage a specific course.
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
- [Display user group member options](#display-user-group-member-options)
|
||||
- [Display group membership in CSV format](#display-group-membership-in-csv-format)
|
||||
- [Display group membership in hierarchical format](#display-group-membership-in-hierarchical-format)
|
||||
|
||||
- [Manage external users in groups with allowExternalMembers False](#manage-external-users-in-groups-with-allowexternalmembers-false)
|
||||
## API documentation
|
||||
* [Cloud Identity Groups Overview](https://cloud.google.com/identity/docs/groups)
|
||||
* [Cloud Identity Groups API - Groups](https://cloud.google.com/identity/docs/reference/rest/v1/groups)
|
||||
@@ -349,7 +349,8 @@ gam print cigroup-members [todrive <ToDriveAttribute>*]
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[verifyallowexternal [<Boolean>]]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
<CIGroupMembersFieldName>* [fields <CIGroupMembersFieldNameList>]
|
||||
[minimal|basic|full]
|
||||
@@ -381,11 +382,23 @@ By default, all members, managers and owners in the group are displayed; these o
|
||||
By default, all types of members (cbcmbrowser, chromeosdevice, customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group and adds the column `category` that shows whether the member
|
||||
is `external` or `internal`.
|
||||
|
||||
The option `verifyallowexternal` causes GAM to only display `external` users in groups with `allowExternalMembers=False'.
|
||||
|
||||
By default, members that are groups are displayed as a single entry of type GROUP; this option recursively expands group members to display their user members.
|
||||
* `recursive` - Recursively expand group members
|
||||
@@ -442,9 +455,10 @@ gam show cigroup-members
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[minimal|basic|full]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[(depth <Number>) | includederivedmembership]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
@@ -470,6 +484,18 @@ By default, all members, managers and owners in the group are displayed; these o
|
||||
By default, all types of members (cbcmbrowser, chromeosdevice, customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
@@ -507,3 +533,41 @@ To show the structure of all groups you can do the following; it will be time co
|
||||
```
|
||||
gam redirect stdout ./groups.txt show cigroup-members types group
|
||||
```
|
||||
|
||||
## Manage external users in groups with allowExternalMembers False
|
||||
|
||||
* See: https://support.google.com/a/answer/16778447
|
||||
|
||||
Get external members of groups with allowExternalMembers = False
|
||||
```
|
||||
gam redirect csv ./ExternalMembersGroupsWithAEMFalse.csv print cigroup-members verifyallowexternal
|
||||
```
|
||||
|
||||
Update selected groups to allowExternalMembers = True
|
||||
* Add a column labelled `update` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For groups to be updated, add an x to the `update` column for any member of the group to be updated
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToAEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield update x allowexternalmembers true
|
||||
```
|
||||
|
||||
Update all groups to allowExternalMembers = True
|
||||
**Caution, be sure that this is what you want**
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group allowexternalmembers true
|
||||
```
|
||||
|
||||
Delete selected external members from groups with allowExternalMembers = False
|
||||
* Add a column labelled `delete` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For exernal members to be deleted, add an x to the `delete` column
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield delete x datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
Delete all external members from groups with allowExternalMembers = False
|
||||
**Caution, be sure that this is what you want**
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ gam info cigroups <GroupEntity>
|
||||
[nosecurity|nosecuritysettings]
|
||||
[allfields|<CIGroupFieldName>*|(fields <CIGroupFieldNameList>)]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[formatjson]
|
||||
@@ -283,13 +283,17 @@ By default, all direct members, managers and owners in the group are displayed;
|
||||
By default, when displaying members from a group, all types of members (customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@@ -317,7 +321,7 @@ gam print cigroups [todrive <ToDriveAttribute>*]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[basic|allfields|(<CIGroupFieldName>* [fields <CIGroupFieldNameList>])]
|
||||
[roles <GroupRoleList>] [memberrestrictions]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[members|memberscount] [managers|managerscount] [owners|ownerscount] [totalcount] [countsonly]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
@@ -374,13 +378,20 @@ By default, no members, managers or owners in the group are displayed; these opt
|
||||
By default, when displaying members from a group, all types of members (customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group.
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
|
||||
@@ -319,6 +319,8 @@ Data fields identified in a `csvkmd` argument.
|
||||
(select <ProjectIDList> | <FileSelector> | <CSVFileSelector>)
|
||||
<PrinterIDEntity> ::=
|
||||
<PrinterIDList> | <FileSelector> | <CSVFileSelector>
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/workspace/drive/api/guides/search-files
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<RecipientEntity> ::=
|
||||
<EmailAddressEntity> | (select <UserTypeEntity>)
|
||||
<ResourceEntity> ::=
|
||||
@@ -329,22 +331,22 @@ Data fields identified in a `csvkmd` argument.
|
||||
<SerialNumberList> | <FileSelector> | <CSVFileSelector>
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>) | (teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>) | (shareddriveadminquery:<QuerySharedDrive>)
|
||||
<SharedDriveEntityAdmin> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>|
|
||||
<SharedDriveAdminQueryEntity>
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
(shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
<SiteACLScopeEntity> ::=
|
||||
|
||||
@@ -114,9 +114,9 @@ ous_and_children_na_ns
|
||||
(anydrivefilename <DriveFileName>)|(anydrivefilename:<DriveFileName>)
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
|
||||
@@ -49,7 +49,8 @@
|
||||
<DriveFolderID> ::= <String>
|
||||
<DriveFolderIDList> ::= "<DriveFolderID>(,<DriveFolderID>)*"
|
||||
<DriveFolderName> ::= <String>
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/drive/api/v3/search-files
|
||||
<QueryDriveFile> :: = <String> See: https://developers.google.com/workspace/drive/api/guides/search-files
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<DriveFileQueryEntity> ::=
|
||||
(query <QueryDriveFile>) | (query:<QueryDriveFile>)
|
||||
<DriveFileQueryShortcut> ::=
|
||||
@@ -90,15 +91,15 @@
|
||||
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <SharedDriveID>) | (teamdriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <SharedDriveID>) | (shareddriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>) | (teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>) | (shareddriveadminquery:<QuerySharedDrive>)
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(query <QueryDriveFile>) | (query:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
@@ -335,13 +336,13 @@ Select a Shared Drive file by giving its unique ID.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo 1234ABCD
|
||||
gam user testuser show fileinfo id 1234ABCD
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD
|
||||
```
|
||||
## Select Shared Drive file by name
|
||||
If you have the name, a search must be performed to find the ID that matches the name.
|
||||
@@ -350,16 +351,16 @@ You must specify the Shared Drive, either by ID or name, and the name of the fil
|
||||
Remember, searching for a file by name may return several file IDs if you have multiple files with the same name.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD teamdrivefilename "Test File"
|
||||
gam user testuser show fileinfo teamdrive "Shared Drive 1" teamdrivefilename "Test File"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD shareddrivefilename "Test File"
|
||||
gam user testuser show fileinfo shareddrive "Shared Drive 1" shareddrivefilename "Test File"
|
||||
```
|
||||
## Select Shared Drive file by query
|
||||
You can use a query to find a file ID. You perform the query on all Shared Drives or a specific Shared Drive.
|
||||
@@ -367,7 +368,7 @@ You can use a query to find a file ID. You perform the query on all Shared Drive
|
||||
See: [Drive Query](https://developers.google.com/drive/api/v3/search-files)
|
||||
```
|
||||
<SharedDriveFileQueryEntity> ::=
|
||||
(teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
(shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
```
|
||||
@@ -380,32 +381,32 @@ Keyword to query mappings for `<DriveFileQueryShortcut>`:
|
||||
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdrive teamdrive "Shared Drive 1" teamdrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD all_non_google_files
|
||||
gam user testuser show fileinfo shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddrive shareddrive "Shared Drive 1" shareddrivequery "name='Test File'"
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD all_non_google_files
|
||||
```
|
||||
## Select root folder of a Shared Drive by ID
|
||||
The root folder of a Shared Drive is a folder, you select it by giving its unique ID.
|
||||
```
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo 1234ABCD
|
||||
gam user testuser show fileinfo teamdriveid 1234ABCD
|
||||
gam user testuser show fileinfo shareddriveid 1234ABCD
|
||||
|
||||
```
|
||||
## Select root folder of a Shared Drive by name
|
||||
If you have a Shared Drive name, a search must be performed to find the ID that matches the name.
|
||||
```
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
```
|
||||
### Examples
|
||||
```
|
||||
gam user testuser show fileinfo teamdrive "Shared Drive 1"
|
||||
gam user testuser show fileinfo shareddrive "Shared Drive 1"
|
||||
|
||||
```
|
||||
|
||||
@@ -27,13 +27,13 @@
|
||||
(anydrivefilename <DriveFileName>) | (anydrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem> |
|
||||
(teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
(shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
<SharedDriveNameEntity>
|
||||
<SharedDriveFileNameEntity> ::=
|
||||
(teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
(shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
```
|
||||
|
||||
@@ -10,6 +10,161 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
||||
|
||||
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
||||
|
||||
### 7.34.00
|
||||
|
||||
Added variable `csv_output_header_required` to `gam.cfg` that is a comma separated list of `<Strings>`
|
||||
that are required to be in the list of column headers in the CSV file written by a gam print command.
|
||||
This will typically be used to specify headers that are required in subsequent commands that process
|
||||
the CSV file even if the API didn't return any data for those columns.
|
||||
|
||||
Updated the following commands to not require the `Directory API - Domains` scope
|
||||
unless the `internal` or `external` options are used to request the member category.
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
|
||||
### 7.33.03
|
||||
|
||||
Fixed bug in `gam [<UserTypeEntity>] sendemail ... from <EmailAddress> replyto <EmailAddress>`
|
||||
where an `<EmailAddress>` of the form `Text <user@domain.com>` had the `Text` removed.
|
||||
|
||||
### 7.33.02
|
||||
|
||||
Added `hideinvitationssetting` to `<UserCalendarSettingsField>` used by
|
||||
`gam <UserTypeEntity> print|show calsettings`.
|
||||
|
||||
### 7.33.01
|
||||
|
||||
Added option `shownopolicy` to `gam print chromepolicies` that will display output like the following
|
||||
if no policies apply to the selected OU or group.
|
||||
```
|
||||
gam print chromepolicies ou /Test appid chrome:emidddocikgklceeeifefomdnbkldhng namespace chrome.users.apps shownopolicy
|
||||
Getting all Chrome Policies that match query (chrome.users.apps.*) for /Test
|
||||
Got 0 Chrome Policies that matched query (chrome.users.apps.*) for /Test...
|
||||
name,orgUnitPath,parentOrgUnitPath,direct,appId
|
||||
noPolicy,/Test,/,False,chrome:emidddocikgklceeeifefomdnbkldhng
|
||||
```
|
||||
|
||||
### 7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
### 7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
### 7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
### 7.32.05
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print messages|threads ... headers <SMTPHeaderList>` where
|
||||
headers other than those specified in `<SMTPHeaderList>` were displayed.
|
||||
|
||||
Updated `gam info users <UserTypeEntity>` to display the following data when the Licensing API
|
||||
does not return data due to quota limits. Previously, no License data was displayed and
|
||||
there was no way to know if it was omitted due to API quota limits vs the user has no license?
|
||||
```
|
||||
Licenses: (1)
|
||||
Not available/incomplete
|
||||
```
|
||||
If a user has no licenses, this will be displayed.
|
||||
```
|
||||
Licenses: (0)
|
||||
```
|
||||
|
||||
You should use `license_skus = <SKUIDList>` in `gam.cfg` to list all of the licensing SKUs
|
||||
used in your workspace. Without this list, GAM has to make 70+ API calls to get the licenses
|
||||
for a user; this can cause quota limit errors.
|
||||
|
||||
### 7.32.04
|
||||
|
||||
Support for student groups in Google Classroom no longer requires Developer Preview membership.
|
||||
|
||||
Upgraded to OpenSSL 3.6.1.
|
||||
|
||||
### 7.32.03
|
||||
|
||||
Added option `template` as an additional formating option for `gam <UserTypeEntity> show signature`
|
||||
that displays just the HTML data; this simplifies capturing the data for use as input to GAM.
|
||||
```
|
||||
$ gam redirect stdout ./SigTemplate.html user user@domain.com show signature template
|
||||
$ more SigTemplate.html
|
||||
<div dir="ltr"><div dir="ltr"><div dir="ltr">
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Email: {Email}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Phone: {Phone}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Mobile: {Mobile}{/RT}</span></p>
|
||||
</div><br></div>\n</div>
|
||||
```
|
||||
|
||||
### 7.32.02
|
||||
|
||||
Added variable `oauth2_txt_lock_mode` to `gam.cfg`, the default is 644 and valid values are: 644, 664, 666.
|
||||
This value is used to set the file permissions on the `oauth2.txt.lock` file. In very special cases where
|
||||
daemon processes, e.g. Apache/httpd, are running GAM, the value 666 may have to be used.
|
||||
|
||||
### 7.32.01
|
||||
|
||||
Added option `(addcsvdata <FieldName> <String>)*` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> csv`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID> csv` that adds additional columns of data to the CSV file output.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/ChromeOS-Devices#bulk-action-example
|
||||
|
||||
### 7.32.00
|
||||
|
||||
Added option `verifyallowexternal` to `gam print cigroup-members|group-members` that causes
|
||||
GAM to only display external members in groups with `allowExternalMembers=False'.
|
||||
This option can be used to help verify that internal-only groups don't have external members.
|
||||
|
||||
Updated option `internaldomains` for the following commands:
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Added option `csv` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand>`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID>` so that command details are displayed in CSV format.
|
||||
This can be used to log commands issued to devices and then monitor the results.
|
||||
|
||||
Added option `filemimetype category <MimeTypeNameList>` to `gam <UserTypeEntity> copy drivefile` to support
|
||||
copying of files based on their MimeType category.
|
||||
|
||||
Added option `attendeeslist` to `gam calendars <CalendarEntity> print events` and `gam <UserTypeEntity> print events`
|
||||
that causes GAM to display the attendee email addresses in a single column `attendeesList`; no attendee details
|
||||
are displayed. The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Fixed bug in `gam sendemail ... replyto <EmailAddress>` that caused a message delivery error if
|
||||
`<EmailAddress>` did not include a domain name.
|
||||
|
||||
Added support for users's chat sections.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-users-sections
|
||||
* This is in Deveoper Preview; you must have a `developer_preview_api_key` in `gam.cfg` to use these commands.
|
||||
|
||||
### 7.31.06
|
||||
|
||||
Added option `batchsize <Integer>` to `gam calendar <CalendarEntity> delete|purge events` and
|
||||
@@ -2925,7 +3080,7 @@ Added option `showmimetype category <MimeTypeNameList>` to `gam <UserTypeEntity>
|
||||
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
|
||||
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
|
||||
|
||||
gam user user@domain.com print filelist fields id,name,mimetype showmimetype prefixes audio,video
|
||||
gam user user@domain.com print filelist fields id,name,mimetype showmimetype category audio,video
|
||||
```
|
||||
|
||||
### 6.71.11
|
||||
|
||||
@@ -615,7 +615,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[membernames] [showdeliverysettings]
|
||||
<MembersFieldName>* [fields <MembersFieldNameList>]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
@@ -682,13 +682,21 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group and adds the column `category` that shows whether the member
|
||||
is `external` or `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@@ -756,7 +764,7 @@ gam show group-members
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners] [depth <Number>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
@@ -809,232 +817,17 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
By default, members of type GROUP are recursively expanded to show their constituent members. (Members of
|
||||
type CUSTOMER are not expanded.) The `depth <Number>` argument controls the depth to which nested groups are displayed.
|
||||
* `depth -1` - all groups in the selected group and below are displayed; this is the default.
|
||||
* `depth 0` - the groups within a selected group are displayed, no descendants are displayed.
|
||||
* `depth N` - the groups within the selected group and those groups N levels below the selected group are displayed.
|
||||
|
||||
The `includederivedmembership` option causes the API to expand type GROUP and type CUSTOMER
|
||||
members to display their constituent members while still displaying the original member.
|
||||
|
||||
The options `types user` and `includederivedmembership types user` return the same list of users.
|
||||
The `includederivedmembership` option makes less API calls but doesn't show hierarchy.
|
||||
Expanding a member of type CUSTOMER may produce a large volume of data as it will display all users in your domain.
|
||||
|
||||
### Display group structure
|
||||
To see a group's structure of nested groups use the `type group` option.
|
||||
```
|
||||
$ gam show group-members group testgroup5 types group
|
||||
Group: testgroup5@domain.com
|
||||
MEMBER, GROUP, testgroup1@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup2@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup3@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup2@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup4@domain.com, ACTIVE
|
||||
```
|
||||
To show the structure of all groups you can do the following; it will be time consuming for a large number of groups.
|
||||
```
|
||||
gam redirect stdout ./groups.txt show group-members types group
|
||||
```
|
||||
|
||||
### Examples
|
||||
#### Print a CSV of all members of a group regardless of role, all fields
|
||||
```
|
||||
gam print group-members <GroupEntity>
|
||||
```
|
||||
#### Print a CSV containing all managers emails
|
||||
```
|
||||
gam print group-members <GroupEntity> role manager fields email
|
||||
```
|
||||
#### Print a CSV output of all members and their emails only
|
||||
```
|
||||
gam print group-members <GroupEntity> role member fields email
|
||||
```
|
||||
#### Display group owners in your domain, but excluding groups where the email starts with a 4 digit code
|
||||
```
|
||||
gam print group-members domain <Your Domain> emailmatchpattern not '^1234.*' roles owners
|
||||
```
|
||||
|
||||
|
||||
These options further limit the list of groups selected above:
|
||||
* `emailmatchpattern <REMatchPattern>` - Limit display to groups whose email address matches `<REMatchPattern>`
|
||||
* `emailmatchpattern not <REMatchPattern>` - Limit display to groups whose email address does not match `<REMatchPattern>`
|
||||
* `namematchpattern <REMatchPattern>` - Limit display to groups whose name matches `<REMatchPattern>`
|
||||
* `namematchpattern not <REMatchPattern>` - Limit display to groups whose name does not match `<REMatchPattern>`
|
||||
* `descriptionmatchpattern <REMatchPattern>` - Limit display to groups whose description matches `<REMatchPattern>`
|
||||
* `descriptionmatchpattern not <REMatchPattern>` - Limit display to groups whose description does not match `<REMatchPattern>`
|
||||
* `admincreatedmatch True` - Limit display to groups created by administrators
|
||||
* `admincreatedmatch False` - Limit display to groups created by users
|
||||
|
||||
By default, all members, managers and owners in the group are displayed; these options modify that behavior:
|
||||
* `roles <GroupRoleList>` - Display specified roles
|
||||
* `members` - Display members
|
||||
* `managers` - Display managers
|
||||
* `owners` - Display owners
|
||||
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, members that are groups are displayed as a single entry of type GROUP; this option recursively expands group members to display their user members.
|
||||
* `recursive` - Recursively expand group members
|
||||
|
||||
When `recursive` is specified, the default is to only display type user members; this option modifies those behaviors:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when displaying members from a group, all members, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Display only non-suspended members
|
||||
* `suspended` - Display only suspended members
|
||||
* `notarchived` - Do not include archived members
|
||||
* `archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `notsuspended notarchived` - Do not include suspended and archived members
|
||||
* `suspended archived` - Include only suspended or archived members
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
By default, the ID, role, email address, type and status of each member are displayed along with the group email address;
|
||||
these options specify which fields to display:
|
||||
* `membernames` - Display members full name; an additional API call per member is required
|
||||
* `showdeliverysettings` - Display delivery settings; an additional API call per member is required
|
||||
* `<MembersFieldName>*` - Individual field names
|
||||
* `fields <MembersFieldNameList>` - A comma separated list of field names
|
||||
* `delivery|deliverysettings` - Specify this field to get delivery information; an additional API call per member is required
|
||||
|
||||
For members that are users, you can specify additional information to display; an additional API call per member is required
|
||||
* `userfields <UserFieldNameList>` - Display specific user fields
|
||||
* `allschemas|(schemas|custom|customschemas <SchemaNameList>)` - Display all or specific custom schema values
|
||||
|
||||
The additional API calls can be reduced with the `cachememberinfo` option; a single API call is made for each user/group
|
||||
and the data is cached to eliminate to need to repeat the API call; this consumes more memory but dramatically reduces the number of API calls.
|
||||
|
||||
If member names are requested, names are not available for users not in the domain; you can request that GAM use the People API to retrieve
|
||||
names for these users. Names are not retrieved in all cases and success is dependent on what user is used to perform the retrievals.
|
||||
* `peoplelookup` - Use the administrator named in oauth2.txt to perform the retrievals
|
||||
* `peoplelookupuser <EmailAddress>` - Use `<EmailAddress>` to perform the retrievals
|
||||
|
||||
By default, when `membernames` is specified, GAM displays `Unknown` for members whose names can not be determined.
|
||||
Use `unknownname <String>` to specify an alternative value.
|
||||
|
||||
By default, the group email address is always shown, you can suppress it with the `nogroupemail` option.
|
||||
|
||||
The `recursive` option adds two columns, level and subgroup, to the output:
|
||||
* `level` - At what level of the expansion does the user appear; level 0 is the top level
|
||||
* `subgroup` - The group that contained the user
|
||||
|
||||
Displaying membership of multiple groups or recursive expansion may result in multiple instances of the same user being displayed; these multiple instances can be reduced to one entry.
|
||||
* `noduplicates` - Reduce multiple instances of the same user to the first instance
|
||||
|
||||
The `includederivedmembership` option is an alternative to `recursive`; it causes the API to expand type GROUP and type CUSTOMER
|
||||
members to display their constituent members while still displaying the original member.
|
||||
The API produces inconsistent results, use with caution.
|
||||
|
||||
The options `recursive noduplicates` and `includederivedmembership types user noduplicates` return the same list of users.
|
||||
The `includederivedmembership` option makes less API calls but doesn't show level and subgroup information.
|
||||
Expanding a member of type CUSTOMER may produce a large volume of data as it will display all users in your domain.
|
||||
|
||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
## Display group membership in hierarchical format
|
||||
```
|
||||
gam show group-members
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners] [depth <Number>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[includederivedmembership]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
|
||||
* You can predefine this list with the `print_agu_domains` variable in `gam.cfg`.
|
||||
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>`
|
||||
* `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
|
||||
* `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
|
||||
* `group <GroupItem>` - Limit display to the single group `<GroupItem>`
|
||||
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members
|
||||
* `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members
|
||||
* `select <GroupEntity>` - Limit display to the groups specified in `<GroupEntity>`
|
||||
* `showownedby <UserItem>` - Limit display to groups owned by `<UserItem>`
|
||||
|
||||
When using `query <QueryGroup>` with the `name:{PREFIX}*` query, `PREFIX` must contain at least three characters.
|
||||
|
||||
You can identify groups with the `All users in the organization` member with:
|
||||
* `query "memberKey=<CustomerID>"`
|
||||
* `member id:<CustomerID>`
|
||||
|
||||
These options further limit the list of groups selected above:
|
||||
* `emailmatchpattern <REMatchPattern>` - Limit display to groups whose email address matches `<REMatchPattern>`
|
||||
* `emailmatchpattern not <REMatchPattern>` - Limit display to groups whose email address does not match `<REMatchPattern>`
|
||||
* `namematchpattern <REMatchPattern>` - Limit display to groups whose name matches `<REMatchPattern>`
|
||||
* `namematchpattern not <REMatchPattern>` - Limit display to groups whose name does not match `<REMatchPattern>`
|
||||
* `descriptionmatchpattern <REMatchPattern>` - Limit display to groups whose description matches `<REMatchPattern>`
|
||||
* `descriptionmatchpattern not <REMatchPattern>` - Limit display to groups whose description does not match `<REMatchPattern>`
|
||||
* `admincreatedmatch True` - Limit display to groups created by administrators
|
||||
* `admincreatedmatch False` - Limit display to groups created by users
|
||||
|
||||
By default, all members, managers and owners in the group are displayed; these options modify that behavior:
|
||||
* `roles <GroupRoleList>` - Display specified roles
|
||||
* `members` - Display members
|
||||
* `managers` - Display managers
|
||||
* `owners` - Display owners
|
||||
|
||||
By default, when displaying members from a group, all members, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Display only non-suspended members
|
||||
* `suspended` - Display only suspended members
|
||||
* `notarchived` - Do not include archived members
|
||||
* `archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `notsuspended notarchived` - Do not include suspended and archived members
|
||||
* `suspended archived` - Include only suspended or archived members
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
|
||||
@@ -396,7 +396,7 @@ gam info group|groups <GroupEntity>
|
||||
[basic] <GroupFieldName>* [fields <GroupFieldNameList>] [nodeprecated]
|
||||
[ciallfields|(cifields <CIGroupFieldNameList>)]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains All|<DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
@@ -423,13 +423,17 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, when displaying members from a group, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your primary workspace domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@@ -467,7 +471,7 @@ gam print groups [todrive <ToDriveAttribute>*]
|
||||
[ciallfields|(cifields <CIGroupFieldNameList>)]
|
||||
[nodeprecated]
|
||||
[roles <GroupRoleList>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[members|memberscount] [managers|managerscount] [owners|ownerscount] [totalcount] [countsonly]
|
||||
[includederivedmembership]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
@@ -558,13 +562,20 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, when displaying members from a group, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
|
||||
@@ -252,7 +252,7 @@ writes the credentials into the file oauth2.txt.
|
||||
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||
admin@server:/Users/admin$ gam version
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
@@ -550,6 +550,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; /Users/admin/GAMConfig/client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@@ -559,6 +560,7 @@ Section: DEFAULT
|
||||
config_dir = /Users/admin/GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -571,32 +573,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = /Users/admin/GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -606,13 +624,15 @@ Section: DEFAULT
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -629,12 +649,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -647,6 +668,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
@@ -751,6 +774,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@@ -760,6 +784,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -772,32 +797,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = my_customer
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = ''
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -807,13 +848,15 @@ Section: DEFAULT
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -830,12 +873,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = utc
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -848,6 +892,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
@@ -990,7 +1036,7 @@ writes the credentials into the file oauth2.txt.
|
||||
C:\>del C:\GAMConfig\oauth2.txt
|
||||
C:\>gam version
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
Windows 11 10.0.26200 AMD64
|
||||
@@ -1289,6 +1335,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@@ -1298,6 +1345,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@@ -1310,32 +1358,48 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@@ -1353,7 +1417,7 @@ Section: DEFAULT
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@@ -1370,12 +1434,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@@ -1388,6 +1453,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
@@ -56,6 +56,7 @@ The only `<VariableNames>` recognized in this `<Section>` are:
|
||||
* `csv_output_header_drop_filter`
|
||||
* `csv_output_header_force`
|
||||
* `csv_output_header_order`
|
||||
* `csv_output_header_required`
|
||||
* `csv_output_row_filter`
|
||||
* `csv_output_row_filter_mode`
|
||||
* `csv_output_row_drop_filter`
|
||||
|
||||
34
wiki/No-Owner-Secondary-Calendars.md
Normal file
34
wiki/No-Owner-Secondary-Calendars.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Secondary Calendars with no Owner
|
||||
|
||||
Here's a start on how to get information for non-owned secondary calendars
|
||||
|
||||
Save the CSV file that Google sent you as NoOwnerSecCals.csv
|
||||
|
||||
Get calendar description and summary for non-owned secondary calendars.
|
||||
* There will be one row per calendar.
|
||||
```
|
||||
gam config num_threads 10 csv_output_header_force "calendarId,description,summary" redirect csv ./NOSC_Details.csv multiprocess redirect stderr - multiprocess csv NoOwnerSecCals.csv gam calendar "~Secondary calendar email" print settings fields description,summary
|
||||
```
|
||||
|
||||
Get event counts for non-owned secondary calendars.
|
||||
* There will be one row per calendar.
|
||||
```
|
||||
gam config num_threads 10 redirect csv ./NOSC_EventCounts.csv multiprocess redirect stderr - multiprocess csv NOSC_Details.csv gam calendar "~calendarId" print events countsonly addcsvdata description "~description" addcsvdata summary "~summary"
|
||||
```
|
||||
|
||||
Get summary for non-owned secondary calendars - Contains details, counts, ACLs
|
||||
* There will be one row per calendar/ACL combination
|
||||
```
|
||||
gam config num_threads 10 redirect csv ./NOSC_Summary.csv multiprocess redirect stderr - multiprocess csv NOSC_EventCounts.csv gam calendar "~calendarId" print acls noselfowner addcsvdata description "~description" addcsvdata summary "~summary" addcsvdata events "~events"
|
||||
```
|
||||
|
||||
You can add an owner.
|
||||
* Replace `admin@domain.com` with a super admin other than the one from: `gam oauth info`
|
||||
```
|
||||
gam config num_threads 10 redirect stdout ./Add_NOSC_Owner.txt multiprocess redirect stderr stdout csv NoOwnerSecCals.csv gam calendar "~Secondary calendar email" add acls owner admin@domain.com sendnotifications false
|
||||
```
|
||||
After inspecting NOSC_Summary.csv, you can delete the calendars if desired.
|
||||
* Replace `admin@domain.com` with the super admin specified in the previous step
|
||||
```
|
||||
gam config num_threads 10 redirect stdout ./Delete_NOSC.txt multiprocess redirect stderr stdout csv NoOwnerSecCals.csv gam user admin@domain.com remove calendar "~Secondary calendar email"
|
||||
``
|
||||
@@ -125,7 +125,7 @@ Limit the time period.
|
||||
* `thismonth` - The current calendar month up to the current time
|
||||
* `previousmonths <Integer>` - A number in the range 1 to 6 indicating calendar months previous to the current month
|
||||
|
||||
For `gam report gmail`, `start <Time>` and `end <Time>`should both be provided, and the scan duration should not be greater than 30 days.
|
||||
For `gam report gmail`, `start <Time>` and `end <Time>` should both be provided, and the scan duration should not be greater than 30 days.
|
||||
GAM will supply missing values:
|
||||
* No time information provided - GAM sets `range -30d today`
|
||||
* Only `start <Time>` provided - GAM sets `end <Time>+30d`
|
||||
@@ -134,6 +134,11 @@ GAM will supply missing values:
|
||||
For `gam report gmail`, `gmaileventtypes <NumberRangeList>` can be used to limit the event types displayed.
|
||||
* See: https://developers.google.com/workspace/admin/reports/v1/appendix/activity/gmail
|
||||
|
||||
You can use the following filter to select a specific event; replace `X` with your desired value.
|
||||
```
|
||||
filter "event_info.mail_event_type==X"
|
||||
```
|
||||
|
||||
Apply API filters.
|
||||
* `filter|filters <String>` - `<String>` is a comma separated list of filter expressions.
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
withlink
|
||||
<DrivePermissionsFieldNameList> ::= "<DrivePermissionsFieldName>(,<DrivePermissionsFieldName>)*"
|
||||
|
||||
<QueryTeamDrive> ::= <String> See: https://developers.google.com/drive/api/v3/search-parameters
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
<SharedDriveACLRole> ::=
|
||||
manager|organizer|owner|
|
||||
contentmanager|fileorganizer|
|
||||
@@ -183,8 +183,8 @@
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|
|
||||
(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
|
||||
<SharedDriveFieldName> ::=
|
||||
backgroundimagefile|
|
||||
@@ -199,11 +199,11 @@
|
||||
<SharedDriveFieldNameList> ::= "<SharedDriveFieldName>(,<SharedDriveFieldName>)*"
|
||||
|
||||
<SharedDriveIDEntity> ::=
|
||||
<DriveFileItem>|(teamdriveid <DriveFileItem>)|(teamdriveid:<DriveFileItem>)
|
||||
<DriveFileItem>|(shareddriveid <DriveFileItem>)|(shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::=
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
<SharedDriveAdminQueryEntity> ::=
|
||||
(teamdriveadminquery <QueryTeamDrive>)|(teamdriveadminquery:<QueryTeamDrive>)
|
||||
(shareddriveadminquery <QuerySharedDrive>)|(shareddriveadminquery:<QuerySharedDrive>)
|
||||
|
||||
<SharedDriveEntityAdmin> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
@@ -327,11 +327,11 @@ When either of these options is chosen, no infomation about Shared Drive restric
|
||||
To retrieve the Shared Drive ID with `returnidonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
teamDriveId=$(gam create shareddrive ... returnidonly)
|
||||
shareddriveId=$(gam create shareddrive ... returnidonly)
|
||||
Windows PowerShell
|
||||
$teamDriveId = & gam create shareddrive ... returnidonly
|
||||
$shareddriveId = & gam create shareddrive ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam create shareddrive ... returnidonly') do set teamDriveId=%a
|
||||
for /f "delims=" %a in ('gam create shareddrive ... returnidonly') do set shareddriveId=%a
|
||||
```
|
||||
|
||||
## Bulk Create Shared Drives
|
||||
@@ -422,14 +422,14 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
```
|
||||
gam [<UserTypeEntity>] show shareddrives
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[fields <SharedDriveFieldNameList>]
|
||||
[showwebviewlink text|hyperlink]
|
||||
[formatjson]
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -441,14 +441,14 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
```
|
||||
gam [<UserTypeEntity>] print shareddrives [todrive <ToDriveAttribute>*]
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[fields <SharedDriveFieldNameList>]
|
||||
[showwebviewlink text|hyperlink]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -498,7 +498,7 @@ Options `shareddriveadminquery|query` and `shareddrives|teamdrives` are mutually
|
||||
Options `shareddriveadminquery|query` and `orgunit|org|ou` require `adminaccess|asadmin`.
|
||||
|
||||
By default, organizers for all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `shareddrives|teamdrives <SharedDriveIDList>` - Select the Shared Drive IDs specified in `<SharedDriveIDList>`
|
||||
* `shareddrives|teamdrives select <FileSelector>|<CSVFileSelector>` - Select the Shared Drive IDs specified in `<FileSelector>|<CSVFileSelector>`
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
@@ -535,12 +535,12 @@ gam print shareddrives query "organizerCount = 0"
|
||||
Display the number of Shared Drives.
|
||||
```
|
||||
gam [<UserTypeEntity>] show|print shareddrives
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
showitemcountonly
|
||||
```
|
||||
By default, all Shared Drives are counted; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -758,7 +758,7 @@ gam config csv_output_header_drop_filter "User,createdTime,permission.photoLink,
|
||||
## Display Shared Drive access for selected Shared Drives
|
||||
```
|
||||
gam [<UserTypeEntity>] show shareddriveacls
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -767,7 +767,7 @@ gam [<UserTypeEntity>] show shareddriveacls
|
||||
[formatjson]
|
||||
|
||||
gam [<UserTypeEntity>] print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
[adminaccess|asadmin] [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[adminaccess|asadmin] [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -777,7 +777,7 @@ gam [<UserTypeEntity>] print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
|
||||
```
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives; all ACLs are displayed for the selected Shared Drives
|
||||
@@ -927,12 +927,12 @@ gam redirect stdout ./DeleteSharedDrives.txt multiprocess redirect stderr stdout
|
||||
## Delete old empty Shared Drives
|
||||
```
|
||||
# Get a list of Shared Drives organizers for Shared Drives created before one year ago; alter date<-1y as required.
|
||||
gam config csv_output_row_filter "createdTime:date<-1y" redirect csv ./TeamDriveOrganizers.csv print shareddriveorganizers domainlist mydomain.com includetypes user oneorganizer shownoorganizerdrives
|
||||
gam config csv_output_row_filter "createdTime:date<-1y" redirect csv ./ShareddriveOrganizers.csv print shareddriveorganizers domainlist mydomain.com includetypes user oneorganizer shownoorganizerdrives
|
||||
|
||||
# Inspect shareddriveOrganizers.csv, you'll have to deal with Shared Drives with no organizer/manager
|
||||
|
||||
# Get old empty Shared Drives
|
||||
gam config num_threads 10 csv_input_row_filter "organizers:regex:^.+$" csv_output_row_filter "Total:count=0" redirect csv ./OldEmptySharedDrives.csv multiprocess redirect stderr - multiprocess csv ./TeamDriveOrganizers.csv gam user "~organizers" print filecounts select shareddriveid "~id" showsize
|
||||
gam config num_threads 10 csv_input_row_filter "organizers:regex:^.+$" csv_output_row_filter "Total:count=0" redirect csv ./OldEmptySharedDrives.csv multiprocess redirect stderr - multiprocess csv ./ShareddriveOrganizers.csv gam user "~organizers" print filecounts select shareddriveid "~id" showsize
|
||||
|
||||
# Inspect OldEmptySharedDrives.csv, if you're confident of the results, proceed
|
||||
|
||||
|
||||
@@ -681,9 +681,10 @@ By default, Gam displays event details, use `countsonly` to display only the num
|
||||
|
||||
```
|
||||
gam <UserTypeEntity> print events <UserCalendarEntity> [<EventEntity>] <EventDisplayProperty>*
|
||||
[fields <EventFieldNameList>] [showdayofweek]
|
||||
[fields <EventFieldNameList>] [showdayofweek] [attendeeslist]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[eventrowfilter] [countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
||||
[eventrowfilter]
|
||||
[countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
||||
```
|
||||
In `<EventEntity>`, any `<EventSelectProperty>` options must precede all other options.
|
||||
|
||||
@@ -694,6 +695,10 @@ option `singleevents` to display all instances of a recurring event.
|
||||
|
||||
`showdayofweek` displays columns `start.dayOfWeek` and `end.dayOfWeek` when event start and end times are displayed.
|
||||
|
||||
By default, each attendee is displayed in a separate column; `attendeeslist` causes GAM to display
|
||||
the attendee email addresses in a single column `attendeesList`; no attendee details are displayed.
|
||||
The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Add additional columns of data from the command line to the output after the calendarId.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Users - Calendars
|
||||
Users - Calendars
|
||||
- [API documentation](#api-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Calendar colors](#calendar-colors)
|
||||
@@ -74,8 +74,8 @@
|
||||
[coursestates <CourseStateList>])|
|
||||
(resource <ResourceID>)|
|
||||
(resources <ResourceIDList>)|
|
||||
((calendars <CalendarList>) | <FileSelector> | <CSVFileSelector> |
|
||||
<CSVkmdSelector> | <CSVDataSelector>)
|
||||
(calendars <CalendarList> | <FileSelector> | <CSVFileSelector> |
|
||||
<CSVkmdSelector> | <CSVDataSelector>)
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
|
||||
<UserCalendarEntity> ::=
|
||||
@@ -90,8 +90,8 @@
|
||||
[coursestates <CourseStateList>])|
|
||||
(resource <ResourceID>)|
|
||||
(resources <ResourceIDList>)|
|
||||
((calendars <CalendarList>) | <FileSelector> | <CSVFileSelector> |
|
||||
<CSVkmdSelector> | <CSVDataSelector>)|
|
||||
(calendars <CalendarList> | <FileSelector> | <CSVFileSelector> |
|
||||
<CSVkmdSelector> | <CSVDataSelector>)|
|
||||
<CalendarSelectProperty>*
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
defaulteventlength|
|
||||
format24hourtime|
|
||||
hideinvitations|
|
||||
hideinvitationssetting|
|
||||
hideweekends|
|
||||
locale|
|
||||
remindonrespondedeventsonly|
|
||||
|
||||
@@ -37,7 +37,9 @@ gam user user@domain.com update serviceaccount
|
||||
[*] 11) Chat API - User Sections (supports readonly)
|
||||
|
||||
```
|
||||
`Chat API - User Sections` is in Developer Preview.
|
||||
`Chat API - User Sections` is in Developer Preview; you must have a the following variables set in `gam.cfg` to use these commands.
|
||||
* `developer_preview_apis = chat`
|
||||
* `developer_preview_api_key = <String>`
|
||||
|
||||
Added `use_chat_admin_access` Boolean variable to `gam.cfg`.
|
||||
```
|
||||
@@ -484,14 +486,22 @@ gam <UserTypeEntity> move chatsectionitem <ChatSectionItem> to <ChatSection>
|
||||
### Display information about a user's chat section items
|
||||
```
|
||||
gam <UserTypeEntity> show chatsectionitems <ChatSection>
|
||||
[space <ChatSpace>]
|
||||
[formatjson]
|
||||
```
|
||||
You can search for a chat section that contains a chat space with:
|
||||
`show chatsectionitems sections/- space <ChatSpace>`
|
||||
|
||||
By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
```
|
||||
gam <UserTypeEntity> print chatsectionitems <ChatSection> [todrive <ToDriveAttribute>*]
|
||||
[space <ChatSpace>]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
You can search for a chat section that contains a chat space with:
|
||||
`print chatsectionitems sections/- space <ChatSpace>`
|
||||
|
||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
<SharedDriveID>|(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
```
|
||||
## Display empty folders
|
||||
```
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
<RESubstitution> ::= <String>>
|
||||
|
||||
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
|
||||
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
|
||||
<MimeType> ::= <MimeTypeName>/<String>
|
||||
<MimeTypeList> ::= "<MimeType>(,<MimeType>)*"
|
||||
|
||||
@@ -67,10 +68,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveFileCopyAttribute> ::=
|
||||
(contentrestrictions readonly false)|
|
||||
@@ -103,13 +104,14 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
<DriveFileCopyAttribute>*
|
||||
[skipids <DriveFileEntity>]
|
||||
[copysubfiles [<Boolean>]] [filenamematchpattern <REMatchPattern>]
|
||||
[filemimetype [not] <MimeTypeList>]
|
||||
[filemimetype [not] <MimeTypeList>] [filemimetype category <MimeTypeNameList>]
|
||||
[copysubfilesownedby
|
||||
any|me|others|
|
||||
users <EmailAddressList>|
|
||||
notusers <EmailAddressList>|
|
||||
regex <REMatchPattern>|
|
||||
notregex <REMatchPattern>]
|
||||
[([start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>])|(range <Date>|<Time> <Date>|<Time>)]|
|
||||
[copysubfolders [<Boolean>]] [foldernamematchpattern <REMatchPattern>]
|
||||
[copysubshortcuts [<Boolean>]] [shortcutnamematchpattern <REMatchPattern>]
|
||||
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||
@@ -181,14 +183,17 @@ You can specify whether sub files, folders and shortcuts are copied. If sub fold
|
||||
* `copysubshortcuts false` - Sub shortcuts are not copied
|
||||
* `copysubshortcuts [true]` - Sub shortcuts are copied; this is the default
|
||||
|
||||
By default, GAM displays a message referencing files and folders not selected for copying by the options above.
|
||||
* `suppressnotselectedmessages false` - Do not suppress these messages; this is the default
|
||||
* `suppressnotselectedmessages [true]` - Suppress these messages
|
||||
|
||||
### By default, when copying sub files, all files, regardless of MIME type, are copied.
|
||||
You can specify restrictions on the MIME types to be copied.
|
||||
* `filemimetypes <MimeTypeList>` - Copy sub files with the specified MIME types
|
||||
* `filemimetypes not <MimeTypeList>` - Copy sub files with MIME types other than those specified
|
||||
* `filemimetypes category <MimeTypeNameList>` - Copy sub files with the specified MIME type categories
|
||||
|
||||
### By default, when copying sub files, all files, regardless of their `modifiedTime`, are copied.
|
||||
You can specify restrictions on the `modifiedTime` to be copied.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
### By default, when copying sub files, folders and shortcuts, all are copied.
|
||||
You can specify `<REMatchPattern>` patterns that limit the items copied based on their name.
|
||||
@@ -205,6 +210,10 @@ You can specify `<REMatchPattern>` patterns that limit the items copied based on
|
||||
* `copysubfilesownedby regex <REMatchPattern>` - Only files owned by users whose email addresses match `<REMatchPattern>` are copied.
|
||||
* `copysubfilesownedby notregex <REMatchPattern>` - Only files owned by users whose email addresses do not match `<REMatchPattern>` are copied.
|
||||
|
||||
### By default, GAM displays a message referencing files and folders not selected for copying by the options above.
|
||||
* `suppressnotselectedmessages false` - Do not suppress these messages; this is the default
|
||||
* `suppressnotselectedmessages [true]` - Suppress these messages
|
||||
|
||||
### Specify a new name for the file/folder
|
||||
* `newfilename <DriveFileName>` - The copied file/folder will be named `<DriveFileName>`
|
||||
* If `stripnameprefix <String>` is specified, `<String>` will be stripped from the front of `<DriveFileName>`
|
||||
@@ -222,10 +231,10 @@ and "Template" is replaced by "NewCustomer" in all copied sub files and folders
|
||||
* `parentid <DriveFolderID>` - The target folder is identified by `<DriveFolderID>` which must be writable by `<UserTypeEntity>`.
|
||||
* `parentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by any user but must be writable by `<UserTypeEntity>`.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the copied file/folder will be located in the source folder.
|
||||
|
||||
### Duplicate files
|
||||
@@ -405,15 +414,15 @@ Specify the target location on the Shared Drive, either the ID of the Shared Dri
|
||||
|
||||
Files/folders in root of My Drive will be merged into `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <DriveFolderID> mergewithparent true
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <DriveFolderID> mergewithparent true
|
||||
```
|
||||
Files/folders in root of My Drive will be in a new folder named `My Drive` created in `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <DriveFolderID> mergewithparent false
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <DriveFolderID> mergewithparent false
|
||||
```
|
||||
Files/folders in root of My Drive will be in a new folder named `<String>` created in `<DriveFolderID>`
|
||||
```
|
||||
gam user user@domain.com copy drivefile root recursive teamdriveparentid <SharedDriveID> mergewithparent false newfilename <String>
|
||||
gam user user@domain.com copy drivefile root recursive shareddriveparentid <SharedDriveID> mergewithparent false newfilename <String>
|
||||
```
|
||||
|
||||
### Copy content of a Shared Drive to another Shared Drive
|
||||
@@ -429,7 +438,7 @@ The example is assuming that the target drive is empty.
|
||||
* Non-inherited sub folder permissions are copied.
|
||||
* Non-inherited file permissions are copied.
|
||||
```
|
||||
gam user user@domain.com copy drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent recursive
|
||||
gam user user@domain.com copy drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent recursive
|
||||
copymergewithparentfolderpermissions true
|
||||
copytopfolderinheritedpermissions false
|
||||
copytopfoldernoninheritedpermissions always
|
||||
@@ -449,7 +458,7 @@ Suppose that the source drive has been updated and you want to refresh the targe
|
||||
* Non-inherited file permissions are copied.
|
||||
* Files and folders that have been deleted from the source drive will remain on the target drive
|
||||
```
|
||||
gam user user@domain.com copy drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent recursive
|
||||
gam user user@domain.com copy drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent recursive
|
||||
copymergewithparentfolderpermissions true
|
||||
copytopfolderinheritedpermissions false
|
||||
copytopfoldernoninheritedpermissions syncallfolders
|
||||
@@ -469,7 +478,7 @@ gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 0AC
|
||||
```
|
||||
Copy the top level items to target Shared Drive; append desired permission options
|
||||
```
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid 0AE_9ZX
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid 0AE_9ZX
|
||||
```
|
||||
|
||||
### Copy content of a source Shared Drive folder to a target Shared Drive with parallel Processing
|
||||
@@ -479,31 +488,31 @@ gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx
|
||||
```
|
||||
Create a folder on target Shared Drive with ID 0AE_9ZX, replace "New Folder Name" as desired.
|
||||
```
|
||||
gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
```
|
||||
Copy the folder top level items to target Shared Drive folder, assume ID 2CY-45G was returned in previous step
|
||||
```
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid 2CY-45G
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid 2CY-45G
|
||||
```
|
||||
You can script the steps:
|
||||
|
||||
Linux/MacOS
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
targetFolderId=$(gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly)
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid $targetFolderId
|
||||
targetFolderId=$(gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly)
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid $targetFolderId
|
||||
```
|
||||
Windows PowerShell
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
$targetFolderId = & gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid $targetFolderId
|
||||
$targetFolderId = & gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid $targetFolderId
|
||||
```
|
||||
Windows Command Prompt
|
||||
```
|
||||
gam redirect csv ./TopSDItems.csv user user@domain.com print filelist select 1Bx-8W3 fields id,name,mimetype depth 0
|
||||
for /f "delims=" %a in ('gam user user@domain.com create drivefile mimetype gfolder teamdriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly') do set taregtFolderId=%a
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive teamdriveparentid %targetFolderId%
|
||||
for /f "delims=" %a in ('gam user user@domain.com create drivefile mimetype gfolder shareddriveparentid 0AE-9ZX drivefilename "New Folder Name" returnidonly') do set taregtFolderId=%a
|
||||
gam redirect stdout ./CopySharedDrive.txt multiprocess redirect stderr stdout csv TopSDItems.csv gam user user@domain.com copy drivefile "~id" recursive shareddriveparentid %targetFolderId%
|
||||
```
|
||||
|
||||
## Move files and folders
|
||||
@@ -605,10 +614,10 @@ This is the default mode.
|
||||
* `parentid <DriveFolderID>` - The target folder is identified by `<DriveFolderID>` which must be writable by `<UserTypeEntity>`.
|
||||
* `parentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - A search is performed for a folder named `<DriveFolderName>` owned by any user but must be writable by `<UserTypeEntity>`.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specific Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the moved file/folder will be located in the source folder.
|
||||
|
||||
### Duplicate files
|
||||
@@ -752,14 +761,14 @@ The following command will change the parents of the top level files and folders
|
||||
|
||||
* No permissions are processed.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX mergewithparent
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX mergewithparent
|
||||
```
|
||||
|
||||
If you want the source Shared Drive with ID 0AC_1AB to be contained in a top level folder of the target Shared Drive with ID 0AE_9ZX, omit the `mergewithparent` argument.
|
||||
The folder on the target Shared Drive will have the same name as the name of the source Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX newfilename "Copy of source Shared Drive"
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB shareddriveparentid 0AE_9ZX newfilename "Copy of source Shared Drive"
|
||||
```
|
||||
|
||||
### Inter-workspace moves
|
||||
@@ -769,7 +778,7 @@ Due to a restructuring, you want to move data from Shared Drive A in domaina.com
|
||||
* `user@domaina.com` is a manager of both Shared Drives.
|
||||
|
||||
```
|
||||
$ gam user user@domaina move drivefile teamdriveid <SharedDriveAID> teamdriveparentid <SharedDriveBID> mergewithparent
|
||||
$ gam user user@domaina move drivefile shareddriveid <SharedDriveAID> shareddriveparentid <SharedDriveBID> mergewithparent
|
||||
User: user@domaina.com, Move 1 Drive File/Folder
|
||||
User: user@domaina.com, Drive Folder: Shared Drive A(<SharedDriveAID>), Move(Merge) contents with Drive Folder: Shared Drive B(<SharedDriveBID>)
|
||||
User: user@domaina.com, Drive File: Filename(<FileID>), Move Failed: Bad Request. User message: "shareOutNotPermitted"
|
||||
@@ -785,13 +794,13 @@ The following command will change the parents of the top level files and folders
|
||||
|
||||
* No permissions are processed.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root mergewithparent
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root mergewithparent
|
||||
```
|
||||
|
||||
If you want the contents of Shared Drive with ID 0AC_1AB to be contained in a top level folder of the My Drive, omit the `mergewithparent` argument.
|
||||
The folder on the My Drive will have the same name as the name of the Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||
```
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root
|
||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root newfilename "Copy of Shared Drive"
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root
|
||||
gam user user@domain.com move drivefile shareddriveid 0AC_1AB parentid root newfilename "Copy of Shared Drive"
|
||||
```
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@
|
||||
|
||||
<SharedDriveID> ::= <String>
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <SharedDriveID>) | (teamdriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <SharedDriveID>) | (shareddriveid:<SharedDriveID>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveIDEntity> |
|
||||
@@ -315,8 +315,8 @@
|
||||
size|
|
||||
spaces|
|
||||
starred|
|
||||
teamdriveid|
|
||||
teamdrivename|
|
||||
shareddriveid|
|
||||
shareddrivename|
|
||||
thumbnaillink|
|
||||
thumbnailversion|
|
||||
title|
|
||||
@@ -430,6 +430,7 @@ gam <UserTypeEntity> show fileinfo <DriveFileEntity>
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
[stripcrsfromname]
|
||||
[formatjson]
|
||||
@@ -440,6 +441,7 @@ gam <UserTypeEntity> info drivefile <DriveFileEntity>
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])*
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [followshortcuts [<Boolean>]]
|
||||
[stripcrsfromname]
|
||||
[formatjson]
|
||||
@@ -880,11 +882,11 @@ Print or show the share type counts of a user's files. These fields are displaye
|
||||
```
|
||||
gam <UserTypeEntity> print filesharecounts [todrive <ToDriveAttribute>*]
|
||||
[excludetrashed]
|
||||
[internaldomains <DomainNameList>]
|
||||
[internaldomains all|primary|<DomainNameList>]
|
||||
[summary none|only|plus] [summaryuser <String>]
|
||||
gam <UserTypeEntity> show filesharecounts
|
||||
[excludetrashed]
|
||||
[internaldomains <DomainNameList>]
|
||||
[internaldomains all|primary|<DomainNameList>]
|
||||
[summary none|only|plus] [summaryuser <String>]
|
||||
```
|
||||
|
||||
@@ -892,8 +894,10 @@ By default, print|show filesharecounts displays share type counts of all files o
|
||||
|
||||
Use the `excludetrashed` option to suppress counting files in the trash.
|
||||
|
||||
By default, `internaldomains <DomainNameList>` defaults to your primary domain; if you have other domains that
|
||||
you consider internal, list all of them in `<DomainNameList>`.
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
By default, share type counts for individual users are displayed; the `summary` option offers alternatives
|
||||
that can display a summarization of share type counts across all users specified in the command.
|
||||
@@ -1100,6 +1104,7 @@ gam <UserTypeEntity> print|show filelist [todrive <ToDriveAttribute>*]
|
||||
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
|
||||
[showdrivename] [showshareddrivepermissions]
|
||||
[(showlabels details|ids)|(includelabels <ClassificationLabelIDList>)]
|
||||
[includepermissionsforview published]
|
||||
[showparentsidsaslist] [showpermissionslast]
|
||||
(orderby <DriveFileOrderByFieldName> [ascending|descending])* [delimiter <Character>]
|
||||
[stripcrsfromname]
|
||||
|
||||
@@ -149,10 +149,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveFileCreateAttribute> ::=
|
||||
<DriveFileAttribute>|
|
||||
@@ -196,10 +196,10 @@ You can specify where the new file is to be located:
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* If none of the parent options are specified, the parent folder is the root folder.
|
||||
|
||||
By default, Google assigns the current time to the attributes `createdTime` and `modifiedTime`; you can assign your own values
|
||||
@@ -290,7 +290,7 @@ This will create a three column CSV file SharedDriveNamesIDs.csv with columns: U
|
||||
You are building student folders on a Shared Drive as an admin and want to add ACLs to the folders
|
||||
allowing the student write access and you want a shortcut on the student's My Drive pointing to the folder.
|
||||
By adding the student's primary email address to the CSV output, it can be used in subsequent commands.
|
||||
Sustitute for admin@domain.com and `<TeamDriveID>`.
|
||||
Sustitute for admin@domain.com and `<SharedDriveID>`.
|
||||
```
|
||||
Students.csv
|
||||
primaryEmail,Name
|
||||
@@ -299,7 +299,7 @@ mary@domain.com, Mary Smith
|
||||
...
|
||||
|
||||
# Create the student folders on the Shared Drive
|
||||
gam redirect csv ./StudentFolders.csv multiprocess csv Students.csv gam user admin@domain.com create drivefile mimetype gfolder drivefilename "~~Name~~ Digital Portfolio" parentid <TeamDriveID> csv addcsvdata primaryEmail "~primaryEmail"
|
||||
gam redirect csv ./StudentFolders.csv multiprocess csv Students.csv gam user admin@domain.com create drivefile mimetype gfolder drivefilename "~~Name~~ Digital Portfolio" parentid <SharedDriveID> csv addcsvdata primaryEmail "~primaryEmail"
|
||||
# Add ACLs granting the students write access to their folders; "~User" refers to admin@domain.com
|
||||
gam csv StudentFolders.csv gam user "~User" add drivefileacl "~id" user "~primaryEmail" role fileorganizer
|
||||
# Add a shortcut to the folder on the student's My Drive
|
||||
@@ -389,7 +389,7 @@ User: user@domain.com, Drive Folder Path:, Create
|
||||
|
||||
Build in a Shared Drive Folder
|
||||
```
|
||||
gam user user@domain.com create drivefolderpath path "Top Folder/Middle Folder/Bottom Folder/Sub Folder" teamdriveparent "TS Shared Drive" teamdriveparentname "TS SD6 Folder"
|
||||
gam user user@domain.com create drivefolderpath path "Top Folder/Middle Folder/Bottom Folder/Sub Folder" shareddriveparent "TS Shared Drive" shareddriveparentname "TS SD6 Folder"
|
||||
Getting all Drive Files/Folders that match query (mimeType = 'application/vnd.google-apps.folder' and name = 'TS SD6 Folder' and trashed = false) for user@domain.com
|
||||
Got 1 Drive File/Folder that matched query (mimeType = 'application/vnd.google-apps.folder' and name = 'TS SD6 Folder' and trashed = false) for user@domain.com...
|
||||
User: user@domain.com, Drive Folder Path:, Create
|
||||
@@ -495,10 +495,10 @@ You can change where the new file is to be located; this removes all other paren
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
|
||||
You can add/remove parent folders without affecting other parent folders.
|
||||
* `addparents|removeparents <DriveFolderIDList>` - Specify the parent folders by ID.
|
||||
|
||||
@@ -30,13 +30,13 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>))|
|
||||
(teamdriveparentid <DriveFolderID>)|(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>))|
|
||||
(shareddriveparentid <DriveFolderID>)|(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
<DriveOrderByFieldName> ::=
|
||||
createddate|createdtime|
|
||||
|
||||
@@ -357,8 +357,8 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
|
||||
For example, to get the ACLs for your Team Drives with the Team Drive name included in the output:
|
||||
```
|
||||
gam redirect csv ./TeamDrives.csv print teamdrives
|
||||
gam redirect csv ./TeamDriveACLs.csv multiprocess csv ./TeamDrives.csv gam print drivefileacls teamdriveid "~id" addtitle "~name" fields id,domain,emailaddress,role,type,deleted
|
||||
gam redirect csv ./SharedDrives.csv print shareddrives
|
||||
gam redirect csv ./SharedDriveACLs.csv multiprocess csv ./SharedDrives.csv gam print drivefileacls shareddriveid "~id" addtitle "~name" fields id,domain,emailaddress,role,type,deleted
|
||||
```
|
||||
|
||||
## Delete all ACLs except owner from a file
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
(parentid <DriveFolderID>)|
|
||||
(parentname <DriveFolderName>)|
|
||||
(anyownerparentname <DriveFolderName>)|
|
||||
(teamdriveparentid <DriveFolderID>)|
|
||||
(teamdriveparent <SharedDriveName>)|
|
||||
(teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>)|
|
||||
(teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>)
|
||||
(shareddriveparentid <DriveFolderID>)|
|
||||
(shareddriveparent <SharedDriveName>)|
|
||||
(shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>)|
|
||||
(shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>)
|
||||
|
||||
```
|
||||
## Create shortcuts
|
||||
@@ -48,10 +48,10 @@ There are two modes of operaton:
|
||||
* `parentid <DriveFolderID>` - Folder ID.
|
||||
* `parentname <DriveFolderName>` - Folder name; the folder must be owned by `<UserTypeEntity>`.
|
||||
* `anyownerparentname <DriveFolderName>` - Folder name; the folder can be owned by any user, `<UserTypeEntity>` must be able to write to the folder.
|
||||
* `teamdriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `teamdriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `teamdriveparentid <SharedDriveID> teamdriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `teamdriveparent <SharedDriveName> teamdriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `shareddriveparentid <DriveFolderID>` - Shared Drive folder ID; when used alone, this indicates a specfic Shared Drive folder.
|
||||
* `shareddriveparent <SharedDriveName>` - Shared Drive name; when used alone, this indicates the root level of the Shared Drive.
|
||||
* `shareddriveparentid <SharedDriveID> shareddriveparentname <DriveFolderName>` - A Shared Drive ID and a folder name on that Shared Drive.
|
||||
* `shareddriveparent <SharedDriveName> shareddriveparentname <DriveFolderName>` - A Shared Drive name and a folder name on that Shared Drive.
|
||||
* `convertparents` - Convert all but the last parent reference in `<DriveFileEntity>` to shortcuts. My testing shows that as parents are added to a file, they are added to the front of the parents list; thus, the last parent is the original parent.
|
||||
|
||||
If neither `<DriveFileParentAttribute>` nor `convertparents` are specified, the shortcut is placed in the root folder (My Drive).
|
||||
@@ -142,6 +142,6 @@ gam csv Shortcuts.csv matchfield code 4 gam user "~owner" create drivefileshortc
|
||||
|
||||
## Check shortcut validity on Shared Drives
|
||||
```
|
||||
gam redirect csv ./TDShortcuts.csv user organizer@domain.com print filelist select teamdriveid <SharedDriveID> showmimetype gshortcut fields id
|
||||
gam redirect csv ./TDShortcuts.csv user organizer@domain.com print filelist select shareddriveid <SharedDriveID> showmimetype gshortcut fields id
|
||||
gam redirect csv ./Shortcuts.csv user organizer@domain.com check drivefileshortcut csvfile TDShortcuts.csv:id csv
|
||||
```
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
- [API documentation](#api-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Aliases](#aliases)
|
||||
- [Delegation Notification](#delegation-notification)
|
||||
- [Delegator Banner](#delegator-banner)
|
||||
- [Delegate Notification](#delegate-notification)
|
||||
- [Create Gmail delegates](#create-gmail-delegates)
|
||||
- [Delete Gmail delegates](#delete-gmail-delegates)
|
||||
- [Update Gmail delegates](#update-gmail-delegates)
|
||||
@@ -57,7 +58,13 @@ The `convertalias` option causes GAM to make an extra API call per user in `<Use
|
||||
to convert aliases to primary email addresses. If you know that all of the email addresses
|
||||
in `<UserEntity>` are primary, you can omit `convertalias` and avoid the extra API calls.
|
||||
|
||||
## Delegation Notification
|
||||
## Delegator Banner
|
||||
When creating a delegate, the following banner is displayed in the delegator's Gmail inbox, it can not be suppressed.
|
||||
```
|
||||
<Delegate> now has delegated access to your account. This notice will end in 7 days.
|
||||
```
|
||||
|
||||
## Delegate Notification
|
||||
When creating a delegate, you can send a message to the delegate.
|
||||
```
|
||||
[notify [<Boolean>]
|
||||
|
||||
@@ -404,7 +404,7 @@ Messages are archived to the group specified by `<GroupItem>`.
|
||||
### Archive a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_archive` - Limit the number of messages that will be archived; use a value of 0 for no limit
|
||||
* `max_to_archive <Number>` - Limit the number of messages that will be archived; use a value of 0 for no limit
|
||||
* `doit` - No messages are archived unless you specify `doit`. By not specifying `doit`, you can preview the messages selected to verify that the results match your expectations.
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
@@ -412,8 +412,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_archive` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_archive` of the matching messages will be fetched.
|
||||
By default, Gam fetches all matching messages from Google and then archives only `max_to_archive <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_archive <Number>` of the matching messages will be fetched.
|
||||
You must still specify `doit` to perform the operation.
|
||||
|
||||
By default, the command results are displayed as indented keys and values. Use the `csv` option
|
||||
@@ -447,6 +447,9 @@ gam <UserTypeEntity> export thread|threads
|
||||
[targetfolder <FilePath>] [targetname <FileName>] [overwrite [<Boolean>]]
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then exports only `max_to_export <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_export <Number>` of the matching messages will be fetched.
|
||||
|
||||
By default, when exporting a message, it is downloaded to the directory specified in `gam.cfg/drive_dir`.
|
||||
* `targetfolder <FilePath>` - Specify an alternate location for the downloaded file.
|
||||
|
||||
@@ -477,6 +480,9 @@ gam <UserTypeEntity> forward thread|threads recipient|to <RecipientEntity>
|
||||
[subject <String>] [addorigfieldstosubject]
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then forwards only `max_to_forward <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_forwrd <Number>` of the matching messages will be fetched.
|
||||
|
||||
By default, the message subject has `Fwd: ` prepended; use `subject <String>` to specify a new subject.
|
||||
|
||||
All `Cc` addresses are removed from the forwarded message.
|
||||
@@ -538,7 +544,7 @@ user@domain.com,18e9fc58c5491f4c,Deleted,
|
||||
### Manage a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_xxx` - Limit the number of messages that will be processed; use a value of 0 for no limit
|
||||
* `max_to_xxx <Number>` - Limit the number of messages that will be processed; use a value of 0 for no limit
|
||||
* `doit` - No messages are processed unless you specify `doit`. By not specifying `doit`, you can preview the messages selected to verify that the results match your expectations.
|
||||
|
||||
When `matchlabel <LabelName>` is specified, the following characters are replaced with a `-` in the generated query.
|
||||
@@ -546,8 +552,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_process` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_process` of the matching messages will be fetched.
|
||||
By default, Gam fetches all matching messages from Google and then processes only `max_to_process <Number>` of them.
|
||||
To speed up fetching, specify `quick` and only `max_to_process <Number>` of the matching messages will be fetched.
|
||||
You must still specify `doit` to perform the operation.
|
||||
|
||||
## Delete messages by Message-Id
|
||||
@@ -611,7 +617,7 @@ gam <UserTypeEntity> print messages|threads [todrive <ToDriveAttribute>*]
|
||||
```
|
||||
## Display all messages
|
||||
By default, Gam displays all messages.
|
||||
* `max_to_xxx` - Limit the number of messages that will be displayed
|
||||
* `max_to_print|max_to_show <Number>` - Limit the number of messages that will be displayed
|
||||
* `includespamtrash` - Include messages in the Spam and Trash folders
|
||||
|
||||
By default, all messages in a thread are displayed with `print|show threads`.
|
||||
@@ -627,7 +633,7 @@ gam user user@domain.com print|show threads maxmessagesperthread 1
|
||||
## Display a selected set of messages
|
||||
* `((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+` - Criteria to select messages
|
||||
* `labelids <LabelIDList>` - Select messages with labels that match all of the specified label IDs.
|
||||
* `max_to_xxx` - Limit the number of messages that will be displayed
|
||||
* `max_to_print|max_to_show <Number>` - Limit the number of messages that will be displayed
|
||||
* `includespamtrash` - Include messages in the Spam and Trash folders
|
||||
* `labelmatchpattern <REMatchPattern>` - Only display messages with some label that matches `<REMatchPattern>`
|
||||
* `labelmatchpattern xyz` - Label must start with xyz
|
||||
@@ -641,8 +647,8 @@ When `matchlabel <LabelName>` is specified, the following characters are replace
|
||||
&()"|{}/
|
||||
```
|
||||
|
||||
By default, Gam fetches only `max_to_process` matching messages from Google and then displays them.
|
||||
To see how many messages actually match, specify `notquick` and all matching messages will be fetched; only `max_to_process` of them will be displayed.
|
||||
By default, Gam fetches only `max_to_process <Number>` matching messages from Google and then displays them.
|
||||
To see how many messages actually match, specify `notquick` and all matching messages will be fetched; only `max_to_process <Number>` of them will be displayed.
|
||||
|
||||
### Difference between `From` and `Sender` headers
|
||||
The `From` header specifies the author of the message, that is,
|
||||
|
||||
@@ -116,7 +116,7 @@ gam <UserTypeEntity> show sendas [compact|format|html]
|
||||
```
|
||||
|
||||
These are the output formatting options:
|
||||
* `compact` - Escape carriage returns as \r and newlines as \n in original HTML; this format produces output that can be used as input to GAM
|
||||
* `compact` - Escape carriage returns as \r and newlines as \n in original HTML
|
||||
* `format` - Strip HTML keywords leaving basic printable information
|
||||
* `html` - Show original HTML; this is the default option; the output is human readable but cannot be used as input to GAM
|
||||
|
||||
@@ -184,12 +184,13 @@ If you have a current default signature, the API will update that, but if you de
|
||||
## Display signature
|
||||
### Display the signature as an indented list of keys and values.
|
||||
```
|
||||
gam <UserTypeEntity> show signature|sig [compact|format|html]
|
||||
gam <UserTypeEntity> show signature|sig [compact|format|html|template]
|
||||
[primary|default] [verifyonly]
|
||||
```
|
||||
|
||||
These are the output formatting options:
|
||||
* `compact` - Escape carriage returns as \r and newlines as \n in original HTML; this format produces output that can be used as input to GAM
|
||||
* `template` - Escape carriage returns as \r and newlines as \n in original HTML; this format produces output that can be used as input to GAM
|
||||
* `compact` - Escape carriage returns as \r and newlines as \n in original HTML
|
||||
* `format` - Strip HTML keywords leaving basic printable information
|
||||
* `html` - Show original HTML; this is the default option; the output is human readable but cannot be used an input to GAM
|
||||
|
||||
@@ -295,19 +296,10 @@ Use text like `{FirstName}` and `{Email}` in the locations where the actual valu
|
||||
|
||||
Once you're created the template signature, do the following:
|
||||
```
|
||||
$ gam user testuser@domain.com show signature compact > SimpleSig.html
|
||||
$ more SimpleSig.html
|
||||
SendAs Address: <testuser@domain.com>
|
||||
IsPrimary: True
|
||||
Default: True
|
||||
Signature: <div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
|
||||
```
|
||||
Edit SimpleSig.html and delete all text from `SendAs ` through `Signature: `.
|
||||
The result should be:
|
||||
```
|
||||
$ gam redirect stdout ./SigTemplate.html user testuser@domain.com show signature template
|
||||
$ more SigTemplate.html
|
||||
<div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
|
||||
```
|
||||
|
||||
This is a sample Users.csv file.
|
||||
```
|
||||
email,first,last,phone
|
||||
@@ -317,5 +309,20 @@ mjones@domain.com,Mary,Jones,510-555-1212 x 456
|
||||
|
||||
This command will update the user's signatures.
|
||||
```
|
||||
gam csv Users.csv gam user "~email" signature htmlfile SimpleSig.html replace FirstName "~first" replace LastName "~last" replace Phone "~phone" replace Email "~email"
|
||||
```
|
||||
gam csv Users.csv gam user "~email" signature htmlfile SigTemplate.html replace FirstName "~first" replace LastName "~last" replace Phone "~phone" replace Email "~email"
|
||||
```
|
||||
|
||||
You can also do the update with user attributes:
|
||||
|
||||
This is a sample Users.csv file.
|
||||
|
||||
```
|
||||
email
|
||||
bsmith@domain.com
|
||||
mjones@domain.com
|
||||
```
|
||||
|
||||
This command will update the user's signatures.
|
||||
```
|
||||
gam csv Users.csv gam user "~email" signature htmlfile SigTemplate.html replace FirstName field:name.givenname replace LastName field:name.familyname replace Phone field:phone.value.type.work replace Email field:email.primaryemail
|
||||
```
|
||||
|
||||
@@ -586,6 +586,9 @@ Getting all Groups for testuser1@domain.com (1/2)
|
||||
Getting all Groups for testuser2@domain.com (2/2)
|
||||
User,Groups,GroupsList
|
||||
testuser2@domain.com,0,
|
||||
|
||||
# Get group membership for all users
|
||||
$ gam config auto_batch_min 1 num_threads 10 redirect csv ./UsersGroups.csv multiprocess sortheaders User,Group redirect stderr - multiprocess all users print groups
|
||||
```
|
||||
### Display groups and their parents
|
||||
Display a user's groups and their parents as an indented list.
|
||||
|
||||
@@ -148,6 +148,8 @@
|
||||
<CSVkmdSelector> |
|
||||
<CSVDataSelector>
|
||||
|
||||
<QuerySharedDrive> ::= <String> See: https://developers.google.com/workspace/drive/api/guides/search-shareddrives
|
||||
|
||||
<SharedDriveACLRole> ::=
|
||||
manager|organizer|owner|
|
||||
contentmanager|fileorganizer|
|
||||
@@ -159,8 +161,8 @@
|
||||
<SharedDriveName> ::= <String>
|
||||
<SharedDriveEntity> ::=
|
||||
<SharedDriveID>|
|
||||
(teamdriveid <SharedDriveID>)|(teamdriveid:<SharedDriveID>)|
|
||||
(teamdrive <SharedDriveName>)|(teamdrive:<SharedDriveName>)
|
||||
(shareddriveid <SharedDriveID>)|(shareddriveid:<SharedDriveID>)|
|
||||
(shareddrive <SharedDriveName>)|(shareddrive:<SharedDriveName>)
|
||||
|
||||
<SharedDriveFieldName> ::=
|
||||
backgroundimagefile|
|
||||
@@ -174,10 +176,10 @@
|
||||
themeid
|
||||
<SharedDriveFieldNameList> ::= "<SharedDriveFieldName>(,<SharedDriveFieldName>)*"
|
||||
|
||||
<SharedDriveIDEntity> ::= (teamdriveid <DriveFileItem>) | (teamdriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (teamdrive <SharedDriveName>) | (teamdrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (teamdrivefilename <DriveFileName>) | (teamdrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::= (teamdrivequery <QueryDriveFile>) | (teamdrivequery:<QueryDriveFile>)
|
||||
<SharedDriveIDEntity> ::= (shareddriveid <DriveFileItem>) | (shareddriveid:<DriveFileItem>)
|
||||
<SharedDriveNameEntity> ::= (shareddrive <SharedDriveName>) | (shareddrive:<SharedDriveName>)
|
||||
<SharedDriveFileNameEntity> ::= (shareddrivefilename <DriveFileName>) | (shareddrivefilename:<DriveFileName>)
|
||||
<SharedDriveFileQueryEntity> ::= (shareddrivequery <QueryDriveFile>) | (shareddrivequery:<QueryDriveFile>)
|
||||
<SharedDriveFileQueryShortcut> ::=
|
||||
all_files | all_folders | all_google_files | all_non_google_files | all_items
|
||||
|
||||
@@ -291,11 +293,11 @@ When either of these options is chosen, no infomation about Shared Drive restric
|
||||
To retrieve the Shared Drive ID with `returnidonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
teamDriveId=$(gam user user@domain.com create shareddrive ... returnidonly)
|
||||
shareddriveId=$(gam user user@domain.com create shareddrive ... returnidonly)
|
||||
Windows PowerShell
|
||||
$teamDriveId = & gam user user@domain.com create shareddrive ... returnidonly
|
||||
$shareddriveId = & gam user user@domain.com create shareddrive ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam user user@domain.com create shareddrive ... returnidonly') do set teamDriveId=%a
|
||||
for /f "delims=" %a in ('gam user user@domain.com create shareddrive ... returnidonly') do set shareddriveId=%a
|
||||
```
|
||||
|
||||
## Bulk Create Shared Drives
|
||||
@@ -417,12 +419,12 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
Display the number of Shared Drives.
|
||||
```
|
||||
gam <UserTypeEntity> show|print shareddrives
|
||||
[teamdriveadminquery|query <QueryTeamDrive>]
|
||||
[shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
showitemcountonly
|
||||
```
|
||||
By default, all Shared Drives are counted; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
|
||||
@@ -466,7 +468,7 @@ Options `shareddriveadminquery|query` and `shareddrives|teamdrives` are mutually
|
||||
Options `shareddriveadminquery|query` and `orgunit|org|ou` require `adminaccess|asadmin`.
|
||||
|
||||
By default, organizers for all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddrives|teamdrives <SharedDriveIDList>` - Select the Shared Drive IDs specified in `<SharedDriveIDList>`
|
||||
* `shareddrives|teamdrives select <FileSelector>|<CSVFileSelector>` - Select the Shared Drive IDs specified in `<FileSelector>|<CSVFileSelector>`
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
@@ -486,7 +488,7 @@ To select organizers from any domain, use: `domainlist ""`
|
||||
|
||||
For example, to get a single user organizer from your domain for all Shared Drives including no organizer drives:
|
||||
```
|
||||
gam redirect csv ./TeamDriveOrganizers.csv print shareddriveorganizers
|
||||
gam redirect csv ./ShareddriveOrganizers.csv print shareddriveorganizers
|
||||
```
|
||||
|
||||
## Manage Shared Drive access
|
||||
@@ -596,14 +598,14 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
## Display Shared Drive access for selected Shared Drives
|
||||
```
|
||||
gam <UserTypeEntity> show shareddriveacls
|
||||
adminaccess [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
adminaccess [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
[oneitemperrow] [<DrivePermissionsFieldName>*|(fields <DrivePermissionsFieldNameList>)]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam <UserTypeEntity> print shareddriveacls [todrive <ToDriveAttribute>*]
|
||||
adminaccess [teamdriveadminquery|query <QueryTeamDrive>]
|
||||
adminaccess [shareddriveadminquery|query <QuerySharedDrive>]
|
||||
[matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
||||
[user|group <EmailAddress> [checkgroups]] (role|roles <SharedDriveACLRoleList>)*
|
||||
<PermissionMatch>* [<PermissionMatchAction>] [pmselect]
|
||||
@@ -615,7 +617,7 @@ Shared Drives in the workspace, `<UserTypeEntity>` should specify a super admin
|
||||
option shoud be used.
|
||||
|
||||
By default, all Shared Drives are displayed; use the following options to select a subset of Shared Drives:
|
||||
* `teamdriveadminquery|query <QueryTeamDrive>` - Use a query to select Shared Drives
|
||||
* `shareddriveadminquery|query <QuerySharedDrive>` - Use a query to select Shared Drives
|
||||
* `matchname <REMatchPattern>` - Retrieve Shared Drives with names that match a pattern.
|
||||
* `orgunit|org|ou <OrgUnitPath>` - Only Shared Drives in the specified Org Unit are selected
|
||||
* `<PermissionMatch>* [<PermissionMatchAction>] pmselect` - Use permission matching to select Shared Drives
|
||||
|
||||
@@ -1018,6 +1018,10 @@ These existing options enable the display of additional information.
|
||||
* `(products|product <ProductIDList>)|(skus|sku <SKUIDList>)` - Display license information for a selected list of products/SKUs.
|
||||
* `schemas|custom|customschemas <SchemaNameList>` - Display all fields or selected fields of the specified custom schemas
|
||||
|
||||
You can use `license_skus = <SKUIDList>` in `gam.cfg` to list all of the licensing SKUs
|
||||
used in your workspace. Without this list or the `products|skus` options from above,
|
||||
GAM has to make 70+ API calls to get the licenses for a user; this can cause quota limit errors.
|
||||
|
||||
By default, Gam displays fields that only an adminstrator can view.
|
||||
* `userview` - Only display fields that other users in the domain can view.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
@@ -15,7 +15,7 @@ Time: 2025-12-23T13:57:00-08:00
|
||||
Print the current version of Gam with details and time offset information
|
||||
```
|
||||
gam version timeoffset
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 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
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
@@ -35,7 +35,7 @@ Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2025-12-23T13:57:00-08:00
|
||||
Your system time differs from admin.googleapis.com by less than 1 second
|
||||
OpenSSL 3.5.3 16 Sep 2025
|
||||
OpenSSL 3.6.1 27 Jan 2026
|
||||
arrow 1.3.0
|
||||
chardet 5.2.0
|
||||
cryptography 46.0.1
|
||||
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 7.31.06
|
||||
Latest: 7.34.00
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -76,7 +76,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
7.31.06
|
||||
7.34.00
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -86,7 +86,7 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 7.31.06 - https://github.com/GAM-team/GAM
|
||||
GAM 7.34.00 - https://github.com/GAM-team/GAM
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
|
||||
@@ -33,6 +33,7 @@ Notes and Information
|
||||
* [Questions? Visit the GAM Discussion Forum](https://groups.google.com/forum/#!forum/google-apps-manager)
|
||||
* [GAM Public Chat Room](GAM-Public-Chat-Room)
|
||||
* [Scripts](Scripts)
|
||||
* [Code Wiki](https://codewiki.google/github.com/gam-team/gam)
|
||||
* [Other Resources](Other-Resources)
|
||||
* [Drive REST API v3](Drive-REST-API-v3)
|
||||
* [BNF Syntax](BNF-Syntax)
|
||||
@@ -73,6 +74,7 @@ Client Access
|
||||
* [Calendars](Calendars)
|
||||
* [Calendars - Access](Calendars-Access)
|
||||
* [Calendars - Events](Calendars-Events)
|
||||
* [Calendars - Secondary Calendars with no Owner](No-Owner-Secondary-Calendars)
|
||||
* [Chrome Auto Update Expiration Counts](Chrome-AUE-Counts)
|
||||
* [Chrome Browser Cloud Management](Chrome-Browser-Cloud-Management)
|
||||
* [Chrome Device Counts](Chrome-Device-Counts)
|
||||
|
||||
@@ -232,6 +232,11 @@ csv_output_header_order
|
||||
Any headers in the file but not in the list will appear after
|
||||
the headers in the list
|
||||
Default: ''
|
||||
csv_output_header_required
|
||||
A list of <Strings> used to specify column headers
|
||||
for inclusion in the CSV file written by a gam print command
|
||||
even if the API didn't return any data for those columns.
|
||||
Default: ''
|
||||
csv_output_line_terminator
|
||||
Allowed values: cr, lf, crlf
|
||||
Designates character(s) used to terminate the lines of a CSV file.
|
||||
@@ -307,6 +312,21 @@ debug_level
|
||||
debug_redaction
|
||||
Enable/disable redaction of sensitive data from API debugging output
|
||||
Default: True
|
||||
developer_preview_apis
|
||||
A comma separated list of APIs requiring a Developer Preview key.
|
||||
Default: Blank
|
||||
Valid values:
|
||||
accesscontextmanager,admin,alertcenter,analyticsadmin,calendar-json,cbcm,chat,
|
||||
chromemanagement,chromepolicy,classroom,cloudchannel,cloudidentity,
|
||||
cloudresourcemanager,contactdelegation,contacts,datastudio,docs,drive,
|
||||
driveactivity,drivelabels,email-audit,forms,gmail,groupsmigration,groupssettings,
|
||||
iam,iamcredentials,keep,licensing,meet,mybusinessaccountmanagement,oauth2,
|
||||
orgpolicy,people,pubsub,reseller,searchconsole,serviceaccountlookup,
|
||||
servicemanagement,serviceusage,sheets,siteVerification,storage,tagmanager,tasks,
|
||||
vault,versionhistory,youtube
|
||||
developer_preview_api_key
|
||||
A Developer Preview API key that is passed to all API calls for APIs in developer_preview_apis
|
||||
Default: Blank
|
||||
device_max_results
|
||||
When retrieving lists of ChromeOS devices from API,
|
||||
how many should be retrieved in each API call
|
||||
@@ -325,13 +345,6 @@ drive_max_results
|
||||
how many should be retrieved in each API call
|
||||
Default: 1000
|
||||
Range: 1 - 1000
|
||||
drive_v3_beta
|
||||
Enable/disable use of Drive API v3 beta for Limited Folder Access testing
|
||||
Default: False
|
||||
drive_v3_native_names
|
||||
Enable/disable use of Drive API v3 native column names
|
||||
in all gam print/show commands related to Google Drive
|
||||
Default: True
|
||||
email_batch_size
|
||||
When archiving, printing, showing, trashing, untrashing, marking as spam Gmail messages.
|
||||
how many should be processed in each batch
|
||||
@@ -371,7 +384,7 @@ gmail_cse_inkey_dir
|
||||
Default: Blank
|
||||
input_dir
|
||||
Input directory for files with non-absolute file names.
|
||||
The default
|
||||
The default is the current working directory.
|
||||
Default: .
|
||||
inter_batch_wait
|
||||
When processing items in batches, how many seconds should GAM wait between batches
|
||||
@@ -387,9 +400,6 @@ license_skus
|
||||
Each item in the list can be a <SKUID> which will be validated or
|
||||
a <ProductID>/<SKUID> which will not be validated.
|
||||
Default: Blank
|
||||
meet_v2_beta
|
||||
Enable/disable use of Meet API v2 beta for additional Chat Space parameters.
|
||||
Default: False
|
||||
member_max_results
|
||||
When retrieving lists of Google Group members from API,
|
||||
how many should be retrieved in each API call
|
||||
@@ -404,7 +414,7 @@ member_max_results_ci_basic
|
||||
member_max_results_ci_full
|
||||
When retrieving lists of Cloud Identity Group members from API
|
||||
with either the basic or full options,
|
||||
how many should be retrieved in each API call
|
||||
how many should be retrieved in each API call
|
||||
Default: 500
|
||||
Range: 1 - 500
|
||||
message_batch_size
|
||||
@@ -463,6 +473,10 @@ oauth2_txt
|
||||
Path to oauth2.txt
|
||||
Default: GamConfigDir/oauth2.txt
|
||||
Environment variable: OAUTHFILE
|
||||
oauth2_txt_lock_mode
|
||||
Allowed values: 644, 664, 666
|
||||
File permissions for oauth2.txt.lock
|
||||
Default: 644
|
||||
oauth2service_json
|
||||
Path to oauth2service.json
|
||||
Default: GamConfigDir/oauth2service.json
|
||||
@@ -573,7 +587,7 @@ timezone
|
||||
to your local timezone. If you are running GAM on a remote computer or on a
|
||||
cloud shell, "local" will mean the time at the remote/cloud shell computer,
|
||||
not your location, Use "+|-hh:mm" to specify the timezone at your location.
|
||||
Starting with version 7.21.00 you can use a timezone name; the names are case sensitive.
|
||||
Starting with version 7.21.00 you can use a timezone name; the names are case sensitive.
|
||||
See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
Default: utc
|
||||
Range: utc|z|local|(+|-hh:mm)|<ValidTimezoneName>
|
||||
@@ -721,6 +735,8 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
@@ -933,7 +949,7 @@ Edit gam.cfg to set up additional clients; it should look like this when complet
|
||||
activity_max_results = 100
|
||||
admin_email = ''
|
||||
api_calls_rate_check = false
|
||||
api_calls_rate_limit = 1000
|
||||
api_calls_rate_limit = 100
|
||||
api_calls_tries_limit = 10
|
||||
auto_batch_min = 0
|
||||
bail_on_internal_error_tries = 2
|
||||
@@ -943,55 +959,75 @@ cache_dir = /Users/admin/.gam/gamcache
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
cmdlog = ''
|
||||
cmdlog_max_backups = 5
|
||||
cmdlog_max_kilo_bytes = 1000
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
cmdlog = ''
|
||||
cmdlog_max_backups = 5
|
||||
cmdlog_max_kilo_bytes = 1000
|
||||
config_dir = /Users/admin/.gam
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
csv_input_row_filter = ''
|
||||
csv_input_row_filter_mode = allmatch
|
||||
csv_input_row_limit = 0
|
||||
csv_output_column_delimiter = ,
|
||||
csv_output_convert_cr_nl = false
|
||||
csv_output_field_delimiter = ' '
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_header_required = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter =
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = my_customer
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain =
|
||||
domain = ''
|
||||
drive_dir = /Users/admin/Downloads
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 100
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args =
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_sku = ''
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 1000
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
no_short_urls = true
|
||||
no_update_check = true
|
||||
no_verify_ssl = false
|
||||
num_tbatch_threads = 2
|
||||
@@ -1005,9 +1041,10 @@ print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = False
|
||||
quick_info_user = False
|
||||
quick_cros_move = false
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section =
|
||||
show_api_calls_retry_data = false
|
||||
show_commands = false
|
||||
@@ -1022,26 +1059,28 @@ smtp_password = ''
|
||||
smtp_username = ''
|
||||
timezone = utc
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_no_escape_char = true
|
||||
todrive_parent = root
|
||||
todrive_sheet_timeformat = ''
|
||||
todrive_sheet_timestamp = false
|
||||
todrive_timeformat = ''
|
||||
todrive_timestamp = false
|
||||
todrive_timezone = ''
|
||||
todrive_upload_nodata = true
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
|
||||
Reference in New Issue
Block a user