mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-23 23:51:36 +00:00
Compare commits
11 Commits
20240404.1
...
20240419.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74be07a9ef | ||
|
|
5607d659fb | ||
|
|
da1ef497a1 | ||
|
|
ac4fef0e4b | ||
|
|
0bc44582af | ||
|
|
baf0c7863f | ||
|
|
b00077151b | ||
|
|
842e46d060 | ||
|
|
bad4866bf7 | ||
|
|
3f5d96e13b | ||
|
|
a0dc04e7b0 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -118,7 +118,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20240210
|
||||
key: gam-${{ matrix.jid }}-20240416
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
- [Definitions](#definitions)
|
||||
- [Manage Projects](#manage-projects)
|
||||
- [Authorize a super admin to create projects](#authorize-a-super-admin-to-create-projects)
|
||||
- [Authorize Service Account Key Uploads](#authorize-service-account-key-uploads)
|
||||
- [Authorize GAM to create projects](#authorize-gam-to-create-projects)
|
||||
- [Create a new GCP project folder](#create-a-new-gcp-project-folder)
|
||||
- [Create a new project for GAM authorization](#create-a-new-project-for-gam-authorization)
|
||||
@@ -116,6 +117,7 @@ Verify whether the super admin you'll be using is in an OU where reauthenticatio
|
||||
|
||||
Additional steps may be required if errors are encountered.
|
||||
* [Authorize a super admin to create projects](#authorize-a-super-admin-to-create-projects)
|
||||
* [Authorize Service Account Key Uploads](#authorize-service-account-key-uploads)
|
||||
* [Authorize GAM to create projects](#authorize-gam-to-create-projects)
|
||||
|
||||
## Headless computers and Cloud Shells
|
||||
@@ -205,6 +207,46 @@ perform these steps and then retry the create project command.
|
||||
* Click Project Creator
|
||||
* Click Save
|
||||
|
||||
## Authorize Service Account Key Uploads
|
||||
|
||||
If you try to create a project and get an error saying that Constraint `constraints/iam.disableServiceAccountKeyUpload violated for service account projects/gam-project-xxx`
|
||||
perform these steps and then retry the create project command.
|
||||
|
||||
* Login as an existing super admin at console.cloud.google.com
|
||||
* In the upper left click the three lines to the left of Google Cloud and select IAM & Admin
|
||||
* Under IAM & Admin select IAM
|
||||
* Click the down arrow in the box to the right of Google Cloud
|
||||
* Click the three dots at the right and select IAM/Permissions
|
||||
* Now you should be at "Permissions for organization ..."
|
||||
* Click on Grant Access
|
||||
* Enter the new admin address in Principals
|
||||
* Click in the Select a role box
|
||||
* Type orgpolicy.policies.update in the Filter box
|
||||
* Click Organization Policy Administrator
|
||||
* Click Save
|
||||
* In the upper left click the three lines to the left of Google Cloud and select IAM & Admin
|
||||
* Under IAM & Admin select IAM
|
||||
* Click the down arrow in the box to the right of Google Cloud
|
||||
* Click the three dots at the right and select Manage Resources
|
||||
* Click the three dots and the end of the line for the GAM project just created
|
||||
* Click Settings
|
||||
* Click Organization Policies in the left column
|
||||
* Now you should be at "Policies for Gam Project"
|
||||
* Click in the Filter box
|
||||
* Enter iam.disableServiceAccountKeyUpload
|
||||
* Click the three dots at the end of the Disable Service Account Key Upload
|
||||
* Choose Edit policy
|
||||
* Click Override parent's policy
|
||||
* Click Add A Rule
|
||||
* Select Enforcement/Off
|
||||
* Click Done
|
||||
* Click Set Policy
|
||||
|
||||
Do the following to upload the service account key:
|
||||
```
|
||||
gam update sakey
|
||||
```
|
||||
|
||||
## Authorize GAM to create projects
|
||||
If you try to create a project and get an error saying "This app has been blocked on your domain for either being
|
||||
insecure or non-edutational"; you'll have to mark the GAM Project Creation app as trusted.
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
- [Definitions](#definitions)
|
||||
- [Quoting rules](#quoting-rules)
|
||||
- [Column row filtering](#column-row-filtering)
|
||||
- [Field names](#field-names)
|
||||
- [Inclusive filters](#inclusive-filters)
|
||||
- [Exclusive filters](#exclusive-filters)
|
||||
- [Matches](#matches)
|
||||
- [Column row limiting](#column-row-limiting)
|
||||
- [Saving filters in gam.cfg](#saving-filters-in-gamcfg)
|
||||
- [Validate filters](#validate-filters)
|
||||
@@ -39,28 +43,30 @@ These filters can be used alone or in conjunction with the `matchfield|skipfield
|
||||
|
||||
<FieldNameFilter> :: = <RegularExpression>
|
||||
<RowValueFilter> ::=
|
||||
[(any|all):]count<Operator><Number>|
|
||||
[(any|all):]countrange=<Number>/<Number>|
|
||||
[(any|all):]countrange!=<Number>/<Number>|
|
||||
[(any|all):]date<Operator><Date>|
|
||||
[(any|all):]daterange=<Date>/<Date>|
|
||||
[(any|all):]daterange!=<Date>/<Date>|
|
||||
[(any|all):]length<Operator><Number>|
|
||||
[(any|all):]lengthrange=<Number>/<Number>|
|
||||
[(any|all):]lengthrange!=<Number>/<Number>|
|
||||
[(any|all):]text<Operator><String>|
|
||||
[(any|all):]textrange=<String>/<String>|
|
||||
[(any|all):]textrange!=<String>/<String>|
|
||||
[(any|all):]time<Operator><Time>|
|
||||
[(any|all):]timerange=<Time>/<Time>|
|
||||
[(any|all):]timerange!=<Time>/<Time>|
|
||||
[(any|all):]boolean:<Boolean>|
|
||||
[(any|all):]regex:<RegularExpression>|
|
||||
[(any|all):]regexcs:<RegularExpression>|
|
||||
[(any|all):]count<Operator><Number>|
|
||||
[(any|all):]countrange!=<Number>/<Number>|
|
||||
[(any|all):]countrange=<Number>/<Number>|
|
||||
[(any|all):]data:<DataSelector>|
|
||||
[(any|all):]date<Operator><Date>|
|
||||
[(any|all):]daterange!=<Date>/<Date>|
|
||||
[(any|all):]daterange=<Date>/<Date>|
|
||||
[(any|all):]length<Operator><Number>|
|
||||
[(any|all):]lengthrange!=<Number>/<Number>|
|
||||
[(any|all):]lengthrange=<Number>/<Number>|
|
||||
[(any|all):]notdata:<DataSelector>|
|
||||
[(any|all):]notregex:<RegularExpression>|
|
||||
[(any|all):]notregexcs:<RegularExpression>|
|
||||
[(any|all):]data:<DataSelector>|
|
||||
[(any|all):]notdata:<DataSelector>|
|
||||
[(any|all):]regex:<RegularExpression>|
|
||||
[(any|all):]regexcs:<RegularExpression>|
|
||||
[(any|all):]text<Operator><String>|
|
||||
[(any|all):]textrange!=<String>/<String>|
|
||||
[(any|all):]textrange=<String>/<String>|
|
||||
[(any|all):]time<Operator><Time>|
|
||||
[(any|all):]timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>|
|
||||
[(any|all):]timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>|
|
||||
[(any|all):]timerange!=<Time>/<Time>|
|
||||
[(any|all):]timerange=<Time>/<Time>|
|
||||
<RowValueFilterList> ::=
|
||||
"'<FieldNameFilter>:<RowValueFilter>'(,'<FieldNameFilter>:<RowValueFilter>')*"
|
||||
<RowValueFilterJSONList> ::=
|
||||
@@ -156,11 +162,13 @@ In the case of `notregex|notregexcs|notdata`, the filter matches if some (not al
|
||||
If neither `any` or `all` is explicitly specified, `any` is the default.
|
||||
|
||||
These are the row value filter types:
|
||||
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
|
||||
* `count<Operator><Number>` - Used on fields with numbers; a blank field will not match
|
||||
* `countrange=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
|
||||
* The field value must be `>=` the left `<Number>` and `<=` the right `<Number>`
|
||||
* `countrange!=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
|
||||
* The field value must be `<` the left `<Number>` or `>` the right `<Number>`
|
||||
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
|
||||
* `date<Operator><Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
|
||||
* `daterange=<Date>/<Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
|
||||
* The field value must be `>=` the left `<Date>` and `<=` the right `<Date>`
|
||||
@@ -171,23 +179,25 @@ These are the row value filter types:
|
||||
* The field length must be `>=` the left `<Number>` and `<=` the right `<Number>`
|
||||
* `lengthrange!=<Number>/<Number>` - Used on fields with strings; non string fields will not match
|
||||
* The field length must be `<` the left `<Number>` or `>` the right `<Number>`
|
||||
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
|
||||
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
|
||||
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
|
||||
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
|
||||
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
|
||||
* `text<Operator><String>` - Used on fields with text
|
||||
* `textrange=<String>/<String>` - Used on fields with strings
|
||||
* The field value must be `>=` the left `<String>` and `<=` the right `<String>`
|
||||
* `textrange!=<String>/<String>` - Used on fields with strings
|
||||
* The field value must be `<` the left `<String>` or `>` the right `<String>`
|
||||
* `time<Operator><Time>` - Used on fields with times; a blank field will not match
|
||||
* `timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `>=` the left `<Hour>:<Minute>` and `<=` the right `<Hour>:<Minute>`
|
||||
* `timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `<` the left `<Hour>:<Minute>` or `>` the right `<Hour>:<Minute>`
|
||||
* `timerange=<Time>/<Time>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `>=` the left `<Time>` and `<=` the right `<Time>`
|
||||
* `timerange!=<Time>/<Time>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `<` the left `<Time>` or `>` the right `<Time>`
|
||||
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
|
||||
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
|
||||
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
|
||||
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
|
||||
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
|
||||
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
|
||||
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
|
||||
|
||||
### **Change in behavior.**
|
||||
In versions prior to `5.12.00`, `regex:<RegularExpression>` and `notregex:<RegularExpression>` were processed in a case sensitive manner;
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
- [Quoting rules](#quoting-rules)
|
||||
- [Column header filtering](#column-header-filtering)
|
||||
- [Column row filtering](#column-row-filtering)
|
||||
- [Field names](#field-names)
|
||||
- [Inclusive filters](#inclusive-filters)
|
||||
- [Exclusive filters](#exclusive-filters)
|
||||
- [Matches](#matches)
|
||||
- [Column row limiting](#column-row-limiting)
|
||||
- [Saving filters in gam.cfg](#saving-filters-in-gamcfg)
|
||||
|
||||
@@ -44,28 +48,30 @@ on all platforms.
|
||||
<FieldNameFilter> :: = <RegularExpression>
|
||||
<ColumnFieldNameFilterList> ::= "<FieldNameFilter>(,<FieldNameFilter>)*"
|
||||
<RowValueFilter> ::=
|
||||
[(any|all):]count<Operator><Number>|
|
||||
[(any|all):]countrange=<Number>/<Number>|
|
||||
[(any|all):]countrange!=<Number>/<Number>|
|
||||
[(any|all):]date<Operator><Date>|
|
||||
[(any|all):]textrange=<String>/<String>|
|
||||
[(any|all):]textrange!=<String>/<String>|
|
||||
[(any|all):]daterange=<Date>/<Date>|
|
||||
[(any|all):]daterange!=<Date>/<Date>|
|
||||
[(any|all):]length<Operator><Number>|
|
||||
[(any|all):]lengthrange=<Number>/<Number>|
|
||||
[(any|all):]lengthrange!=<Number>/<Number>|
|
||||
[(any|all):]text<Operator><String>|
|
||||
[(any|all):]time<Operator><Time>|
|
||||
[(any|all):]timerange=<Time>/<Time>|
|
||||
[(any|all):]timerange!=<Time>/<Time>|
|
||||
[(any|all):]boolean:<Boolean>|
|
||||
[(any|all):]regex:<RegularExpression>|
|
||||
[(any|all):]regexcs:<RegularExpression>|
|
||||
[(any|all):]count<Operator><Number>|
|
||||
[(any|all):]countrange!=<Number>/<Number>|
|
||||
[(any|all):]countrange=<Number>/<Number>|
|
||||
[(any|all):]data:<DataSelector>|
|
||||
[(any|all):]date<Operator><Date>|
|
||||
[(any|all):]daterange!=<Date>/<Date>|
|
||||
[(any|all):]daterange=<Date>/<Date>|
|
||||
[(any|all):]length<Operator><Number>|
|
||||
[(any|all):]lengthrange!=<Number>/<Number>|
|
||||
[(any|all):]lengthrange=<Number>/<Number>|
|
||||
[(any|all):]notdata:<DataSelector>
|
||||
[(any|all):]notregex:<RegularExpression>|
|
||||
[(any|all):]notregexcs:<RegularExpression>|
|
||||
[(any|all):]data:<DataSelector>|
|
||||
[(any|all):]notdata:<DataSelector>
|
||||
[(any|all):]regex:<RegularExpression>|
|
||||
[(any|all):]regexcs:<RegularExpression>|
|
||||
[(any|all):]text<Operator><String>|
|
||||
[(any|all):]textrange!=<String>/<String>|
|
||||
[(any|all):]textrange=<String>/<String>|
|
||||
[(any|all):]time<Operator><Time>|
|
||||
[(any|all):]timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>|
|
||||
[(any|all):]timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>|
|
||||
[(any|all):]timerange!=<Time>/<Time>|
|
||||
[(any|all):]timerange=<Time>/<Time>|
|
||||
<RowValueFilterList> ::=
|
||||
"'<FieldNameFilter>:<RowValueFilter>'(,'<FieldNameFilter>:<RowValueFilter>')*"
|
||||
<RowValueFilterJSONList> ::=
|
||||
@@ -214,11 +220,13 @@ In the case of `notregex|notregexcs|notdata`, the filter matches if some (not al
|
||||
If neither `any` or `all` is explicitly specified, `any` is the default.
|
||||
|
||||
These are the row value filter types:
|
||||
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
|
||||
* `count<Operator><Number>` - Used on fields with numbers; a blank field will not match
|
||||
* `countrange=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
|
||||
* The field value must be `>=` the left `<Number>` and `<=` the right `<Number>`
|
||||
* `countrange!=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
|
||||
* The field value must be `<` the left `<Number>` or `>` the right `<Number>`
|
||||
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
|
||||
* `date<Operator><Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
|
||||
* `daterange=<Date>/<Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
|
||||
* The field value must be `>=` the left `<Date>` and `<=` the right `<Date>`
|
||||
@@ -229,23 +237,25 @@ These are the row value filter types:
|
||||
* The field length must be `>=` the left `<Number>` and `<=` the right `<Number>`
|
||||
* `lengthrange!=<Number>/<Number>` - Used on fields with strings; non string fields will not match
|
||||
* The field length must be `<` the left `<Number>` or `>` the right `<Number>`
|
||||
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
|
||||
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
|
||||
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
|
||||
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
|
||||
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
|
||||
* `text<Operator><String>` - Used on fields with text
|
||||
* `textrange=<String>/<String>` - Used on fields with strings
|
||||
* The field value must be `>=` the left `<String>` and `<=` the right `<String>`
|
||||
* `textrange!=<String>/<String>` - Used on fields with strings
|
||||
* The field value must be `<` the left `<String>` or `>` the right `<String>`
|
||||
* `time<Operator><Time>` - Used on fields with times; a blank field will not match
|
||||
* `timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `>=` the left `<Hour>:<Minute>` and `<=` the right `<Hour>:<Minute>`
|
||||
* `timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `<` the left `<Hour>:<Minute>` or `>` the right `<Hour>:<Minute>`
|
||||
* `timerange=<Time>/<Time>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `>=` the left `<Time>` and `<=` the right `<Time>`
|
||||
* `timerange!=<Time>/<Time>` - Used on fields with times; a blank field will not match
|
||||
* The field value must be `<` the left `<Time>` or `>` the right `<Time>`
|
||||
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
|
||||
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
|
||||
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
|
||||
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
|
||||
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
|
||||
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
|
||||
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
|
||||
|
||||
### **Change in behavior.**
|
||||
In versions prior to `5.12.00`, `regex:<RegularExpression>` and `notregex:<RegularExpression>` were processed in a case sensitive manner;
|
||||
|
||||
@@ -196,6 +196,9 @@ Client access works when accessing Resource calendars.
|
||||
|
||||
<EventMatchProperty> ::=
|
||||
(matchfield attendees <EmailAddressEntity>)|
|
||||
(matchfield attendeesonlydomainlist <DomainNameList>)|
|
||||
(matchfield attendeesdomainlist <DomainNameList>)|
|
||||
(matchfield attendeesnotdomainlist <DomainNameList>)|
|
||||
(matchfield attendeespattern <RegularExpression>)|
|
||||
(matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>)|
|
||||
(matchfield creatoremail <RegularExpression>)|
|
||||
@@ -216,7 +219,6 @@ Client access works when accessing Resource calendars.
|
||||
(event|events <EventIdList> |
|
||||
<FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVSubkeySelector> | <CSVDataSelector>)
|
||||
See: https://github.com/taers232c/GAMADV-XTD3/wiki/Collections-of-Items
|
||||
|
||||
<EventSelectEntity> ::=
|
||||
(<EventSelectProperty>+ <EventMatchProperty>*)
|
||||
|
||||
@@ -229,6 +231,7 @@ Client access works when accessing Resource calendars.
|
||||
lavender|peacock|sage|tangerine|tomato
|
||||
<PropertyKey> ::= <String>
|
||||
<PropertyValue> ::= <String>
|
||||
<TimeZone> ::= <String>
|
||||
|
||||
<EventAttribute> ::=
|
||||
(allday <Date>)|
|
||||
@@ -356,7 +359,13 @@ The Google Calendar API processes `<EventSelectProperty>*`; you may specify none
|
||||
|
||||
GAM processes `<EventMatchProperty>*`; you may specify none or multiple properties.
|
||||
* `matchfield attendees <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
|
||||
* `matchfield attendeespattern <RegularExpression>` - Some attendee must match `<RegularExpression>`
|
||||
* `matchfield attendeesonlydomainlist <DomainNameList>` - All attendee's email addresses must be in a domain in `<DomainNameList>`
|
||||
* For example, this lets you look for events with all attendees in your internal domains
|
||||
* `matchfield attendeesdomainlist <DomainNameList>` - Some attendee's email address must be in a domain in `<DomainNameList>`
|
||||
* For example, this lets you look for events with attendees in specific external domains
|
||||
* `matchfield attendeesnotdomainlist <DomainNameList>` - Some attendee's email address must be in a domain not in `<DomainNameList>`
|
||||
* For example, this lets you look for events with attendees not in your internal domains
|
||||
* `matchfield attendeespattern <RegularExpression>` - Some attendee's email address must match `<RegularExpression>`
|
||||
* `matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
|
||||
and must have the specified values.
|
||||
* `<AttendeeAttendance>` - Default is `required`
|
||||
|
||||
@@ -10,10 +10,71 @@ 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.73.00
|
||||
|
||||
The Google Chat API has been updated so that chat members can now have their role set to manager.
|
||||
|
||||
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#manage-chat-members
|
||||
|
||||
### 6.72.16
|
||||
|
||||
Updated `emailaddressList <EmailAddressList>` and `domainlist|notdomainlist <DomainNameList>`
|
||||
in `<PermissionMatch>` to perform case-insensitive matches as the API is returning mixed case
|
||||
ACL email addresses in some cases.
|
||||
|
||||
### 6.75.15
|
||||
|
||||
Updated all commands that display tasks to display the due date in GMT as the time portion
|
||||
is not supported by the API and converting the due date to local time may display the wrong date.
|
||||
|
||||
Renamed license SKU `1010400001` from `Beyond Corp Enterprise` to `Chrome Enterprise Premium`.
|
||||
|
||||
### 6.72.14
|
||||
|
||||
Upgraded to Python 3.12.3 where possible.
|
||||
|
||||
### 6.72.13
|
||||
|
||||
Added the following option to `<EventMatchProperty>` that can be used to select
|
||||
events based on the domains of the attendees.
|
||||
```
|
||||
matchfield attendeesonlydomainlist <DomainNameList>
|
||||
```
|
||||
This returns true if all attendee's email addresses are in a domain in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees only in your internal domains.
|
||||
|
||||
### 6.72.12
|
||||
|
||||
Added the following options to `<EventMatchProperty>` that can be used to select
|
||||
events based on the domains of the attendees.
|
||||
```
|
||||
matchfield attendeesdomainlist <DomainNameList>
|
||||
matchfield attendeesnotdomainlist <DomainNameList>
|
||||
```
|
||||
The first returns true if any attendee's email address is in a domain in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees in specific external domains.
|
||||
|
||||
The second returns true if any attendee's email address is in a domain other than those in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees not in your internal domains.
|
||||
|
||||
### 6.72.11
|
||||
|
||||
Added option `oneitemperrow` to 'gam print vaultholds` to have each of a
|
||||
hold's accounts displayed on a separate row with all of the other hold fields.
|
||||
|
||||
### 6.72.10
|
||||
|
||||
Added `timeofdayrange=<HH:MM>/<HH:MM>` and `timeofdayrange!=<HH:MM>/<HH:MM>` to `<RowValueFilter>` that allows
|
||||
CSV row filtering based on time-of-day.
|
||||
|
||||
### 6.72.09
|
||||
|
||||
Updated `countsonly` option of `gam <UserTypeEntity> print|show notes` to additionally display the total number of notes.
|
||||
|
||||
### 6.72.08
|
||||
|
||||
Added option `countsonly` to `gam <UserTypeEntity> print|show notes` that displays
|
||||
the number of notes a user owns and the number of notes of user can edit.
|
||||
the number of notes a user owns and the number of notes a user can edit.
|
||||
|
||||
### 6.72.07
|
||||
|
||||
|
||||
@@ -334,10 +334,10 @@ 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.72.08 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.73.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
MacOS Sonoma 14.2.1 x86_64
|
||||
Python 3.12.3 64-bit final
|
||||
MacOS Sonoma 14.4.1 x86_64
|
||||
Path: /Users/admin/bin/gamadv-xtd3
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@@ -1006,9 +1006,9 @@ 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.72.08 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.73.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
Python 3.12.3 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
Path: C:\GAMADV-XTD3
|
||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|--------------|------------|
|
||||
| AppSheet | 101038 |
|
||||
| Assured Controls | 101039 |
|
||||
| Beyond Corp Enterprise | 101040 |
|
||||
| Chrome Enterprise | 101040 |
|
||||
| Cloud Identity Free | 101001 |
|
||||
| Cloud Identity Premium | 101005 |
|
||||
| Cloud Search | 101035 |
|
||||
@@ -43,7 +43,7 @@
|
||||
| AppSheet Enterprise Standard | 1010380002 | appsheetstandard |
|
||||
| AppSheet Enterprise Plus | 1010380003 | appsheetplus |
|
||||
| Assured Controls | 1010390001 | assuredcontrols |
|
||||
| Beyond Corp Enterprise | 1010400001 | bce |
|
||||
| Chrome Enterprise Premium | 1010400001 | cep | chromeenterprisepremium |
|
||||
| Cloud Identity Free | 1010010001 | cloudidentity |
|
||||
| Cloud Identity Premium | 1010050001 | cloudidentitypremium |
|
||||
| Cloud Search | 1010350001 | cloudsearch |
|
||||
@@ -140,7 +140,7 @@
|
||||
appsheetstandard | appsheetenterprisestandard | 1010380002 | AppSheet Enterprise Standard |
|
||||
appsheetplus | appsheetenterpriseplus | 1010380003 | AppSheet Enterprise Plus |
|
||||
assuredcontrols | 1010390001 | Assured Controls |
|
||||
bce | beyondcorp | beyondcorpenterprise | 1010400001 | Beyond Corp Enterprise |
|
||||
bce | beyondcorp | beyondcorpenterprise | cep | chromeenterprisepremium | 1010400001 | Chrome Enterprise Premium |
|
||||
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
|
||||
cloudidentity | identity | 1010010001 | Cloud Identity |
|
||||
cloudidentitypremium | identitypremium | 1010050001 | Cloud Identity Premium |
|
||||
|
||||
@@ -261,6 +261,9 @@
|
||||
|
||||
<EventMatchProperty> ::=
|
||||
(matchfield attendees <EmailAddressEntity>)|
|
||||
(matchfield attendeesonlydomainlist <DomainNameList>)|
|
||||
(matchfield attendeesdomainlist <DomainNameList>)|
|
||||
(matchfield attendeesnotdomainlist <DomainNameList>)|
|
||||
(matchfield attendeespattern <RegularExpression>)|
|
||||
(matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>)|
|
||||
(matchfield creatoremail <RegularExpression>)|
|
||||
@@ -438,7 +441,13 @@ The Google Calendar API processes `<EventSelectProperty>*`; you may specify none
|
||||
|
||||
GAM processes `<EventMatchProperty>*`; you may specify none or multiple properties.
|
||||
* `matchfield attendees <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
|
||||
* `matchfield attendeespattern <RegularExpression>` - Some attendee must match `<RegularExpression>`
|
||||
* `matchfield attendeesonlydomainlist <DomainNameList>` - All attendee's email addresses must be in a domain in `<DomainNameList>`
|
||||
* For example, this lets you look for events with all attendees in your internal domains
|
||||
* `matchfield attendeesdomainlist <DomainNameList>` - Some attendee's email address must be in a domain in `<DomainNameList>`
|
||||
* For example, this lets you look for events with attendees in specific external domains
|
||||
* `matchfield attendeesnotdomainlist <DomainNameList>` - Some attendee's email address must be in a domain not in `<DomainNameList>`
|
||||
* For example, this lets you look for events with attendees not in your internal domains
|
||||
* `matchfield attendeespattern <RegularExpression>` - Some attendee's email address must match `<RegularExpression>`
|
||||
* `matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
|
||||
and must have the specified values.
|
||||
* `<AttendeeAttendance>` - Default is `required`
|
||||
|
||||
@@ -238,6 +238,20 @@ Delete members by specifying chatmember names.
|
||||
gam <UserTypeEntity> remove chatmember members <ChatMemberList>
|
||||
```
|
||||
|
||||
### Update members role
|
||||
Update members by specifying a chat space, user/group email addresses and role.
|
||||
```
|
||||
gam <UserTypeEntity> update chatmember <ChatSpace>
|
||||
role member|manager
|
||||
((user <UserItem>)|(members <UserTypeEntity>))+
|
||||
```
|
||||
Update members by specifying chatmember names and role.
|
||||
```
|
||||
gam <UserTypeEntity> modify chatmember
|
||||
role member|manager
|
||||
members <ChatMemberList>
|
||||
```
|
||||
|
||||
## Display Chat Members
|
||||
### Display information about a specific chat members
|
||||
```
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- [Manage vacation](#manage-vacation)
|
||||
- [Display vacation](#display-vacation)
|
||||
- [User attribute `replace <Tag> <UserReplacement>` processing](Tag-Replace)
|
||||
- [Standardize user signatures](#standardize-user-signatures)
|
||||
|
||||
## API documentation
|
||||
* https://developers.google.com/gmail/api/reference/rest/v1/users.settings.sendAs
|
||||
@@ -245,3 +246,37 @@ Gam displays the information in CSV form.
|
||||
* `compact` - Strip carriage returns and newlines in original HTML; this makes these values easier to process in the CSV file
|
||||
and can be used as input to GAM.
|
||||
* `enabledonly` - Do not display users with vacation autoreply disabled.
|
||||
|
||||
## Standardize user signatures
|
||||
You can standardize user signatures by creating a signature template and a CSV file with data for each user.
|
||||
|
||||
You can create a signature template by defining the signature in the Gmail Settings GUI of a test user.
|
||||
You must use the default signature `My signature`.
|
||||
Use text like `{FirstName}` and `{Email}` in the locations where the actual values will go.
|
||||
|
||||
Once you're created the template signature, do the following:
|
||||
```
|
||||
$ gam user testuser@domain.com show signature compact > SimpleSig.html
|
||||
$ more SimpleSig.html
|
||||
SendAs Address: <testuser@domain.com>
|
||||
IsPrimary: True
|
||||
Default: True
|
||||
Signature: <div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
|
||||
```
|
||||
Edit SimpleSig.html and delete all text from `SendAs ` through `Signature: `.
|
||||
The result should be:
|
||||
```
|
||||
<div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
|
||||
```
|
||||
|
||||
This is a sample Users.csv file.
|
||||
```
|
||||
email,first,last,phone
|
||||
bsmith@domain.com,Bob,Smith,510-555-1212 x 123
|
||||
mjones@domain.com,Mary,Jones,510-555-1212 x 456
|
||||
```
|
||||
|
||||
This command will update the user's signatures.
|
||||
```
|
||||
gam csv Users.csv gam user "~email" signature htmlfile SimpleSig.html replace FirstName "~first" replace LastName "~last" replace Phone "~phone" replace Email "~email"
|
||||
```
|
||||
@@ -139,8 +139,8 @@ By default, GAM displays all non-trashed notes:
|
||||
* `filter trashed` - Display notes in the trash
|
||||
* `role owner|writer` - Display notes where the user has the specified role
|
||||
|
||||
When option `countsonly` is specified, the number of notes a user owns and the number of notes of user can edit
|
||||
if displayed.
|
||||
When option `countsonly` is specified, the number of notes a user owns, the number of notes of user can edit
|
||||
and the total number of notes is displayed.
|
||||
|
||||
By default, Gam displays the information as an indented list of keys and values; the note text is displayed as individual lines.
|
||||
* `compact` - Display the note text with escaped carriage returns as \r and newlines as \n
|
||||
@@ -158,8 +158,8 @@ By default, GAM displays all non-trashed notes:
|
||||
* `filter trashed` - Display notes in the trash
|
||||
* `role owner|writer` - Display notes where the user has the specified role
|
||||
|
||||
When option `countsonly` is specified, the number of notes a user owns and the number of notes of user can edit
|
||||
if displayed.
|
||||
When option `countsonly` is specified, the number of notes a user owns, the number of notes of user can edit
|
||||
and the total number of notes is displayed.
|
||||
|
||||
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.
|
||||
|
||||
@@ -69,6 +69,8 @@ gam <UserTypeEntity> create task <TasklistEntity>
|
||||
<TaskAttribute>* [parent <TaskID>] [previous <TaskID>]
|
||||
[compact|formatjson|returnidonly]
|
||||
```
|
||||
The API only supports all-day tasks; you should specify: `due YYYY-MM-DDT00:00:00Z`.
|
||||
|
||||
By default, Gam displays the created task as an indented list of keys and values; the task notes text is displayed as individual lines.
|
||||
* `compact` - Display the task notes text with escaped carriage returns as \r and newlines as \n
|
||||
* `formatjson` - Display the task in JSON format
|
||||
@@ -100,6 +102,9 @@ By default, Gam displays the moved task as an indented list of keys and values;
|
||||
* `formatjson` - Display the task in JSON format
|
||||
|
||||
## Display Tasks
|
||||
All commands that display tasks display the due date in GMT as the time portion
|
||||
is not supported by the API and converting the due date to local time may display the wrong date.
|
||||
|
||||
### Display selected tasks
|
||||
```
|
||||
gam <UserTypeEntity> info task <TasklistIDTaskIDEntity>
|
||||
@@ -119,6 +124,13 @@ gam <UserTypeEntity> show tasks [tasklists <TasklistEntity>]
|
||||
[orderby completed|due|updated]
|
||||
[countsonly|compact|formatjson]
|
||||
```
|
||||
The API only supports dates in `duemin` and `duemax' but you must supply a null time:
|
||||
* `duemin YYYY-MM-DDT00:00:00Z` - Specify the starting due date
|
||||
* `duemax YYYY-MM-DDT00:00:00Z` - Specify one day beyond the ending due date
|
||||
|
||||
For example: `duemin 2024-05-01T00:00:00Z duemax 2024-05-02T00:00:00Z` will
|
||||
display all tasks on 2024-05-01.
|
||||
|
||||
By default, tasks are displayed in hierarchical order.
|
||||
* `orderby completed` - Display tasks in completed date order regardless of the hierarchy.
|
||||
* `orderby due` - Display tasks in due date order regardless of the hierarchy.
|
||||
@@ -142,6 +154,13 @@ gam <UserTypeEntity> print tasks [tasklists <TasklistEntity>] [todrive <ToDriveA
|
||||
[orderby completed|due|updated]
|
||||
[countsonly | (formatjson [quotechar <Character>])]
|
||||
```
|
||||
The API only supports dates in `duemin` and `duemax' but you must supply a null time:
|
||||
* `duemin YYYY-MM-DDT00:00:00Z` - Specify the starting due date
|
||||
* `duemax YYYY-MM-DDT00:00:00Z` - Specify one day beyond the ending due date
|
||||
|
||||
For example: `duemin 2024-05-01T00:00:00Z duemax 2024-05-02T00:00:00Z` will
|
||||
display all tasks on 2024-05-01.
|
||||
|
||||
By default, tasks are displayed in hierarchical order.
|
||||
* `orderby completed` - Display tasks in completed date order regardless of the hierarchy.
|
||||
* `orderby due` - Display tasks in due date order regardless of the hierarchy.
|
||||
|
||||
@@ -657,7 +657,11 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
gam print vaultholds|holds [todrive <ToDriveAttributes>*] [matters <MatterItemList>]
|
||||
[fields <VaultHoldFieldNameList>] [shownames]
|
||||
[formatjson [quotechar <Character>]]
|
||||
[oneitemperrow]
|
||||
```
|
||||
By default, all accounts for a hold are displayed on a single row;
|
||||
use `oneitemperrow` to have each account displayed on a separate row.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAMADV-XTD3 6.72.08 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.73.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
MacOS Sonoma 14.2.1 x86_64
|
||||
Python 3.12.3 64-bit final
|
||||
MacOS Sonoma 14.4.1 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2023-06-02T21:10:00-07:00
|
||||
@@ -15,10 +15,10 @@ 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.72.08 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.73.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
MacOS Sonoma 14.2.1 x86_64
|
||||
Python 3.12.3 64-bit final
|
||||
MacOS Sonoma 14.4.1 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Your system time differs from www.googleapis.com by less than 1 second
|
||||
@@ -27,17 +27,17 @@ 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.72.08 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.73.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
MacOS Sonoma 14.2.1 x86_64
|
||||
Python 3.12.3 64-bit final
|
||||
MacOS Sonoma 14.4.1 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2023-06-02T21:10:00-07:00
|
||||
Your system time differs from admin.googleapis.com by less than 1 second
|
||||
OpenSSL 3.1.1 30 May 2023
|
||||
cryptography 41.0.1
|
||||
filelock 3.12.2
|
||||
filelock 3.12.3
|
||||
google-api-python-client 2.88.0
|
||||
google-auth-httplib2 0.1.0
|
||||
google-auth-oauthlib 1.0.0
|
||||
@@ -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.72.08
|
||||
Latest: 6.73.00
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -72,7 +72,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
6.72.08
|
||||
6.73.00
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -82,10 +82,10 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 6.72.08 - https://github.com/taers232c/GAMADV-XTD3
|
||||
GAM 6.73.00 - https://github.com/taers232c/GAMADV-XTD3
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.2 64-bit final
|
||||
MacOS Sonoma 14.2.1 x86_64
|
||||
Python 3.12.3 64-bit final
|
||||
MacOS Sonoma 14.4.1 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2023-06-02T21:10:00-07:00
|
||||
|
||||
@@ -123,7 +123,9 @@ clock_skew_in_seconds
|
||||
Default: 10
|
||||
Range: 10 - 3600
|
||||
cmdlog
|
||||
Path to GAM Log file; there is no logging if cmdlog is empty
|
||||
Path to GAM Log file; there is no logging if cmdlog is empty.
|
||||
If cmdlog specifies a relative path, e.g., just a filename, it is appended to GamConfigDir.
|
||||
If cmdlog specifies a full path, it is used as is.
|
||||
Default: ''
|
||||
cmdlog_max_backups
|
||||
Maximum number of backup log files
|
||||
|
||||
@@ -263,7 +263,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
appsheetstandard | appsheetenterprisestandard | 1010380002 | AppSheet Enterprise Standard |
|
||||
appsheetplus | appsheetenterpriseplus | 1010380003 | AppSheet Enterprise Plus |
|
||||
assuredcontrols | 1010390001 | Assured Controls |
|
||||
bce | beyondcorp | beyondcorpenterprise | 1010400001 | Beyond Corp Enterprise |
|
||||
bce | beyondcorp | beyondcorpenterprise | cep | chromeenterprisepremium | 1010400001 | Chrome Enterprise Premium |
|
||||
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
|
||||
cloudidentity | identity | 1010010001 | Cloud Identity |
|
||||
cloudidentitypremium | identitypremium | 1010050001 | Cloud Identity Premium |
|
||||
@@ -1597,6 +1597,9 @@ gam calendar <CalendarEntity> printacl [todrive <ToDriveAttribute>*]
|
||||
|
||||
<EventMatchProperty> ::=
|
||||
(matchfield attendees <EmailAddressEntity>)|
|
||||
(matchfield attendeesonlydomainlist <DomainNameList>)|
|
||||
(matchfield attendeesdomainlist <DomainNameList>)|
|
||||
(matchfield attendeesnotdomainlist <DomainNameList>)|
|
||||
(matchfield attendeespattern <RegularExpression>)|
|
||||
(matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>)|
|
||||
(matchfield creatoremail <RegularExpression>)|
|
||||
@@ -5137,6 +5140,7 @@ gam info vaulthold|hold <MatterItem> <HoldItem>
|
||||
gam print vaultholds|holds [todrive <ToDriveAttribute>*] [matters <MatterItemList>]
|
||||
[fields <VaultHoldFieldNameList>] [shownames]
|
||||
[formatjson [quotechar <Character>]]
|
||||
[oneitemperrow]
|
||||
gam show vaultholds|holds [matters <MatterItemList>]
|
||||
[fields <VaultHoldFieldNameList>] [shownames]
|
||||
[formatjson]
|
||||
@@ -5976,6 +5980,12 @@ gam <UserTypeEntity> delete chatmember <ChatSpace>
|
||||
((user <UserItem>)|(members <UserTypeEntity>)|
|
||||
(group <GroupItem>)|(groups <GroupEntity>))+
|
||||
gam <UserTypeEntity> remove chatmember members <ChatMemberList>
|
||||
gam <UserTypeEntity> update chatmember <ChatSpace>
|
||||
role member|manager
|
||||
((user <UserItem>)|(members <UserTypeEntity>))+
|
||||
gam <UserTypeEntity> modify chatmember
|
||||
role member|manager
|
||||
members <ChatMemberList>
|
||||
|
||||
gam <UserTypeEntity> info chatmember members <ChatMemberList>
|
||||
[formatjson]
|
||||
|
||||
@@ -2,10 +2,71 @@
|
||||
|
||||
Merged GAM-Team version
|
||||
|
||||
6.73.00
|
||||
|
||||
The Google Chat API has been updated so that chat members can now have their role set to manager.
|
||||
|
||||
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#manage-chat-members
|
||||
|
||||
6.72.16
|
||||
|
||||
Updated `emailaddressList <EmailAddressList>` and `domainlist|notdomainlist <DomainNameList>`
|
||||
in `<PermissionMatch>` to perform case-insensitive matches as the API is returning mixed case
|
||||
ACL email addresses in some cases.
|
||||
|
||||
6.72.15
|
||||
|
||||
Updated all commands that display tasks to display the due date in GMT as the time portion
|
||||
is not supported by the API and converting the due date to local time may display the wrong date.
|
||||
|
||||
Renamed license SKU `1010400001` from `Beyond Corp Enterprise` to `Chrome Enterprise Premium`.
|
||||
|
||||
6.72.14
|
||||
|
||||
Upgraded to Python 3.12.3 where possible.
|
||||
|
||||
6.72.13
|
||||
|
||||
Added the following option to `<EventMatchProperty>` that can be used to select
|
||||
events based on the domains of the attendees.
|
||||
```
|
||||
matchfield attendeesonlydomainlist <DomainNameList>
|
||||
```
|
||||
This returns true if all attendee's email addresses are in a domain in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees only in your internal domains.
|
||||
|
||||
6.72.12
|
||||
|
||||
Added the following options to `<EventMatchProperty>` that can be used to select
|
||||
events based on the domains of the attendees.
|
||||
```
|
||||
matchfield attendeesdomainlist <DomainNameList>
|
||||
matchfield attendeesnotdomainlist <DomainNameList>
|
||||
```
|
||||
The first returns true if any attendee's email address is in a domain in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees in specific external domains.
|
||||
|
||||
The second returns true if any attendee's email address is in a domain other than those in `<DomainNameList>`;
|
||||
for example this lets you look for events with attendees not in your internal domains.
|
||||
|
||||
6.72.11
|
||||
|
||||
Added option `oneitemperrow` to 'gam print vaultholds` to have each of a
|
||||
hold's accounts displayed on a separate row with all of the other hold fields.
|
||||
|
||||
6.72.10
|
||||
|
||||
Added `timeofdayrange=<HH:MM>/<HH:MM>` and `timeofdayrange!=<HH:MM>/<HH:MM>` to `<RowValueFilter>` that allows
|
||||
CSV row filtering based on time-of-day.
|
||||
|
||||
6.72.09
|
||||
|
||||
Updated `countsonly` option of `gam <UserTypeEntity> print|show notes` to additionally display the total number of notes.
|
||||
|
||||
6.72.08
|
||||
|
||||
Added option `countsonly` to `gam <UserTypeEntity> print|show notes` that displays
|
||||
the number of notes a user owns and the number of notes of user can edit.
|
||||
the number of notes a user owns and the number of notes a user can edit.
|
||||
|
||||
6.72.07
|
||||
|
||||
|
||||
@@ -3420,6 +3420,7 @@ def SetGlobalVariables():
|
||||
ROW_FILTER_ANY_ALL_PATTERN = re.compile(r'^(any:|all:)(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_COMP_PATTERN = re.compile(r'^(date|time|count|length)\s*([<>]=?|=|!=)(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_RANGE_PATTERN = re.compile(r'^(daterange|timerange|countrange|lengthrange)(=|!=)(\S+)/(\S+)$', re.IGNORECASE)
|
||||
ROW_FILTER_TIMEOFDAYRANGE_PATTERN = re.compile(r'^(timeofdayrange)(=|!=)(\d\d):(\d\d)/(\d\d):(\d\d)$', re.IGNORECASE)
|
||||
ROW_FILTER_BOOL_PATTERN = re.compile(r'^(boolean):(.+)$', re.IGNORECASE)
|
||||
ROW_FILTER_TEXT_PATTERN = re.compile(r'^(text)([<>]=?|=|!=)(.*)$', re.IGNORECASE)
|
||||
ROW_FILTER_TEXTRANGE_PATTERN = re.compile(r'^(textrange)(=|!=)(.*)/(.*)$', re.IGNORECASE)
|
||||
@@ -3520,6 +3521,19 @@ def SetGlobalVariables():
|
||||
else:
|
||||
_printValueError(sectionName, itemName, f'"{column}": "{filterStr}"', f'{Msg.EXPECTED}: <Number>/<Number>')
|
||||
continue
|
||||
mg = ROW_FILTER_TIMEOFDAYRANGE_PATTERN.match(filterStr)
|
||||
if mg:
|
||||
filterType = mg.group(1).lower()
|
||||
startHour = int(mg.group(3))
|
||||
startMinute = int(mg.group(4))
|
||||
endHour = int(mg.group(5))
|
||||
endMinute = int(mg.group(6))
|
||||
if startHour > 23 or startMinute > 59 or endHour > 23 or endMinute > 59 or \
|
||||
endHour < startHour or (endHour == startHour and endMinute < startMinute):
|
||||
Cmd.Backup()
|
||||
usageErrorExit(Msg.INVALID_TIMEOFDAY_RANGE.format(f'{startHour:02d}:{startMinute:02d}', f'{endHour:02d}:{endMinute:02d}'))
|
||||
rowFilters.append((columnPat, anyMatch, filterType, mg.group(2), f'{startHour:02d}:{startMinute:02d}', f'{endHour:02d}:{endMinute:02d}'))
|
||||
continue
|
||||
mg = ROW_FILTER_BOOL_PATTERN.match(filterStr)
|
||||
if mg:
|
||||
filterType = mg.group(1).lower()
|
||||
@@ -3558,7 +3572,7 @@ def SetGlobalVariables():
|
||||
rowFilters.append((columnPat, anyMatch, filterType, getEntitiesFromCSVFile(False, returnSet=True)))
|
||||
Cmd.RestoreArguments()
|
||||
continue
|
||||
_printValueError(sectionName, itemName, f'"{column}": "{filterStr}"', f'{Msg.EXPECTED}: date|time|count|text<Operator><Value> or boolean:<Boolean> or regex:<RegularExpression> or data:<DataSelector>')
|
||||
_printValueError(sectionName, itemName, f'"{column}": "{filterStr}"', f'{Msg.EXPECTED}: <RowValueFilter>')
|
||||
return rowFilters
|
||||
|
||||
def _getCfgSection(sectionName, itemName):
|
||||
@@ -4180,6 +4194,7 @@ def SetGlobalVariables():
|
||||
# Set environment variables so GData API can find cacerts.pem
|
||||
os.environ['REQUESTS_CA_BUNDLE'] = GC.Values[GC.CACERTS_PEM]
|
||||
os.environ['DEFAULT_CA_BUNDLE_PATH'] = GC.Values[GC.CACERTS_PEM]
|
||||
os.environ['HTTPLIB2_CA_CERTS'] = GC.Values[GC.CACERTS_PEM]
|
||||
os.environ['SSL_CERT_FILE'] = GC.Values[GC.CACERTS_PEM]
|
||||
httplib2.CA_CERTS = GC.Values[GC.CACERTS_PEM]
|
||||
# Needs to be set so oauthlib doesn't puke when Google changes our scopes
|
||||
@@ -7307,6 +7322,36 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
|
||||
return False
|
||||
return True
|
||||
|
||||
def getHourMinuteFromDateTime(rowDate):
|
||||
if YYYYMMDD_PATTERN.match(rowDate):
|
||||
return None
|
||||
try:
|
||||
rowTime, _ = iso8601.parse_date(rowDate)
|
||||
except (iso8601.ParseError, OverflowError):
|
||||
return None
|
||||
return f'{rowTime.hour:02d}:{rowTime.minute:02d}'
|
||||
|
||||
def rowTimeOfDayRangeFilterMatch(op, startHourMinute, endHourMinute):
|
||||
def checkMatch(rowDate):
|
||||
if not rowDate or not isinstance(rowDate, str) or rowDate == GC.Values[GC.NEVER_TIME]:
|
||||
return False
|
||||
rowHourMinute = getHourMinuteFromDateTime(rowDate)
|
||||
if not rowHourMinute:
|
||||
return False
|
||||
if op == '!=':
|
||||
return not startHourMinute <= rowHourMinute <= endHourMinute
|
||||
return startHourMinute <= rowHourMinute <= endHourMinute
|
||||
|
||||
if anyMatch:
|
||||
for column in columns:
|
||||
if checkMatch(row.get(column, '')):
|
||||
return True
|
||||
return False
|
||||
for column in columns:
|
||||
if not checkMatch(row.get(column, '')):
|
||||
return False
|
||||
return True
|
||||
|
||||
def rowCountFilterMatch(op, filterCount):
|
||||
def checkMatch(rowCount):
|
||||
if isinstance(rowCount, str):
|
||||
@@ -7509,6 +7554,9 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
|
||||
elif filterVal[2] in {'daterange', 'timerange'}:
|
||||
if rowDateTimeRangeFilterMatch(filterVal[2] == 'date', filterVal[3], filterVal[4], filterVal[5]):
|
||||
return True
|
||||
elif filterVal[2] == 'timeofdayrange':
|
||||
if rowTimeOfDayRangeFilterMatch(filterVal[3], filterVal[4], filterVal[5]):
|
||||
return True
|
||||
elif filterVal[2] == 'count':
|
||||
if rowCountFilterMatch(filterVal[3], filterVal[4]):
|
||||
return True
|
||||
@@ -25464,7 +25512,7 @@ def createChatMember(users):
|
||||
member = callGAPI(chat.spaces().members(), 'patch',
|
||||
bailOnInternalError=True,
|
||||
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
|
||||
name=name, updateMask='role', body={'role': role})
|
||||
name=member['name'], updateMask='role', body={'role': role})
|
||||
if not returnIdOnly:
|
||||
kvList[-1] = member['name']
|
||||
_getChatMemberEmail(cd, member)
|
||||
@@ -36117,6 +36165,9 @@ LIST_EVENTS_SELECT_PROPERTIES = {
|
||||
|
||||
LIST_EVENTS_MATCH_FIELDS = {
|
||||
'attendees': ['attendees', 'email'],
|
||||
'attendeesonlydomainlist': ['attendees', 'onlydomainlist'],
|
||||
'attendeesdomainlist': ['attendees', 'domainlist'],
|
||||
'attendeesnotdomainlist': ['attendees', 'notdomainlist'],
|
||||
'attendeespattern': ['attendees', 'match'],
|
||||
'attendeesstatus': ['attendees', 'status'],
|
||||
'description': ['description'],
|
||||
@@ -36188,6 +36239,8 @@ def getCalendarEventEntity():
|
||||
calendarEventEntity['matches'].append((matchField, getBoolean()))
|
||||
elif matchField[0] != 'attendees' or matchField[1] == 'match':
|
||||
calendarEventEntity['matches'].append((matchField, getREPattern(re.IGNORECASE)))
|
||||
elif matchField[0] == 'attendees' and matchField[1] in {'onlydomainlist', 'domainlist', 'notdomainlist'}:
|
||||
calendarEventEntity['matches'].append((matchField, set(getString(Cmd.OB_DOMAIN_NAME_LIST).replace(',', ' ').split())))
|
||||
elif matchField[1] == 'email':
|
||||
calendarEventEntity['matches'].append((matchField, getNormalizedEmailAddressEntity()))
|
||||
else: #status
|
||||
@@ -36462,6 +36515,24 @@ def _eventMatches(event, match):
|
||||
if match[1].search(attendee) is not None:
|
||||
return True
|
||||
return False
|
||||
if match[0][1] == 'onlydomainlist':
|
||||
for attendee in attendees:
|
||||
_, domain = attendee.lower().split('@', 1)
|
||||
if domain not in match[1]:
|
||||
return False
|
||||
return True
|
||||
if match[0][1] == 'domainlist':
|
||||
for attendee in attendees:
|
||||
_, domain = attendee.lower().split('@', 1)
|
||||
if domain in match[1]:
|
||||
return True
|
||||
return False
|
||||
if match[0][1] == 'notdomainlist':
|
||||
for attendee in attendees:
|
||||
_, domain = attendee.lower().split('@', 1)
|
||||
if domain not in match[1]:
|
||||
return True
|
||||
return False
|
||||
# status
|
||||
for matchEmail in match[3]:
|
||||
for attendee in event['attendees']:
|
||||
@@ -39269,16 +39340,27 @@ PRINT_VAULT_HOLDS_TITLES = ['matterId', 'matterName', 'holdId', 'name', 'updateT
|
||||
# gam print vaultholds|holds [todrive <ToDriveAttribute>*] [matters <MatterItemList>]
|
||||
# [fields <VaultHoldFieldNameList>] [shownames]
|
||||
# [formatjson [quotechar <Character>]]
|
||||
# [oneitemperrow]
|
||||
# gam show vaultholds|holds [matters <MatterItemList>]
|
||||
# [fields <VaultHoldFieldNameList>] [shownames]
|
||||
# [formatjson]
|
||||
def doPrintShowVaultHolds():
|
||||
def _printVaultHold(hold):
|
||||
row = flattenJSON(hold, flattened={'matterId': matterId, 'matterName': matterName}, timeObjects=VAULT_HOLD_TIME_OBJECTS)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
csvPF.WriteRowNoFilter({'matterId': matterId, 'matterName': matterName,
|
||||
'holdId': hold['holdId'], 'name': hold['name'],
|
||||
'JSON': json.dumps(cleanJSON(hold, timeObjects=VAULT_HOLD_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)})
|
||||
|
||||
v = buildGAPIObject(API.VAULT)
|
||||
csvPF = CSVPrintFile(PRINT_VAULT_HOLDS_TITLES, 'sortall') if Act.csvFormat() else None
|
||||
FJQC = FormatJSONQuoteChar()
|
||||
matters = []
|
||||
cd = None
|
||||
fieldsList = []
|
||||
oneItemPerRow = False
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if csvPF and myarg == 'todrive':
|
||||
@@ -39289,6 +39371,8 @@ def doPrintShowVaultHolds():
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
elif getFieldsList(myarg, VAULT_HOLD_FIELDS_CHOICE_MAP, fieldsList, initialField=['holdId', 'name']):
|
||||
pass
|
||||
elif csvPF and myarg == 'oneitemperrow':
|
||||
oneItemPerRow = True
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, False)
|
||||
fields = getItemFieldsFromFieldsList('holds', fieldsList)
|
||||
@@ -39352,13 +39436,12 @@ def doPrintShowVaultHolds():
|
||||
else:
|
||||
for hold in holds:
|
||||
_cleanVaultHold(hold, cd)
|
||||
row = flattenJSON(hold, flattened={'matterId': matterId, 'matterName': matterName}, timeObjects=VAULT_HOLD_TIME_OBJECTS)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
csvPF.WriteRowNoFilter({'matterId': matterId, 'matterName': matterName,
|
||||
'holdId': hold['holdId'], 'name': hold['name'],
|
||||
'JSON': json.dumps(cleanJSON(hold, timeObjects=VAULT_HOLD_TIME_OBJECTS), ensure_ascii=False, sort_keys=True)})
|
||||
if not oneItemPerRow or not hold.get('accounts', []):
|
||||
_printVaultHold(hold)
|
||||
else:
|
||||
for account in hold.pop('accounts'):
|
||||
hold['account'] = account
|
||||
_printVaultHold(hold)
|
||||
if csvPF:
|
||||
csvPF.writeCSVfile('Vault Holds')
|
||||
|
||||
@@ -52875,7 +52958,7 @@ class PermissionMatch():
|
||||
body['emailAddress'] = getREPattern(re.IGNORECASE)
|
||||
self.permissionFields.add('emailAddress')
|
||||
elif myarg == 'emailaddresslist':
|
||||
body[myarg] = set(getString(Cmd.OB_EMAIL_ADDRESS_LIST).replace(',', ' ').split())
|
||||
body[myarg] = set(getString(Cmd.OB_EMAIL_ADDRESS_LIST).replace(',', ' ').lower().split())
|
||||
self.permissionFields.add('emailAddress')
|
||||
elif myarg == 'permissionidlist':
|
||||
body[myarg] = set(getString(Cmd.OB_PERMISSION_ID_LIST).replace(',', ' ').split())
|
||||
@@ -52885,7 +52968,7 @@ class PermissionMatch():
|
||||
self.permissionFields.add('domain')
|
||||
self.permissionFields.add('emailAddress')
|
||||
elif myarg in {'domainlist', 'notdomainlist'}:
|
||||
body[myarg] = set(getString(Cmd.OB_DOMAIN_NAME_LIST).replace(',', ' ').split())
|
||||
body[myarg] = set(getString(Cmd.OB_DOMAIN_NAME_LIST).replace(',', ' ').lower().split())
|
||||
self.permissionFields.add('domain')
|
||||
self.permissionFields.add('emailAddress')
|
||||
elif myarg == 'withlink':
|
||||
@@ -52992,7 +53075,7 @@ class PermissionMatch():
|
||||
elif field == 'emailaddresslist':
|
||||
emailAddress = permission.get('emailAddress')
|
||||
if emailAddress:
|
||||
if emailAddress not in value:
|
||||
if emailAddress.lower() not in value:
|
||||
break
|
||||
else:
|
||||
break
|
||||
@@ -53008,9 +53091,9 @@ class PermissionMatch():
|
||||
break
|
||||
else:
|
||||
if 'domain' in permission:
|
||||
domain = permission['domain']
|
||||
domain = permission['domain'].lower()
|
||||
elif 'emailAddress' in permission and permission['emailAddress']:
|
||||
_, domain = splitEmailAddress(permission['emailAddress'])
|
||||
_, domain = splitEmailAddress(permission['emailAddress'].lower())
|
||||
else:
|
||||
break
|
||||
if ((field == 'domain' and not value.match(domain)) or
|
||||
@@ -71349,7 +71432,7 @@ def printShowNotes(users):
|
||||
csvPF.RemoveJSONTitles(['owner', 'ownedByMe'])
|
||||
if countsOnly and csvPF:
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.SetTitles(['User', 'noteOwner', 'noteWriter'])
|
||||
csvPF.SetTitles(['User', 'noteOwner', 'noteWriter', 'totalNotes'])
|
||||
else:
|
||||
csvPF.SetJSONTitles(['User', 'JSON'])
|
||||
fields = getItemFieldsFromFieldsList('notes', fieldsList, returnItemIfNoneList=False)
|
||||
@@ -71370,27 +71453,30 @@ def printShowNotes(users):
|
||||
throwReasons=GAPI.KEEP_THROW_REASONS,
|
||||
filter=noteFilter, fields=fields)
|
||||
if countsOnly:
|
||||
noteCounts = {'User': user, 'noteOwner': 0, 'noteWriter': 0}
|
||||
noteCounts = {'User': user, 'noteOwner': 0, 'noteWriter': 0, 'totalNotes': 0}
|
||||
for note in notes:
|
||||
noteCounts['totalNotes'] += 1
|
||||
for permission in note['permissions']:
|
||||
if permission.get('user', {}).get('email', '').lower() == user:
|
||||
noteCounts[NOTES_COUNTS_MAP[permission['role']]] += 1
|
||||
break
|
||||
if not csvPF:
|
||||
if not FJQC.formatJSON:
|
||||
printEntityKVList([Ent.USER, user], ['noteOwner', noteCounts['noteOwner'], 'noteWriter', noteCounts['noteWriter']], i, count)
|
||||
printEntityKVList([Ent.USER, user], ['noteOwner', noteCounts['noteOwner'],
|
||||
'noteWriter', noteCounts['noteWriter'],
|
||||
'totalNotes', noteCounts['totalNotes']], i, count)
|
||||
else:
|
||||
printLine(json.dumps(cleanJSON(noteCounts), ensure_ascii=False, sort_keys=True))
|
||||
else:
|
||||
row = {'User': user, 'noteOwner': noteCounts['noteOwner'], 'noteWriter': noteCounts['noteWriter']}
|
||||
row = {'User': user, 'noteOwner': noteCounts['noteOwner'], 'noteWriter': noteCounts['noteWriter'],
|
||||
'totalNotes': noteCounts['totalNotes']}
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
row = {'User': noteCounts.pop('User')}
|
||||
row['JSON'] = json.dumps(cleanJSON(noteCounts), ensure_ascii=False, sort_keys=True)
|
||||
csvPF.WriteRowNoFilter(row)
|
||||
continue
|
||||
if not csvPF:
|
||||
elif not csvPF:
|
||||
jcount = len(notes)
|
||||
if not FJQC.formatJSON:
|
||||
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.NOTE, i, count)
|
||||
@@ -71704,7 +71790,7 @@ def getTaskListIDfromTitle(svc, userTasklists, title, user, i, count):
|
||||
return userTasklists, None
|
||||
|
||||
TASK_SKIP_OBJECTS = ['selfLink']
|
||||
TASK_TIME_OBJECTS = ['due', 'completed', 'updated']
|
||||
TASK_TIME_OBJECTS = ['completed', 'updated']
|
||||
|
||||
def _showTask(tasklist, task, j=0, jcount=0, FJQC=None, compact=False):
|
||||
task['tasklistId'] = tasklist
|
||||
|
||||
@@ -293,6 +293,7 @@ INVALID_SCHEMA_VALUE = 'Invalid Schema Value'
|
||||
INVALID_SCOPE = 'Invalid Scope'
|
||||
INVALID_SITE = 'Invalid Site ({0}), must match pattern ({1})'
|
||||
INVALID_TAG_SPECIFICATION = 'Invalid tag, expected field.subfield or field.subfield.subfield.string'
|
||||
INVALID_TIMEOFDAY_RANGE = '{0} must be less than/equal to {1}'
|
||||
IN_SKIPIDS = 'In skipids'
|
||||
IN_THE = ' in the {0}'
|
||||
IN_TRASH_AND_EXCLUDE_TRASHED = 'In Trash and excludeTrashed'
|
||||
|
||||
@@ -32,7 +32,7 @@ _PRODUCTS = {
|
||||
'101037': 'Google Workspace for Education',
|
||||
'101038': 'AppSheet',
|
||||
'101039': 'Assured Controls',
|
||||
'101040': 'Beyond Corp Enterprise',
|
||||
'101040': 'Chrome Enterprise',
|
||||
'101043': 'Google Workspace Additional Storage',
|
||||
'101047': 'Gemini',
|
||||
'101049': 'Education Endpoint Management',
|
||||
@@ -83,7 +83,7 @@ _SKUS = {
|
||||
'1010390001': {
|
||||
'product': '101039', 'aliases': ['assuredcontrols'], 'displayName': 'Assured Controls'},
|
||||
'1010400001': {
|
||||
'product': '101040', 'aliases': ['beyondcorp', 'beyondcorpenterprise', 'bce'], 'displayName': 'Beyond Corp Enterprise'},
|
||||
'product': '101040', 'aliases': ['beyondcorp', 'beyondcorpenterprise', 'bce', 'cep', 'chromeenterprisepremium'], 'displayName': 'Chrome Enterprise Premium'},
|
||||
'1010430001': {
|
||||
'product': '101043', 'aliases': ['gwas', 'plusstorage'], 'displayName': 'Google Workspace Additional Storage'},
|
||||
'1010470001': {
|
||||
|
||||
Reference in New Issue
Block a user