Updated print aliases|groups|group-members|users

Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`
This commit is contained in:
Ross Scroggs
2023-09-13 13:45:10 -07:00
parent e998bcfde6
commit effa972a40
16 changed files with 389 additions and 203 deletions

View File

@@ -21,8 +21,12 @@
* https://developers.google.com/admin-sdk/directory/v1/guides/search-users * https://developers.google.com/admin-sdk/directory/v1/guides/search-users
## Definitions ## Definitions
See [Collections of Items](Collections-of-Items)
``` ```
<DomainName> ::= <String>(.<String>)+ <DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName> <EmailAddress> ::= <String>@<DomainName>
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*" <EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
<EmailAddressEntity> ::= <EmailAddressList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector> <EmailAddressEntity> ::= <EmailAddressList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector>
@@ -84,7 +88,8 @@ gam info alias|aliases <EmailAddressEntity>
Display selected aliases. Display selected aliases.
``` ```
gam print aliases [todrive <ToDriveAttribute>*] gam print aliases [todrive <ToDriveAttribute>*]
[domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>])
[user|users <EmailAddressList>] [group|groups <EmailAddressList>] [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
[select <UserTypeEntity>] [select <UserTypeEntity>]
[aliasmatchpattern <RegularExpression>] [aliasmatchpattern <RegularExpression>]
@@ -94,8 +99,9 @@ gam print aliases [todrive <ToDriveAttribute>*]
(addcsvdata <FieldName> <String>)* (addcsvdata <FieldName> <String>)*
``` ```
By default, group and user aliases in all domains in the account are selected; these options allow selection of subsets of aliases: By default, group and user aliases in all domains in the account are selected; these options allow selection of subsets of aliases:
* `domain <DomainName>` - Limit aliases to those in `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit aliases to those in the domains specified by `<DomainNameEntity>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Print aliases for selected users * `(query <QueryUser>)|(queries <QueryUserList>)` - Print aliases for users/groups that match a query; each query is run against each domain
* `limittoou <OrgUnitItem>` - Print aliases for users in the specified `<OrgUnitItem>`
* `user|users <EmailAddressList>` - Print aliases for users in `<EmailAddressList` * `user|users <EmailAddressList>` - Print aliases for users in `<EmailAddressList`
* `select <UserTypeEntity>` - Print aliases for users in `<UserTypeEntity>` * `select <UserTypeEntity>` - Print aliases for users in `<UserTypeEntity>`
* `group|groups <EmailAddressList>` - Print aliases for groups in `<EmailAddressList` * `group|groups <EmailAddressList>` - Print aliases for groups in `<EmailAddressList`
@@ -117,6 +123,21 @@ By default, the aliases in a list are separated by the `csv_output_field_delimit
Specifying both `onerowpertarget` and `suppressnoaliasrows` causes GAM to not display any targets that have no aliases. Specifying both `onerowpertarget` and `suppressnoaliasrows` causes GAM to not display any targets that have no aliases.
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
```
$ gam print aliases domains school.org,students.school.org queries "'email:admin*','email:test*'"
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 3 Users: admin@school.org - admindirector@school.org
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 20 Users: testusera@school.org - testuserx@school.org
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 1 User: admin@students.school.org - admin@students.school.org
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
Alias,Target,TargetType
...
```
## Bulk delete aliases ## Bulk delete aliases
You can bulk delete aliases as follows; use `(query <QueryUser>)|(queries <QueryUserList>)` and You can bulk delete aliases as follows; use `(query <QueryUser>)|(queries <QueryUserList>)` and
`aliasmatchpattern <RegularExpression>` as desired. `aliasmatchpattern <RegularExpression>` as desired.

View File

