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
## Definitions
See [Collections of Items](Collections-of-Items)
```
<DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName>
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
<EmailAddressEntity> ::= <EmailAddressList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector>
@@ -84,7 +88,8 @@ gam info alias|aliases <EmailAddressEntity>
Display selected aliases.
```
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>]
[select <UserTypeEntity>]
[aliasmatchpattern <RegularExpression>]
@@ -94,8 +99,9 @@ gam print aliases [todrive <ToDriveAttribute>*]
(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:
* `domain <DomainName>` - Limit aliases to those in `<DomainName>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Print aliases for selected users
* `domain|domains <DomainNameEntity>` - Limit aliases to those in the domains specified by `<DomainNameEntity>`
* `(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`
* `select <UserTypeEntity>` - Print aliases for users in `<UserTypeEntity>`
* `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.
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
You can bulk delete aliases as follows; use `(query <QueryUser>)|(queries <QueryUserList>)` and
`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.
### 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
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
## Definitions
See [Collections of Items](Collections-of-Items)
```
<DeliverySetting> ::=
allmail|
@@ -27,6 +28,9 @@
disabled|
none|nomail
<DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<UniqueID> ::= id:<String>
@@ -41,6 +45,7 @@
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
<QueryGroup> ::= <String>
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<MembersFieldName> ::=
delivery|deliverysettings|
@@ -563,7 +568,7 @@ gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity>
By default, delivery information is not displayed.
```
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>)|
(select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -581,10 +586,10 @@ gam print group-members [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
```
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>`
* `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_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
@@ -679,7 +684,7 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
## Display group membership in hierarchical format
```
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>)|
(select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
@@ -692,10 +697,10 @@ gam show group-members
[includederivedmembership]
```
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>`
* `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_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

View File

@@ -44,8 +44,12 @@
* https://support.google.com/a/answer/167430
## Definitions
See [Collections of Items](Collections-of-Items)
```
<DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::= <String>@<DomainName>
<UniqueID> ::= id:<String>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
@@ -60,6 +64,7 @@
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
<QueryGroup> ::= <String>
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<QueryDynamicGroup> ::= <String>
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.
```
gam print groups [todrive <ToDriveAttribute>*]
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -423,12 +428,11 @@ gam print groups [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
```
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>`
* `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>`
* `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.

View File

@@ -174,6 +174,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = 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$ ./gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.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>
Python 3.10.8 64-bit final
MacOS High Sierra 10.13.6 x86_64
@@ -596,6 +597,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = false
quick_info_user = False
@@ -794,6 +796,7 @@ Section: DEFAULT
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = 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>gam version
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>
Python 3.11.5 64-bit final
Windows-10-10.0.17134 AMD64
@@ -1240,6 +1243,7 @@ Section: DEFAULT
output_dateformat = ''
output_timeformat = ''
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = false
quick_info_user = False

View File

@@ -78,6 +78,7 @@
<QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*"
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
<QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*"
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
<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.
`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
### Add members to a chat space
```

View File

@@ -92,6 +92,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
(mappermissionsdomain <DomainName> <DomainName>)*
[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 `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.
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:
### Copy the top folder but none of its sub files/folders
@@ -443,12 +448,17 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
[updatefilepermissions [<Boolean>]]
[retainsourcefolders [<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 `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.
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:
### 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
<DomainName> ::= <String>(.<String>)+
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DomainNameEntity> ::=
<DomainNameList> | <FileSelector> | <CSVFileSelector>
<EmailAddress> ::=
<String>@<DomainName> |
<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
```
gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted])
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences]
[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:
* `domain <DomainName>` - Limit users to those in `<DomainName>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>`
* `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; each query is run against each domain
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
* `deleted_only|only_deleted` - Only display deleted users
* `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
```
gam print users [todrive <ToDriveAttribute>*]
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted])
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[formatjson [quotechar <Character>]] [countonly]
[issuspended <Boolean>]
```
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>`
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>`
* `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; each query is run against each domain
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
* `deleted_only|only_deleted` - Only display deleted users
* `issuspended <Boolean>` - Limit users based on their status

View File

@@ -3,7 +3,7 @@
Print the current version of Gam with details
```
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>
Python 3.11.5 64-bit final
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
```
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>
Python 3.11.5 64-bit final
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
```
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>
Python 3.11.5 64-bit final
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
Version Check:
Current: 5.35.08
Latest: 6.63.13
Latest: 6.63.14
echo $?
1
```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.63.13
6.63.14
```
In Linux/MacOS you can do:
```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.63.13 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.63.14 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.11.5 64-bit final
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
Default: 100
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
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.
@@ -633,6 +640,7 @@ Section: DEFAULT
output_dateformat = ''
output_timeformat = ''
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = false
quick_info_user = false
@@ -814,6 +822,7 @@ oauth2service_json = oauth2service.json
output_dateformat = ''
output_timeformat = ''
people_max_results = 100
print_agu_domains = ''
process_wait_limit = 0
quick_cros_move = False
quick_info_user = False

View File

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

View File

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

View File

@@ -198,6 +198,8 @@ OUTPUT_DATEFORMAT = 'output_dateformat'
OUTPUT_TIMEFORMAT = 'output_timeformat'
# When retrieving lists of people from API, how many should be retrieved in each chunk
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
PROCESS_WAIT_LIMIT = 'process_wait_limit'
# Use quick method to move Chromebooks to OU
@@ -363,6 +365,7 @@ Defaults = {
OUTPUT_DATEFORMAT: '',
OUTPUT_TIMEFORMAT: '',
PEOPLE_MAX_RESULTS: '100',
PRINT_AGU_DOMAINS: '',
PROCESS_WAIT_LIMIT: '0',
QUICK_CROS_MOVE: FALSE,
QUICK_INFO_USER: FALSE,
@@ -511,6 +514,7 @@ VAR_INFO = {
OUTPUT_DATEFORMAT: {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)},
PRINT_AGU_DOMAINS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
PROCESS_WAIT_LIMIT: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, None)},
QUICK_CROS_MOVE: {VAR_TYPE: TYPE_BOOLEAN},
QUICK_INFO_USER: {VAR_TYPE: TYPE_BOOLEAN},

View File

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