@@ -10,6 +10,37 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation. See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation.
### 6.63.14
Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`. When a copy/move
operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
`verifyorganizer false` suppresses the verification.
Updated the following commands to be able to specify a list of domains rather than a single domain:
```
gam print aliases
gam print groups
gam print|show group-members
gam print users
```
Added `print_agu_domains` variable to `gam.cfg` that provides a default list of domains for these commands.
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
```
$ gam print users domains school.org,students.school.org queries "'email:admin*','email:test*'"
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 3 Users: admin@school.org - admindirector@school.org
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 20 Users: testusera@school.org - testuserx@school.org
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 1 User: admin@students.school.org - admin@students.school.org
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
primaryEmail
...
```
### 6.63.13 ### 6.63.13
Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename` Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename`

View File

@@ -19,6 +19,7 @@
* https://developers.google.com/admin-sdk/directory/v1/reference/members * https://developers.google.com/admin-sdk/directory/v1/reference/members
## Definitions ## Definitions
See [Collections of Items](Collections-of-Items)
``` ```
<DeliverySetting> ::= <DeliverySetting> ::=
allmail| allmail|
@@ -27,6 +28,9 @@
disabled| disabled|
none|nomail none|nomail
<DomainName> ::= <String>(.<String>)+ <DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName> <EmailAddress> ::= <String>@<DomainName>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String> <EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<UniqueID> ::= id:<String> <UniqueID> ::= id:<String>
@@ -41,6 +45,7 @@
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*" <GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
<QueryGroup> ::= <String> <QueryGroup> ::= <String>
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<MembersFieldName> ::= <MembersFieldName> ::=
delivery|deliverysettings| delivery|deliverysettings|
@@ -563,7 +568,7 @@ gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity>
By default, delivery information is not displayed. By default, delivery information is not displayed.
``` ```
gam print group-members [todrive <ToDriveAttribute>*] gam print group-members [todrive <ToDriveAttribute>*]
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(group|group_ns|group_susp <GroupItem>)| (group|group_ns|group_susp <GroupItem>)|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -581,10 +586,10 @@ gam print group-members [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
``` ```
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups: By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>` * `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>` * `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
* `query <QueryGroup>` - Limit display to groups that match `<QueryGroup>`, matching is done at Google; mutually exclusive with `member <UserItem>` * `(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 <GroupItem>` - Limit display to the single group `<GroupItem>`
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members * `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 * `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members
@@ -679,7 +684,7 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
## Display group membership in hierarchical format ## Display group membership in hierarchical format
``` ```
gam show group-members gam show group-members
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(group|group_ns|group_susp <GroupItem>)| (group|group_ns|group_susp <GroupItem>)|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -692,10 +697,10 @@ gam show group-members
[includederivedmembership] [includederivedmembership]
``` ```
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups: By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>` * `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>` * `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
* `query <QueryGroup>` - Limit display to groups that match `<QueryGroup>`, matching is done at Google; mutually exclusive with `member <UserItem>` * `(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 <GroupItem>` - Limit display to the single group `<GroupItem>`
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members * `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 * `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members

View File

@@ -44,8 +44,12 @@
* https://support.google.com/a/answer/167430 * https://support.google.com/a/answer/167430
## Definitions ## Definitions
See [Collections of Items](Collections-of-Items)
``` ```
<DomainName> ::= <String>(.<String>)+ <DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName> <EmailAddress> ::= <String>@<DomainName>
<UniqueID> ::= id:<String> <UniqueID> ::= id:<String>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String> <EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
@@ -60,6 +64,7 @@
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*" <GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
<QueryGroup> ::= <String> <QueryGroup> ::= <String>
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<QueryDynamicGroup> ::= <String> <QueryDynamicGroup> ::= <String>
See: https://cloud.google.com/identity/docs/reference/rest/v1/groups#dynamicgroupquery See: https://cloud.google.com/identity/docs/reference/rest/v1/groups#dynamicgroupquery
@@ -404,7 +409,7 @@ By default, Gam displays the information as an indented list of keys and values.
This command displays information in CSV format. This command displays information in CSV format.
``` ```
gam print groups [todrive <ToDriveAttribute>*] gam print groups [todrive <ToDriveAttribute>*]
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)* [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -423,12 +428,11 @@ gam print groups [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
``` ```
By default, all groups in the account are displayed, these options allow selection of subsets of groups: By default, all groups in the account are displayed, these options allow selection of subsets of groups:
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>` * `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>` * `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
* `query <QueryGroup>` - Limit display to groups that match <QueryGroup>, matching is done at Google; mutually exclusive with `member <UserItem>` * `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
* `select <GroupEntity>` - Limit display to the groups specified in `<GroupEntity>` * `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. When using `query <QueryGroup>` with the `name:{PREFIX}*` query, `PREFIX` must contain at least three characters.

View File

@@ -174,6 +174,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = false quick_cros_move = false
quick_info_user = false quick_info_user = false
@@ -330,7 +331,7 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.10.8 64-bit final Python 3.10.8 64-bit final
MacOS High Sierra 10.13.6 x86_64 MacOS High Sierra 10.13.6 x86_64
@@ -596,6 +597,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = false quick_cros_move = false
quick_info_user = False quick_info_user = False
@@ -794,6 +796,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = false quick_cros_move = false
quick_info_user = False quick_info_user = False
@@ -972,7 +975,7 @@ writes the credentials into the file oauth2.txt.
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
C:\GAMADV-XTD3>gam version C:\GAMADV-XTD3>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final Python 3.11.5 64-bit final
Windows-10-10.0.17134 AMD64 Windows-10-10.0.17134 AMD64
@@ -1240,6 +1243,7 @@ Section: DEFAULT
output_dateformat = '' output_dateformat = ''
output_timeformat = '' output_timeformat = ''
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = false quick_cros_move = false
quick_info_user = False quick_info_user = False

View File

@@ -78,6 +78,7 @@
<QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*" <QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*"
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*" <QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
<QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*" <QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*"
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*" <QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*" <QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*" <ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"

View File

@@ -179,6 +179,27 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable 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. `quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
### Display information about all chat spaces
```
# Local file
gam config auto_batch_min 1 redirect csv ./AllChatSpaces.csv multiprocess redirect stdout - multiprocess redirect stderr stdout all users print chatspaces
# Google sheet
gam config auto_batch_min 1 redirect csv - todrive <ToDriveAttribute>* multiprocess redirect stdout - multiprocess redirect stderr stdout all users print chatspaces
```
Add these options as desired:
```
[types <ChatSpaceTypeList>]
[formatjson [quotechar <Character>]]
```
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.
## Manage Chat Members ## Manage Chat Members
### Add members to a chat space ### Add members to a chat space
``` ```

View File

@@ -92,6 +92,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
(mappermissionsdomain <DomainName> <DomainName>)* (mappermissionsdomain <DomainName> <DomainName>)*
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[verifyorganizer [<Boolean>]]
``` ```
The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being copied. The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being copied.
The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being copied recursively, the files/folders that it contains are referred as `sub`. The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being copied recursively, the files/folders that it contains are referred as `sub`.
@@ -100,6 +101,10 @@ At its simplest, you copy files/folders by giving the copy a new name and parent
By default, files/folders in the Trash are copied; use `excludetrashed` to prevent these files/folders from being copied. By default, files/folders in the Trash are copied; use `excludetrashed` to prevent these files/folders from being copied.
When a copy operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
`verifyorganizer false` suppresses the verification.
When copying folders, you have three modes of operation: When copying folders, you have three modes of operation:
### Copy the top folder but none of its sub files/folders ### Copy the top folder but none of its sub files/folders
@@ -443,12 +448,17 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
[updatefilepermissions [<Boolean>]] [updatefilepermissions [<Boolean>]]
[retainsourcefolders [<Boolean>]] [retainsourcefolders [<Boolean>]]
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[verifyorganizer [<Boolean>]]
``` ```
The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being moved. The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being moved.
The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being moved, the files/folders that it contains are referred as `sub`. The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being moved, the files/folders that it contains are referred as `sub`.
At its simplest, you move files/folders by giving them a new name and parent location. At its simplest, you move files/folders by giving them a new name and parent location.
When a move operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
`verifyorganizer false` suppresses the verification.
When moving folders, you have two modes of operation: When moving folders, you have two modes of operation:
### Move the top folder and its sub files/folders ### Move the top folder and its sub files/folders

View File

@@ -80,6 +80,9 @@ queries "\"orgUnitPath='/Students/Middle School'\",\"orgUnitPath='/Students/Low
none|nomail none|nomail
<DomainName> ::= <String>(.<String>)+ <DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <EmailAddress> ::=
<String>@<DomainName> | <String>@<DomainName> |
<String> <<String>@<DomainName>> # The outer <> around <String>@<DomainName> are literal, e.g., IT Group<group@domain.com> <String> <<String>@<DomainName>> # The outer <> around <String>@<DomainName> are literal, e.g., IT Group<group@domain.com>
@@ -953,8 +956,8 @@ gam print users
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-users See: https://developers.google.com/admin-sdk/directory/v1/guides/search-users
``` ```
gam print users [todrive <ToDriveAttribute>*] gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted]) [limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]] [orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences] [groups|groupsincolumns] [license|licenses|licence|licences]
[schemas|custom|customschemas all|<SchemaNameList>] [schemas|custom|customschemas all|<SchemaNameList>]
@@ -966,9 +969,9 @@ gam print users [todrive <ToDriveAttribute>*]
``` ```
By default, users in all domains in the account are selected; these options allow selection of subsets of users: By default, users in all domains in the account are selected; these options allow selection of subsets of users:
* `domain <DomainName>` - Limit users to those in `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit users to those in the domains specified by `<DomainNameEntity>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query * `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query; each query is run against each domain
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>` * `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
* `deleted_only|only_deleted` - Only display deleted users * `deleted_only|only_deleted` - Only display deleted users
* `issuspended <Boolean>` - Limit users based on their status * `issuspended <Boolean>` - Limit users based on their status
@@ -1060,15 +1063,15 @@ Print a CSV file with headers `domain,count` that gives the number of users in e
### Print domain counts for users in a specific domain and/or selected by a query ### Print domain counts for users in a specific domain and/or selected by a query
``` ```
gam print users [todrive <ToDriveAttribute>*] gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted]) [limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[formatjson [quotechar <Character>]] [countonly] [formatjson [quotechar <Character>]] [countonly]
[issuspended <Boolean>] [issuspended <Boolean>]
``` ```
By default, users in all domains in the account are selected; these options allow selection of subsets of users: By default, users in all domains in the account are selected; these options allow selection of subsets of users:
* `domain <DomainName>` - Limit users to those in `<DomainName>` * `domain|domains <DomainNameEntity>` - Limit users to those in the domains specified by `<DomainNameEntity>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query * `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query; each query is run against each domain
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>` * `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
* `deleted_only|only_deleted` - Only display deleted users * `deleted_only|only_deleted` - Only display deleted users
* `issuspended <Boolean>` - Limit users based on their status * `issuspended <Boolean>` - Limit users based on their status

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details Print the current version of Gam with details
``` ```
gam version gam version
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final Python 3.11.5 64-bit final
MacOS Monterey 12.6.6 x86_64 MacOS Monterey 12.6.6 x86_64
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information Print the current version of Gam with details and time offset information
``` ```
gam version timeoffset gam version timeoffset
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final Python 3.11.5 64-bit final
MacOS Monterey 12.6.6 x86_64 MacOS Monterey 12.6.6 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 Print the current version of Gam with extended details and SSL information
``` ```
gam version extended gam version extended
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final Python 3.11.5 64-bit final
MacOS Monterey 12.6.6 x86_64 MacOS Monterey 12.6.6 x86_64
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3 Path: /Users/Admin/bin/gamadv-xtd3
Version Check: Version Check:
Current: 5.35.08 Current: 5.35.08
Latest: 6.63.13 Latest: 6.63.14
echo $? echo $?
1 1
``` ```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details Print the current version number without details
``` ```
gam version simple gam version simple
6.63.13 6.63.14
``` ```
In Linux/MacOS you can do: In Linux/MacOS you can do:
``` ```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki Print the current version of Gam and address of this Wiki
``` ```
gam help gam help
GAM 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 GAM 6.63.14 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com> Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final Python 3.11.5 64-bit final
MacOS Monterey 12.6.6 x86_64 MacOS Monterey 12.6.6 x86_64

View File

@@ -401,6 +401,13 @@ people_max_results
how many should be retrieved in each API call how many should be retrieved in each API call
Default: 100 Default: 100
Range: 1 - 1000 Range: 1 - 1000
print_agu_domains
A comma separated list of domain names that are used in these commands:
gam print aliases
gam print groups
gam print|show group-members
gam print users
Default: Blank
process_wait_limit process_wait_limit
When processing batch/CSV files, how long (in seconds) GAM should wait for all batch|csv processes to complete When processing batch/CSV files, how long (in seconds) GAM should wait for all batch|csv processes to complete
after all have been started. If the limit is reached, GAM terminates any remaining processes. after all have been started. If the limit is reached, GAM terminates any remaining processes.
@@ -633,6 +640,7 @@ Section: DEFAULT
output_dateformat = '' output_dateformat = ''
output_timeformat = '' output_timeformat = ''
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = false quick_cros_move = false
quick_info_user = false quick_info_user = false
@@ -814,6 +822,7 @@ oauth2service_json = oauth2service.json
output_dateformat = '' output_dateformat = ''
output_timeformat = '' output_timeformat = ''
people_max_results = 100 people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0 process_wait_limit = 0
quick_cros_move = False quick_cros_move = False
quick_info_user = False quick_info_user = False

View File

@@ -700,6 +700,7 @@ If an item contains spaces, it should be surrounded by ".
<QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*" <QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*"
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*" <QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
<QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*" <QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*"
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*" <QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*" <QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*" <ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
@@ -1453,7 +1454,8 @@ gam <UserTypeEntity> delete alias|aliases
gam info alias|aliases <EmailAddressEntity> gam info alias|aliases <EmailAddressEntity>
gam print alias|aliases [todrive <ToDriveAttribute>*] gam print alias|aliases [todrive <ToDriveAttribute>*]
[domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>])
[user|users <EmailAddressList>] [group|groups <EmailAddressList>] [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
[select <UserTypeEntity>] [select <UserTypeEntity>]
[aliasmatchpattern <RegularExpression>] [aliasmatchpattern <RegularExpression>]
@@ -3555,7 +3557,7 @@ gam info group|groups <GroupEntity>
[memberemaildisplaypattern|memberemailskippattern <RegularExpression>] [memberemaildisplaypattern|memberemailskippattern <RegularExpression>]
[formatjson] [formatjson]
gam print groups [todrive <ToDriveAttribute>*] gam print groups [todrive <ToDriveAttribute>*]
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)* [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -3591,7 +3593,7 @@ gam show grouptree <GroupEntity>
gam <UserTypeEntity> info member|group-members <GroupEntity> gam <UserTypeEntity> info member|group-members <GroupEntity>
gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity> gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity>
gam print group-members [todrive <ToDriveAttribute>*] gam print group-members [todrive <ToDriveAttribute>*]
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(group|group_ns|group_susp <GroupItem>)| (group|group_ns|group_susp <GroupItem>)|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -3608,7 +3610,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
[peoplelookup|(peoplelookupuser <EmailAddress>)] [peoplelookup|(peoplelookupuser <EmailAddress>)]
[formatjson [quotechar <Character>]] [formatjson [quotechar <Character>]]
gam show group-members gam show group-members
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(group|group_ns|group_susp <GroupItem>)| (group|group_ns|group_susp <GroupItem>)|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -5262,7 +5264,7 @@ Print fields for selected users; use these options to select users:
If none of these options are chosen, all users are selected. If none of these options are chosen, all users are selected.
gam print users [todrive <ToDriveAttribute>*] gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted]) [limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]] [orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [groups|groupsincolumns]
@@ -5310,12 +5312,12 @@ gam <UserTypeEntity> print
gam <UserTypeEntity> print users gam <UserTypeEntity> print users
Print user domain counts for selected users; use these options to select users: Print user domain counts for selected users; use these options to select users:
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted]) [limittoou <OrgUnitItem>] [deleted_only|only_deleted])
If none of these options are chosen, all users are selected. If none of these options are chosen, all users are selected.
gam print users [todrive <ToDriveAttribute>*] gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted]) [limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[formatjson [quotechar <Character>]] [countsonly|countonly] [formatjson [quotechar <Character>]] [countsonly|countonly]
[issuspended <Boolean>] [issuspended <Boolean>]
@@ -6015,6 +6017,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[copysheetprotectedrangesnoninheritedpermissions [<Boolean>]] [copysheetprotectedrangesnoninheritedpermissions [<Boolean>]]
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[suppressnotselectedmessages [<Boolean>]] [suppressnotselectedmessages [<Boolean>]]
[verifyorganizer [<Boolean>]]
gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>] gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>]
[summary [<Boolean>]] [showpermissionmessages [<Boolean>]] [summary [<Boolean>]] [showpermissionmessages [<Boolean>]]
@@ -6037,6 +6040,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
[updatefilepermissions [<Boolean>]] [updatefilepermissions [<Boolean>]]
[retainsourcefolders [<Boolean>]] [retainsourcefolders [<Boolean>]]
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[verifyorganizer [<Boolean>]]
gam <UserTypeEntity> get document <DriveFileEntity> gam <UserTypeEntity> get document <DriveFileEntity>
[viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions] [viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions]

View File

@@ -2,6 +2,37 @@
Merged GAM-Team version Merged GAM-Team version
6.63.14
Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`. When a copy/move
operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
`verifyorganizer false` suppresses the verification.
Updated the following commands to be able to specify a list of domains rather than a single domain:
```
gam print alias|aliases
gam print groups
gam print|show group-members
gam print users
```
Added `print_agu_domains` variable to `gam.cfg` that provides a default list of domains for these commands.
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
```
$ gam print users domains school.org,students.school.org queries "'email:admin*','email:test*'"
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 3 Users: admin@school.org - admindirector@school.org
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 20 Users: testusera@school.org - testuserx@school.org
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
Got 1 User: admin@students.school.org - admin@students.school.org
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
primaryEmail
...
```
6.63.13 6.63.13
Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename` Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename`

View File

@@ -2375,12 +2375,9 @@ def emptyQuery(query, entityType):
def invalidQuery(query): def invalidQuery(query):
return f'{Ent.Singular(Ent.QUERY)} ({query}) {Msg.INVALID}' return f'{Ent.Singular(Ent.QUERY)} ({query}) {Msg.INVALID}'
def invalidMember(kwargs): def invalidMember(query):
if 'userKey' in kwargs: if query:
badRequestWarning(Ent.GROUP, Ent.MEMBER, kwargs['userKey']) badRequestWarning(Ent.GROUP, Ent.QUERY, invalidQuery(query))
return True
if 'query' in kwargs:
badRequestWarning(Ent.GROUP, Ent.QUERY, invalidQuery(kwargs['query']))
return True return True
return False return False
@@ -4121,6 +4118,8 @@ def SetGlobalVariables():
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE]
if not GC.Values[GC.CSV_OUTPUT_ROW_LIMIT]: if not GC.Values[GC.CSV_OUTPUT_ROW_LIMIT]:
GC.Values[GC.CSV_OUTPUT_ROW_LIMIT] = GM.Globals[GM.CSV_OUTPUT_ROW_LIMIT] GC.Values[GC.CSV_OUTPUT_ROW_LIMIT] = GM.Globals[GM.CSV_OUTPUT_ROW_LIMIT]
if not GC.Values[GC.PRINT_AGU_DOMAINS]:
GC.Values[GC.PRINT_AGU_DOMAINS] = GM.Globals[GM.PRINT_AGU_DOMAINS]
# customer_id, domain and admin_email must be set when enable_dasa = true # customer_id, domain and admin_email must be set when enable_dasa = true
if GC.Values[GC.ENABLE_DASA]: if GC.Values[GC.ENABLE_DASA]:
errors = 0 errors = 0
@@ -9300,7 +9299,7 @@ def terminateStdQueueHandler(mpQueue, mpQueueHandler):
mpQueueHandler.join() mpQueueHandler.join()
def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr, def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr,
debugLevel, todrive, debugLevel, todrive, printAguDomains,
output_dateformat, output_timeformat, output_dateformat, output_timeformat,
csvColumnDelimiter, csvQuoteChar, csvColumnDelimiter, csvQuoteChar,
csvTimestampColumn, csvTimestampColumn,
@@ -9339,6 +9338,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
GM.Globals[GM.OUTPUT_TIMEFORMAT] = output_timeformat GM.Globals[GM.OUTPUT_TIMEFORMAT] = output_timeformat
GM.Globals[GM.NUM_BATCH_ITEMS] = numItems GM.Globals[GM.NUM_BATCH_ITEMS] = numItems
GM.Globals[GM.PID] = pid GM.Globals[GM.PID] = pid
GM.Globals[GM.PRINT_AGU_DOMAINS] = printAguDomains
GM.Globals[GM.SAVED_STDOUT] = None GM.Globals[GM.SAVED_STDOUT] = None
GM.Globals[GM.SYSEXITRC] = 0 GM.Globals[GM.SYSEXITRC] = 0
if mpQueueCSVFile: if mpQueueCSVFile:
@@ -9508,6 +9508,7 @@ def MultiprocessGAMCommands(items, showCmds):
poolProcessResults[pid] = pool.apply_async(ProcessGAMCommandMulti, poolProcessResults[pid] = pool.apply_async(ProcessGAMCommandMulti,
[pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr, [pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr,
GC.Values[GC.DEBUG_LEVEL], GM.Globals[GM.CSV_TODRIVE], GC.Values[GC.DEBUG_LEVEL], GM.Globals[GM.CSV_TODRIVE],
GC.Values[GC.PRINT_AGU_DOMAINS],
GC.Values[GC.OUTPUT_DATEFORMAT], GC.Values[GC.OUTPUT_TIMEFORMAT], GC.Values[GC.OUTPUT_DATEFORMAT], GC.Values[GC.OUTPUT_TIMEFORMAT],
GC.Values[GC.CSV_OUTPUT_COLUMN_DELIMITER], GC.Values[GC.CSV_OUTPUT_COLUMN_DELIMITER],
GC.Values[GC.CSV_OUTPUT_QUOTE_CHAR], GC.Values[GC.CSV_OUTPUT_QUOTE_CHAR],
@@ -17452,11 +17453,58 @@ def infoAliases(entityList):
def doInfoAliases(): def doInfoAliases():
infoAliases(getEntityList(Cmd.OB_EMAIL_ADDRESS_ENTITY)) infoAliases(getEntityList(Cmd.OB_EMAIL_ADDRESS_ENTITY))
def initUserGroupDomainQueryFilters():
if not GC.Values[GC.PRINT_AGU_DOMAINS]:
return {'list': [{'customer': GC.Values[GC.CUSTOMER_ID]}], 'queries': [None]}
return {'list': [{'domain': domain.lower()} for domain in GC.Values[GC.PRINT_AGU_DOMAINS].replace(',', ' ').split()], 'queries': [None]}
def getUserGroupDomainQueryFilters(myarg, kwargsDict):
if myarg in {'domain', 'domains'}:
kwargsDict['list'] = [{'domain': domain.lower()} for domain in getEntityList(Cmd.OB_DOMAIN_NAME_ENTITY)]
elif myarg in {'query', 'queries'}:
kwargsDict['queries'] = getQueries(myarg)
else:
return False
return True
def makeUserGroupDomainQueryFilters(kwargsDict):
kwargsQueries = []
for kwargs in kwargsDict['list']:
for query in kwargsDict['queries']:
kwargsQueries.append((kwargs, query))
return kwargsQueries
def userFilters(kwargs, query, orgUnitPath, isSuspended):
queryTitle = ''
if kwargs.get('domain'):
queryTitle += f'domain={kwargs["domain"]}, '
if orgUnitPath is not None:
if query is not None and query.find(orgUnitPath) == -1:
query += f" orgUnitPath='{orgUnitPath}'"
else:
if query is None:
query = ''
else:
query += ' '
query += f"orgUnitPath='{orgUnitPath}'"
if isSuspended is not None:
if query is None:
query = ''
else:
query += ' '
query += f'isSuspended={isSuspended}'
if query is not None:
queryTitle += f'query="{query}", '
if queryTitle:
return query, queryTitle[:-2]
return query, queryTitle
# gam print aliases|nicknames [todrive <ToDriveAttribute>*] # gam print aliases|nicknames [todrive <ToDriveAttribute>*]
# [domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] # ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
# [limittoou <OrgUnitItem>])
# [user|users <EmailAddressList>] [group|groups <EmailAddressList>] # [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
# [select <UserTypeEntity>] # [select <UserTypeEntity>]
# [aliasmatchpattern <RegularExpression>] # [issuspended <Boolean>] [aliasmatchpattern <RegularExpression>]
# [shownoneditable] [nogroups] [nousers] # [shownoneditable] [nogroups] [nousers]
# [onerowpertarget] [delimiter <Character>] # [onerowpertarget] [delimiter <Character>]
# [suppressnoaliasrows] # [suppressnoaliasrows]
@@ -17497,12 +17545,12 @@ def doPrintAliases():
userFields = ['primaryEmail', 'aliases'] userFields = ['primaryEmail', 'aliases']
groupFields = ['email', 'aliases'] groupFields = ['email', 'aliases']
oneRowPerTarget = showNonEditable = suppressNoAliasRows = False oneRowPerTarget = showNonEditable = suppressNoAliasRows = False
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]} kwargsDict = initUserGroupDomainQueryFilters()
queries = [None]
getGroups = getUsers = True getGroups = getUsers = True
groups = [] groups = []
users = [] users = []
aliasMatchPattern = re.compile(r'^.*$') aliasMatchPattern = re.compile(r'^.*$')
isSuspended = orgUnitPath = None
addCSVData = {} addCSVData = {}
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
@@ -17517,15 +17565,17 @@ def doPrintAliases():
getGroups = False getGroups = False
elif myarg == 'nousers': elif myarg == 'nousers':
getUsers = False getUsers = False
elif myarg == 'domain': elif myarg == 'limittoou':
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower() orgUnitPath = getOrgUnitItem(pathOnly=True, cd=cd)
kwargs.pop('customer', None) orgUnitPathLower = orgUnitPath.lower()
elif myarg in {'query', 'queries'}: userFields.append('orgUnitPath')
queries = getQueries(myarg)
getGroups = False getGroups = False
getUsers = True elif getUserGroupDomainQueryFilters(myarg, kwargsDict):
pass
elif myarg == 'select': elif myarg == 'select':
_, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS) _, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
elif myarg == 'issuspended':
isSuspended = getBoolean()
elif myarg in {'user','users'}: elif myarg in {'user','users'}:
users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0))) users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0)))
elif myarg in {'group', 'groups'}: elif myarg in {'group', 'groups'}:
@@ -17543,7 +17593,7 @@ def doPrintAliases():
delimiter = getCharacter() delimiter = getCharacter()
else: else:
unknownArgumentExit() unknownArgumentExit()
if users or groups and queries[0] is None: if (users or groups) and kwargsDict['queries'][0] is None:
getUsers = getGroups = False getUsers = getGroups = False
if not oneRowPerTarget: if not oneRowPerTarget:
titlesList = ['Alias', 'Target', 'TargetType'] titlesList = ['Alias', 'Target', 'TargetType']
@@ -17557,8 +17607,11 @@ def doPrintAliases():
if addCSVData: if addCSVData:
csvPF.AddTitles(sorted(addCSVData.keys())) csvPF.AddTitles(sorted(addCSVData.keys()))
if getUsers: if getUsers:
for query in queries: for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
printGettingAllAccountEntities(Ent.USER, query) kwargs = kwargsQuery[0]
query = kwargsQuery[1]
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
printGettingAllAccountEntities(Ent.USER, pquery)
try: try:
entityList = callGAPIpages(cd.users(), 'list', 'users', entityList = callGAPIpages(cd.users(), 'list', 'users',
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='primaryEmail', pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='primaryEmail',
@@ -17568,13 +17621,14 @@ def doPrintAliases():
fields=f'nextPageToken,users({",".join(userFields)})', fields=f'nextPageToken,users({",".join(userFields)})',
maxResults=GC.Values[GC.USER_MAX_RESULTS], **kwargs) maxResults=GC.Values[GC.USER_MAX_RESULTS], **kwargs)
for user in entityList: for user in entityList:
if orgUnitPath is None or orgUnitPathLower == user.get('orgUnitPath', '').lower():
writeAliases(user, user['primaryEmail'], 'User') writeAliases(user, user['primaryEmail'], 'User')
except (GAPI.invalidOrgunit, GAPI.invalidInput): except (GAPI.invalidOrgunit, GAPI.invalidInput):
entityActionFailedWarning([Ent.ALIAS, None], invalidQuery(query)) entityActionFailedWarning([Ent.ALIAS, None], invalidQuery(query))
return continue
except GAPI.domainNotFound as e : except GAPI.domainNotFound as e :
entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e)) entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e))
return continue
except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest): except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest):
accessErrorExit(cd) accessErrorExit(cd)
count = len(users) count = len(users)
@@ -17593,17 +17647,22 @@ def doPrintAliases():
except (GAPI.userNotFound, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.invalidResource, GAPI.conditionNotMet) as e: except (GAPI.userNotFound, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.invalidResource, GAPI.conditionNotMet) as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count) entityActionFailedWarning([Ent.USER, user], str(e), i, count)
if getGroups: if getGroups:
printGettingAllAccountEntities(Ent.GROUP) for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
kwargs = kwargsQuery[0]
query = kwargsQuery[1]
query, pquery = groupFilters(kwargs, query)
printGettingAllAccountEntities(Ent.GROUP, pquery)
try: try:
entityList = callGAPIpages(cd.groups(), 'list', 'groups', entityList = callGAPIpages(cd.groups(), 'list', 'groups',
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email', pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
throwReasons=GAPI.GROUP_LIST_THROW_REASONS, throwReasons=GAPI.GROUP_LIST_THROW_REASONS,
orderBy='email', fields=f'nextPageToken,groups({",".join(groupFields)})', **kwargs) query=query, orderBy='email',
fields=f'nextPageToken,groups({",".join(groupFields)})', **kwargs)
for group in entityList: for group in entityList:
writeAliases(group, group['email'], 'Group') writeAliases(group, group['email'], 'Group')
except GAPI.domainNotFound as e : except GAPI.domainNotFound as e :
entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e)) entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e))
return continue
except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest): except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest):
accessErrorExit(cd) accessErrorExit(cd)
count = len(groups) count = len(groups)
@@ -30442,40 +30501,32 @@ def infoGroups(entityList):
def doInfoGroups(): def doInfoGroups():
infoGroups(getEntityList(Cmd.OB_GROUP_ENTITY)) infoGroups(getEntityList(Cmd.OB_GROUP_ENTITY))
def groupFilters(kwargs): def groupFilters(kwargs, query):
queryTitle = '' queryTitle = ''
if kwargs.get('domain'): if kwargs.get('domain'):
queryTitle += f'{Ent.Singular(Ent.DOMAIN)}={kwargs["domain"]}, ' queryTitle += f'domain={kwargs["domain"]}, '
if kwargs.get('userKey'): if query is not None:
queryTitle += f'{Ent.Singular(Ent.MEMBER)}={kwargs["userKey"]}, ' queryTitle += f'query="{query}", '
if kwargs.get('query'):
queryTitle += f'query="{kwargs["query"]}", '
if queryTitle: if queryTitle:
return queryTitle[:-2] return query, queryTitle[:-2]
return queryTitle return query, queryTitle
def getGroupFilters(myarg, kwargs, showOwnedBy): def getGroupFilters(myarg, kwargsDict, showOwnedBy):
if myarg == 'domain': if getUserGroupDomainQueryFilters(myarg, kwargsDict):
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower() pass
kwargs.pop('customer', None)
elif myarg in {'member', 'showownedby'}: elif myarg in {'member', 'showownedby'}:
emailAddressOrUID = getEmailAddress() emailAddressOrUID = getEmailAddress()
if emailAddressOrUID != GC.Values[GC.CUSTOMER_ID].lower(): if emailAddressOrUID != GC.Values[GC.CUSTOMER_ID].lower():
kwargs['userKey'] = emailAddressOrUID kwargsDict['queries'] = [f'memberKey={emailAddressOrUID}']
kwargs.pop('customer', None)
key = 'email' if emailAddressOrUID.find('@') != -1 else 'id' key = 'email' if emailAddressOrUID.find('@') != -1 else 'id'
else: else:
kwargs['query'] = f'memberKey={GC.Values[GC.CUSTOMER_ID]}' kwargsDict['queries'] = [f'memberKey={GC.Values[GC.CUSTOMER_ID]}']
key = 'id' key = 'id'
if myarg == 'showownedby': if myarg == 'showownedby':
showOwnedBy['key'] = key showOwnedBy['key'] = key
showOwnedBy['value'] = emailAddressOrUID showOwnedBy['value'] = emailAddressOrUID
elif myarg == 'query':
kwargs['query'] = getString(Cmd.OB_QUERY)
else: else:
return False return False
if kwargs.get('userKey') and kwargs.get('query'):
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('member', 'query'))
return True return True
def checkGroupShowOwnedBy(showOwnedBy, members): def checkGroupShowOwnedBy(showOwnedBy, members):
@@ -30669,7 +30720,7 @@ def addMemberInfoToRow(row, groupMembers, typesSet, memberOptions, memberDisplay
PRINT_GROUPS_JSON_TITLES = ['email', 'JSON'] PRINT_GROUPS_JSON_TITLES = ['email', 'JSON']
# gam print groups [todrive <ToDriveAttribute>*] # gam print groups [todrive <ToDriveAttribute>*]
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| # [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
# (select <GroupEntity>)] # (select <GroupEntity>)]
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] # [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
# [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)* # [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -30849,7 +30900,7 @@ def doPrintGroups():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
ci = None ci = None
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]} kwargsDict = initUserGroupDomainQueryFilters()
convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL] convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL]
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER] delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
getCloudIdentity = getSettings = showCIgroupKey = sortHeaders = False getCloudIdentity = getSettings = showCIgroupKey = sortHeaders = False
@@ -30871,7 +30922,7 @@ def doPrintGroups():
myarg = getArgument() myarg = getArgument()
if myarg == 'todrive': if myarg == 'todrive':
csvPF.GetTodriveParameters() csvPF.GetTodriveParameters()
elif getGroupFilters(myarg, kwargs, showOwnedBy): elif getGroupFilters(myarg, kwargsDict, showOwnedBy):
pass pass
elif getGroupMatchPatterns(myarg, matchPatterns, False): elif getGroupMatchPatterns(myarg, matchPatterns, False):
pass pass
@@ -30992,20 +31043,23 @@ def doPrintGroups():
if getCloudIdentity: if getCloudIdentity:
csvPF.AddJSONTitle('JSON-cloudIdentity') csvPF.AddJSONTitle('JSON-cloudIdentity')
if entitySelection is None: if entitySelection is None:
printGettingAllAccountEntities(Ent.GROUP, groupFilters(kwargs)) entityList = []
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
kwargs = kwargsQuery[0]
query = kwargsQuery[1]
query, pquery = groupFilters(kwargs, query)
printGettingAllAccountEntities(Ent.GROUP, pquery)
try: try:
entityList = callGAPIpages(cd.groups(), 'list', 'groups', entityList.extend(callGAPIpages(cd.groups(), 'list', 'groups',
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email', pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS, throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
orderBy='email', fields=cdfieldsnp, maxResults=maxResults, **kwargs) orderBy='email', query=query, fields=cdfieldsnp, maxResults=maxResults, **kwargs))
except (GAPI.invalidMember, GAPI.invalidInput) as e: except (GAPI.invalidMember, GAPI.invalidInput) as e:
if not invalidMember(kwargs): if not invalidMember(query):
entityActionFailedExit([Ent.GROUP, None], str(e)) entityActionFailedExit([Ent.GROUP, None], str(e))
entityList = []
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest): except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
if kwargs.get('domain'): if kwargs.get('domain'):
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain']) badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
entityList = []
else: else:
accessErrorExit(cd) accessErrorExit(cd)
if getCloudIdentity: if getCloudIdentity:
@@ -31219,24 +31273,26 @@ def infoGroupMembers(entityList, ciGroupsAPI=False):
def doInfoGroupMembers(): def doInfoGroupMembers():
infoGroupMembers(getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)[1], False) infoGroupMembers(getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)[1], False)
def getGroupMembersEntityList(cd, entityList, matchPatterns, fieldsList, kwargs): def getGroupMembersEntityList(cd, entityList, matchPatterns, fieldsList, kwargsDict):
if entityList is None: if entityList is None:
updateFieldsForGroupMatchPatterns(matchPatterns, fieldsList) updateFieldsForGroupMatchPatterns(matchPatterns, fieldsList)
subTitle = groupFilters(kwargs) entityList = []
printGettingAllAccountEntities(Ent.GROUP, subTitle) for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
kwargs = kwargsQuery[0]
query = kwargsQuery[1]
query, pquery = groupFilters(kwargs, query)
printGettingAllAccountEntities(Ent.GROUP, pquery)
try: try:
entityList = callGAPIpages(cd.groups(), 'list', 'groups', entityList.extend(callGAPIpages(cd.groups(), 'list', 'groups',
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email', pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS, throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
orderBy='email', fields=f'nextPageToken,groups({",".join(set(fieldsList))})', **kwargs) orderBy='email', query=query, fields=f'nextPageToken,groups({",".join(set(fieldsList))})', **kwargs))
except (GAPI.invalidMember, GAPI.invalidInput) as e: except (GAPI.invalidMember, GAPI.invalidInput) as e:
if not invalidMember(kwargs): if not invalidMember(query):
entityActionFailedExit([Ent.GROUP, None], str(e)) entityActionFailedExit([Ent.GROUP, None], str(e))
entityList = []
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest): except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
if kwargs.get('domain'): if kwargs.get('domain'):
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain']) badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
entityList = []
else: else:
accessErrorExit(cd) accessErrorExit(cd)
else: else:
@@ -31347,7 +31403,7 @@ GROUPMEMBERS_FIELDS_CHOICE_MAP = {
GROUPMEMBERS_DEFAULT_FIELDS = ['group', 'type', 'role', 'id', 'status', 'email'] GROUPMEMBERS_DEFAULT_FIELDS = ['group', 'type', 'role', 'id', 'status', 'email']
# gam print group-members [todrive <ToDriveAttribute>*] # gam print group-members [todrive <ToDriveAttribute>*]
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| # [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
# (group|group_ns|group_susp <GroupItem>)| # (group|group_ns|group_susp <GroupItem>)|
# (select <GroupEntity>)] # (select <GroupEntity>)]
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] # [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -31384,7 +31440,8 @@ def doPrintGroupMembers():
peopleNames = {} peopleNames = {}
memberOptions = initMemberOptions() memberOptions = initMemberOptions()
groupColumn = True groupColumn = True
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]} customerKey = GC.Values[GC.CUSTOMER_ID]
kwargsDict = initUserGroupDomainQueryFilters()
subTitle = f'{Msg.ALL} {Ent.Plural(Ent.GROUP)}' subTitle = f'{Msg.ALL} {Ent.Plural(Ent.GROUP)}'
fieldsList = [] fieldsList = []
csvPF = CSVPrintFile('group') csvPF = CSVPrintFile('group')
@@ -31401,7 +31458,7 @@ def doPrintGroupMembers():
myarg = getArgument() myarg = getArgument()
if myarg == 'todrive': if myarg == 'todrive':
csvPF.GetTodriveParameters() csvPF.GetTodriveParameters()
elif getGroupFilters(myarg, kwargs, showOwnedBy): elif getGroupFilters(myarg, kwargsDict, showOwnedBy):
pass pass
elif getGroupMatchPatterns(myarg, matchPatterns, False): elif getGroupMatchPatterns(myarg, matchPatterns, False):
pass pass
@@ -31457,7 +31514,7 @@ def doPrintGroupMembers():
FJQC.GetFormatJSONQuoteChar(myarg, False) FJQC.GetFormatJSONQuoteChar(myarg, False)
if not typesSet: if not typesSet:
typesSet = {Ent.TYPE_USER} if memberOptions[MEMBEROPTION_RECURSIVE] else ALL_GROUP_TYPES typesSet = {Ent.TYPE_USER} if memberOptions[MEMBEROPTION_RECURSIVE] else ALL_GROUP_TYPES
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargs) entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargsDict)
if not fieldsList: if not fieldsList:
for field in GROUPMEMBERS_DEFAULT_FIELDS: for field in GROUPMEMBERS_DEFAULT_FIELDS:
csvPF.AddField(field, {field: field}, fieldsList) csvPF.AddField(field, {field: field}, fieldsList)
@@ -31493,7 +31550,6 @@ def doPrintGroupMembers():
getRolesSet.add(Ent.ROLE_OWNER) getRolesSet.add(Ent.ROLE_OWNER)
getRoles = ','.join(sorted(getRolesSet)) getRoles = ','.join(sorted(getRolesSet))
level = 0 level = 0
customerKey = GC.Values[GC.CUSTOMER_ID]
setCustomerMemberEmail = 'email' in fieldsList setCustomerMemberEmail = 'email' in fieldsList
i = 0 i = 0
count = len(entityList) count = len(entityList)
@@ -31595,7 +31651,7 @@ def doPrintGroupMembers():
csvPF.writeCSVfile(f'Group Members ({subTitle})') csvPF.writeCSVfile(f'Group Members ({subTitle})')
# gam show group-members # gam show group-members
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))| # [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
# (group|group_ns|group_susp <GroupItem>)| # (group|group_ns|group_susp <GroupItem>)|
# (select <GroupEntity>)] # (select <GroupEntity>)]
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] # [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -31642,8 +31698,7 @@ def doShowGroupMembers():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
ci = None ci = None
customerKey = GC.Values[GC.CUSTOMER_ID] kwargsDict = initUserGroupDomainQueryFilters()
kwargs = {'customer': customerKey}
entityList = None entityList = None
showOwnedBy = {} showOwnedBy = {}
cdfieldsList = ['email'] cdfieldsList = ['email']
@@ -31655,7 +31710,7 @@ def doShowGroupMembers():
includeDerivedMembership = False includeDerivedMembership = False
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if getGroupFilters(myarg, kwargs, showOwnedBy): if getGroupFilters(myarg, kwargsDict, showOwnedBy):
pass pass
elif getGroupMatchPatterns(myarg, matchPatterns, False): elif getGroupMatchPatterns(myarg, matchPatterns, False):
pass pass
@@ -31687,7 +31742,7 @@ def doShowGroupMembers():
rolesSet = ALL_GROUP_ROLES rolesSet = ALL_GROUP_ROLES
if not typesSet: if not typesSet:
typesSet = ALL_GROUP_TYPES typesSet = ALL_GROUP_TYPES
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargs) entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargsDict)
i = 0 i = 0
count = len(entityList) count = len(entityList)
for group in entityList: for group in entityList:
@@ -41662,7 +41717,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
'phones', 'posixAccounts', 'relations', 'sshPublicKeys', 'websites'] 'phones', 'posixAccounts', 'relations', 'sshPublicKeys', 'websites']
# gam print users [todrive <ToDriveAttribute>*] # gam print users [todrive <ToDriveAttribute>*]
# ([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)] # ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
# [limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>] # [limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>]
# [groups|groupsincolumns] # [groups|groupsincolumns]
# [license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser] # [license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
@@ -41804,8 +41859,7 @@ def doPrintUsers(entityList=None):
'sortHeaders': False, 'sortHeaders': False,
'maxGroups': 0 'maxGroups': 0
} }
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]} kwargsDict = initUserGroupDomainQueryFilters()
queries = [None]
licenses = {} licenses = {}
lic = None lic = None
skus = None skus = None
@@ -41824,11 +41878,8 @@ def doPrintUsers(entityList=None):
elif entityList is None and myarg == 'limittoou': elif entityList is None and myarg == 'limittoou':
orgUnitPath = getOrgUnitItem(pathOnly=True, cd=cd) orgUnitPath = getOrgUnitItem(pathOnly=True, cd=cd)
orgUnitPathLower = orgUnitPath.lower() orgUnitPathLower = orgUnitPath.lower()
elif myarg == 'domain': elif entityList is None and getUserGroupDomainQueryFilters(myarg, kwargsDict):
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower() pass
kwargs.pop('customer', None)
elif entityList is None and myarg in {'query', 'queries'}:
queries = getQueries(myarg)
elif entityList is None and myarg in {'deletedonly', 'onlydeleted'}: elif entityList is None and myarg in {'deletedonly', 'onlydeleted'}:
showDeleted = True showDeleted = True
elif entityList is None and myarg == 'select': elif entityList is None and myarg == 'select':
@@ -41927,23 +41978,11 @@ def doPrintUsers(entityList=None):
if orgUnitPath is not None and fieldsList: if orgUnitPath is not None and fieldsList:
fieldsList.append('orgUnitPath') fieldsList.append('orgUnitPath')
fields = getItemFieldsFromFieldsList('users', fieldsList) fields = getItemFieldsFromFieldsList('users', fieldsList)
for query in queries: for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
if orgUnitPath is not None: kwargs = kwargsQuery[0]
if query is not None and query.find(orgUnitPath) == -1: query = kwargsQuery[1]
query += f" orgUnitPath='{orgUnitPath}'" query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
else: printGettingAllAccountEntities(Ent.USER, pquery)
if query is None:
query = ''
else:
query += ' '
query += f"orgUnitPath='{orgUnitPath}'"
if isSuspended is not None:
if query is None:
query = ''
else:
query += ' '
query += f'isSuspended={isSuspended}'
printGettingAllAccountEntities(Ent.USER, query)
pageMessage = getPageMessage(showFirstLastItems=True) pageMessage = getPageMessage(showFirstLastItems=True)
try: try:
feed = yieldGAPIpages(cd.users(), 'list', 'users', feed = yieldGAPIpages(cd.users(), 'list', 'users',
@@ -41972,7 +42011,7 @@ def doPrintUsers(entityList=None):
_updateDomainCounts(user['primaryEmail']) _updateDomainCounts(user['primaryEmail'])
except GAPI.domainNotFound: except GAPI.domainNotFound:
entityActionFailedWarning([Ent.USER, None, Ent.DOMAIN, kwargs['domain']], Msg.NOT_FOUND) entityActionFailedWarning([Ent.USER, None, Ent.DOMAIN, kwargs['domain']], Msg.NOT_FOUND)
return continue
except (GAPI.invalidOrgunit, GAPI.invalidInput) as e: except (GAPI.invalidOrgunit, GAPI.invalidInput) as e:
if query and not customFieldMask: if query and not customFieldMask:
entityActionFailedWarning([Ent.USER, None], invalidQuery(query)) entityActionFailedWarning([Ent.USER, None], invalidQuery(query))
@@ -41982,7 +42021,7 @@ def doPrintUsers(entityList=None):
entityActionFailedWarning([Ent.USER, None], f'{invalidQuery(query)} or {invalidUserSchema(customFieldMask)}') entityActionFailedWarning([Ent.USER, None], f'{invalidQuery(query)} or {invalidUserSchema(customFieldMask)}')
else: else:
entityActionFailedWarning([Ent.USER, None], str(e)) entityActionFailedWarning([Ent.USER, None], str(e))
return continue
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
accessErrorExit(cd) accessErrorExit(cd)
else: else:
@@ -50235,8 +50274,6 @@ class DriveFileFields():
self.fieldsList = [] self.fieldsList = []
self.includeLabels = [] self.includeLabels = []
self.parentsSubFields = {'id': False, 'isRoot': False, 'rootFolderId': None} self.parentsSubFields = {'id': False, 'isRoot': False, 'rootFolderId': None}
self.sharedDriveNames = {}
self.drive = None
def SetAllParentsSubFields(self): def SetAllParentsSubFields(self):
self.parentsSubFields['id'] = self.parentsSubFields['isRoot'] = True self.parentsSubFields['id'] = self.parentsSubFields['isRoot'] = True
@@ -50286,16 +50323,6 @@ class DriveFileFields():
def orderBy(self): def orderBy(self):
return self.OBY.orderBy return self.OBY.orderBy
def SharedDriveName(self, drive, driveId):
if driveId not in self.sharedDriveNames:
try:
self.sharedDriveNames[driveId] = callGAPI(drive.drives(), 'get',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
useDomainAdminAccess=False, driveId=driveId, fields='name')['name']
except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
self.sharedDriveNames[driveId] = TEAM_DRIVE
return self.sharedDriveNames[driveId]
def _setSkipObjects(skipObjects, skipTitles, fieldsList): def _setSkipObjects(skipObjects, skipTitles, fieldsList):
for field in skipTitles: for field in skipTitles:
if field != 'parents': if field != 'parents':
@@ -50453,9 +50480,9 @@ def showFileInfo(users):
driveId = result.get('driveId') driveId = result.get('driveId')
if driveId: if driveId:
if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE: if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE:
result['name'] = DFF.SharedDriveName(drive, driveId) result['name'] = _getSharedDriveNameFromId(drive, driveId)
if DFF.showSharedDriveNames: if DFF.showSharedDriveNames:
result['driveName'] = DFF.SharedDriveName(drive, driveId) result['driveName'] = _getSharedDriveNameFromId(drive, driveId)
if showNoParents: if showNoParents:
result.setdefault('parents', []) result.setdefault('parents', [])
if getPermissionsForSharedDrives and driveId and 'permissions' not in result: if getPermissionsForSharedDrives and driveId and 'permissions' not in result:
@@ -51791,7 +51818,7 @@ def printFileList(users):
if not pmselect and 'permissions' in fileInfo: if not pmselect and 'permissions' in fileInfo:
fileInfo['permissions'] = DLP.GetFileMatchingPermission(fileInfo) fileInfo['permissions'] = DLP.GetFileMatchingPermission(fileInfo)
if DFF.showSharedDriveNames and driveId: if DFF.showSharedDriveNames and driveId:
fileInfo['driveName'] = DFF.SharedDriveName(drive, driveId) fileInfo['driveName'] = _getSharedDriveNameFromId(drive, driveId)
if filepath: if filepath:
if not FJQC.formatJSON or not addPathsToJSON: if not FJQC.formatJSON or not addPathsToJSON:
addFilePathsToRow(drive, fileTree, fileInfo, filePathInfo, csvPF, row, fullpath=fullpath, showDepth=showDepth) addFilePathsToRow(drive, fileTree, fileInfo, filePathInfo, csvPF, row, fullpath=fullpath, showDepth=showDepth)
@@ -54904,6 +54931,7 @@ copyReturnItemMap = {
# [copysheetprotectedrangesnoninheritedpermissions [<Boolean>]] # [copysheetprotectedrangesnoninheritedpermissions [<Boolean>]]
# [sendemailifrequired [<Boolean>]] # [sendemailifrequired [<Boolean>]]
# [suppressnotselectedmessages [<Boolean>]] # [suppressnotselectedmessages [<Boolean>]]
# [verifyorganizer [<Boolean>]]
def copyDriveFile(users): def copyDriveFile(users):
def _writeCSVData(user, oldName, oldId, newName, newId, mimeType): def _writeCSVData(user, oldName, oldId, newName, newId, mimeType):
row = {'User': user, fileNameTitle: oldName, 'id': oldId, row = {'User': user, fileNameTitle: oldName, 'id': oldId,
@@ -55240,6 +55268,7 @@ def copyDriveFile(users):
copyMoveOptions = initCopyMoveOptions(True) copyMoveOptions = initCopyMoveOptions(True)
excludeTrashed = newParentsSpecified = recursive = suppressNotSelectedMessages = False excludeTrashed = newParentsSpecified = recursive = suppressNotSelectedMessages = False
maxdepth = -1 maxdepth = -1
verifyOrganizer = True
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if getCopyMoveOptions(myarg, copyMoveOptions): if getCopyMoveOptions(myarg, copyMoveOptions):
@@ -55267,6 +55296,8 @@ def copyDriveFile(users):
suppressNotSelectedMessages = getBoolean() suppressNotSelectedMessages = getBoolean()
elif getDriveFileCopyAttribute(myarg, copyBody, copyParameters): elif getDriveFileCopyAttribute(myarg, copyBody, copyParameters):
pass pass
elif myarg == 'verifyorganizer':
verifyOrganizer = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
if csvPF: if csvPF:
@@ -55319,7 +55350,7 @@ def copyDriveFile(users):
continue continue
if copyMoveOptions['sourceDriveId']: if copyMoveOptions['sourceDriveId']:
# If copying from a Shared Drive, user has to be an organizer # If copying from a Shared Drive, user has to be an organizer
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']): if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER) _incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
continue continue
sourceSearchArgs = {'driveId': copyMoveOptions['sourceDriveId'], 'corpora': 'drive', 'includeItemsFromAllDrives': True, 'supportsAllDrives': True} sourceSearchArgs = {'driveId': copyMoveOptions['sourceDriveId'], 'corpora': 'drive', 'includeItemsFromAllDrives': True, 'supportsAllDrives': True}
@@ -55347,7 +55378,7 @@ def copyDriveFile(users):
copyMoveOptions['destParentType'] = dest['destParentType'] copyMoveOptions['destParentType'] = dest['destParentType']
if copyMoveOptions['destDriveId']: if copyMoveOptions['destDriveId']:
# If copying to a Shared Drive, user has to be an organizer # If copying to a Shared Drive, user has to be an organizer
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']): if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER) _incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
continue continue
if not parentParms[DFA_SEARCHARGS]: if not parentParms[DFA_SEARCHARGS]:
@@ -55680,6 +55711,7 @@ def _updateMoveFilePermissions(drive, user, i, count,
# [updatefilepermissions [<Boolean>]] # [updatefilepermissions [<Boolean>]]
# [retainsourcefolders [<Boolean>]] # [retainsourcefolders [<Boolean>]]
# [sendemailifrequired [<Boolean>]] # [sendemailifrequired [<Boolean>]]
# [verifyorganizer [<Boolean>]]
def moveDriveFile(users): def moveDriveFile(users):
def _cloneFolderMove(drive, user, i, count, j, jcount, def _cloneFolderMove(drive, user, i, count, j, jcount,
source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime, source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime,
@@ -56014,6 +56046,7 @@ def moveDriveFile(users):
newParentsSpecified = False newParentsSpecified = False
movedFiles = {} movedFiles = {}
updateFilePermissions = False updateFilePermissions = False
verifyOrganizer = True
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if getCopyMoveOptions(myarg, copyMoveOptions): if getCopyMoveOptions(myarg, copyMoveOptions):
@@ -56022,6 +56055,8 @@ def moveDriveFile(users):
newParentsSpecified = True newParentsSpecified = True
elif myarg == 'updatefilepermissions': elif myarg == 'updatefilepermissions':
updateFilePermissions = getBoolean() updateFilePermissions = getBoolean()
elif myarg == 'verifyorganizer':
verifyOrganizer = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
i, count, users = getEntityArgument(users) i, count, users = getEntityArgument(users)
@@ -56054,7 +56089,7 @@ def moveDriveFile(users):
copyMoveOptions['sourceDriveId'] = source.get('driveId') copyMoveOptions['sourceDriveId'] = source.get('driveId')
if copyMoveOptions['sourceDriveId']: if copyMoveOptions['sourceDriveId']:
# If moving from a Shared Drive, user has to be an organizer # If moving from a Shared Drive, user has to be an organizer
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']): if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER) _incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
continue continue
if source['trashed']: if source['trashed']:
@@ -56099,7 +56134,7 @@ def moveDriveFile(users):
continue continue
if copyMoveOptions['destDriveId']: if copyMoveOptions['destDriveId']:
# If moving to a Shared Drive, user has to be an organizer # If moving to a Shared Drive, user has to be an organizer
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']): if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER) _incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
continue continue
# 3rd party shortcuts can't be moved to Shared Drives # 3rd party shortcuts can't be moved to Shared Drives

View File

@@ -198,6 +198,8 @@ OUTPUT_DATEFORMAT = 'output_dateformat'
OUTPUT_TIMEFORMAT = 'output_timeformat' OUTPUT_TIMEFORMAT = 'output_timeformat'
# When retrieving lists of people from API, how many should be retrieved in each chunk # When retrieving lists of people from API, how many should be retrieved in each chunk
PEOPLE_MAX_RESULTS = 'people_max_results' PEOPLE_MAX_RESULTS = 'people_max_results'
# Domains for print alises|groups|users
PRINT_AGU_DOMAINS = 'print_agu_domains'
# Number of seconds to wait for batch/csv processes to complete # Number of seconds to wait for batch/csv processes to complete
PROCESS_WAIT_LIMIT = 'process_wait_limit' PROCESS_WAIT_LIMIT = 'process_wait_limit'
# Use quick method to move Chromebooks to OU # Use quick method to move Chromebooks to OU
@@ -363,6 +365,7 @@ Defaults = {
OUTPUT_DATEFORMAT: '', OUTPUT_DATEFORMAT: '',
OUTPUT_TIMEFORMAT: '', OUTPUT_TIMEFORMAT: '',
PEOPLE_MAX_RESULTS: '100', PEOPLE_MAX_RESULTS: '100',
PRINT_AGU_DOMAINS: '',
PROCESS_WAIT_LIMIT: '0', PROCESS_WAIT_LIMIT: '0',
QUICK_CROS_MOVE: FALSE, QUICK_CROS_MOVE: FALSE,
QUICK_INFO_USER: FALSE, QUICK_INFO_USER: FALSE,
@@ -511,6 +514,7 @@ VAR_INFO = {
OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)}, OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)}, OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
PEOPLE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, 1000)}, PEOPLE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, 1000)},
PRINT_AGU_DOMAINS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
PROCESS_WAIT_LIMIT: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, None)}, PROCESS_WAIT_LIMIT: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, None)},
QUICK_CROS_MOVE: {VAR_TYPE: TYPE_BOOLEAN}, QUICK_CROS_MOVE: {VAR_TYPE: TYPE_BOOLEAN},
QUICK_INFO_USER: {VAR_TYPE: TYPE_BOOLEAN}, QUICK_INFO_USER: {VAR_TYPE: TYPE_BOOLEAN},

View File

@@ -156,6 +156,8 @@ OUTPUT_TIMEFORMAT = 'outf'
PARSER = 'pars' PARSER = 'pars'
# Process ID # Process ID
PID = 'pid ' PID = 'pid '
# Domains for print alises|groups|users
PRINT_AGU_DOMAINS = 'pagu'
# Check API calls rate # Check API calls rate
RATE_CHECK_COUNT = 'rccn' RATE_CHECK_COUNT = 'rccn'
RATE_CHECK_START = 'rcst' RATE_CHECK_START = 'rcst'
@@ -269,6 +271,7 @@ Globals = {
OUTPUT_TIMEFORMAT: '', OUTPUT_TIMEFORMAT: '',
PARSER: None, PARSER: None,
PID: 0, PID: 0,
PRINT_AGU_DOMAINS: '',
RATE_CHECK_COUNT: 0, RATE_CHECK_COUNT: 0,
RATE_CHECK_START: 0, RATE_CHECK_START: 0,
SECTION: None, SECTION: None,