gam7 7.20.3__py3-none-any.whl → 7.28.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gam/__init__.py +1279 -673
- gam/__main__.py +6 -1
- gam/gamlib/glapi.py +35 -19
- gam/gamlib/glcfg.py +12 -0
- gam/gamlib/glclargs.py +288 -5
- gam/gamlib/glentity.py +11 -3
- gam/gamlib/glglobals.py +3 -0
- gam/gamlib/glmsgs.py +3 -1
- gam/gamlib/glverlibs.py +1 -1
- gam/gamlib/yubikey.py +13 -12
- {gam7-7.20.3.dist-info → gam7-7.28.2.dist-info}/METADATA +10 -4
- {gam7-7.20.3.dist-info → gam7-7.28.2.dist-info}/RECORD +15 -33
- gam/googleapiclient/__init__.py +0 -27
- gam/googleapiclient/_auth.py +0 -167
- gam/googleapiclient/_helpers.py +0 -207
- gam/googleapiclient/channel.py +0 -315
- gam/googleapiclient/discovery.py +0 -1662
- gam/googleapiclient/discovery_cache/__init__.py +0 -78
- gam/googleapiclient/discovery_cache/appengine_memcache.py +0 -55
- gam/googleapiclient/discovery_cache/base.py +0 -46
- gam/googleapiclient/discovery_cache/file_cache.py +0 -145
- gam/googleapiclient/errors.py +0 -197
- gam/googleapiclient/http.py +0 -1962
- gam/googleapiclient/mimeparse.py +0 -183
- gam/googleapiclient/model.py +0 -429
- gam/googleapiclient/schema.py +0 -317
- gam/googleapiclient/version.py +0 -15
- gam/iso8601/__init__.py +0 -28
- gam/iso8601/iso8601.py +0 -160
- gam/six.py +0 -982
- {gam7-7.20.3.dist-info → gam7-7.28.2.dist-info}/WHEEL +0 -0
- {gam7-7.20.3.dist-info → gam7-7.28.2.dist-info}/entry_points.txt +0 -0
- {gam7-7.20.3.dist-info → gam7-7.28.2.dist-info}/licenses/LICENSE +0 -0
gam/__init__.py
CHANGED
|
@@ -25,17 +25,15 @@ https://github.com/GAM-team/GAM/wiki
|
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
27
|
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
|
28
|
-
__version__ = '7.
|
|
28
|
+
__version__ = '7.28.02'
|
|
29
29
|
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
|
30
30
|
|
|
31
31
|
#pylint: disable=wrong-import-position
|
|
32
32
|
import base64
|
|
33
|
-
import calendar as calendarlib
|
|
34
33
|
import codecs
|
|
35
34
|
import collections
|
|
36
35
|
import configparser
|
|
37
36
|
import csv
|
|
38
|
-
import datetime
|
|
39
37
|
from email.charset import add_charset, QP
|
|
40
38
|
from email.generator import Generator
|
|
41
39
|
from email.header import decode_header, Header
|
|
@@ -51,7 +49,7 @@ from email.policy import SMTP as policySMTP
|
|
|
51
49
|
import hashlib
|
|
52
50
|
from html.entities import name2codepoint
|
|
53
51
|
from html.parser import HTMLParser
|
|
54
|
-
import http.client
|
|
52
|
+
import http.client
|
|
55
53
|
import importlib
|
|
56
54
|
from importlib.metadata import version as lib_version
|
|
57
55
|
import io
|
|
@@ -109,7 +107,7 @@ from cryptography.x509.oid import NameOID
|
|
|
109
107
|
if not getattr(sys, 'frozen', False):
|
|
110
108
|
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
|
111
109
|
|
|
112
|
-
|
|
110
|
+
import arrow
|
|
113
111
|
|
|
114
112
|
from pathvalidate import sanitize_filename, sanitize_filepath
|
|
115
113
|
|
|
@@ -120,6 +118,10 @@ from google.auth.jwt import Credentials as JWTCredentials
|
|
|
120
118
|
import google.oauth2.service_account
|
|
121
119
|
import google_auth_oauthlib.flow
|
|
122
120
|
import google_auth_httplib2
|
|
121
|
+
import googleapiclient
|
|
122
|
+
import googleapiclient.discovery
|
|
123
|
+
import googleapiclient.errors
|
|
124
|
+
import googleapiclient.http
|
|
123
125
|
import httplib2
|
|
124
126
|
|
|
125
127
|
httplib2.RETRIES = 5
|
|
@@ -149,12 +151,6 @@ import gdata.apps.audit
|
|
|
149
151
|
import gdata.apps.audit.service
|
|
150
152
|
import gdata.apps.contacts
|
|
151
153
|
import gdata.apps.contacts.service
|
|
152
|
-
# Import local library, does not include discovery documents
|
|
153
|
-
import googleapiclient
|
|
154
|
-
import googleapiclient.discovery
|
|
155
|
-
import googleapiclient.errors
|
|
156
|
-
import googleapiclient.http
|
|
157
|
-
from iso8601 import iso8601
|
|
158
154
|
|
|
159
155
|
IS08601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S%:z'
|
|
160
156
|
RFC2822_TIME_FORMAT = '%a, %d %b %Y %H:%M:%S %z'
|
|
@@ -163,7 +159,7 @@ def ISOformatTimeStamp(timestamp):
|
|
|
163
159
|
return timestamp.isoformat('T', 'seconds')
|
|
164
160
|
|
|
165
161
|
def currentISOformatTimeStamp(timespec='milliseconds'):
|
|
166
|
-
return
|
|
162
|
+
return arrow.now(GC.Values[GC.TIMEZONE]).isoformat('T', timespec)
|
|
167
163
|
|
|
168
164
|
Act = glaction.GamAction()
|
|
169
165
|
Cmd = glclargs.GamCLArgs()
|
|
@@ -213,6 +209,7 @@ ONE_GIGA_10_BYTES = 1000000000
|
|
|
213
209
|
ONE_KILO_BYTES = 1024
|
|
214
210
|
ONE_MEGA_BYTES = 1048576
|
|
215
211
|
ONE_GIGA_BYTES = 1073741824
|
|
212
|
+
DAYS_OF_WEEK = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
|
216
213
|
SECONDS_PER_MINUTE = 60
|
|
217
214
|
SECONDS_PER_HOUR = 3600
|
|
218
215
|
SECONDS_PER_DAY = 86400
|
|
@@ -378,6 +375,37 @@ YUBIKEY_VALUE_ERROR_RC = 85
|
|
|
378
375
|
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
|
|
379
376
|
YUBIKEY_NOT_FOUND_RC = 87
|
|
380
377
|
|
|
378
|
+
DEBUG_REDACTION_PATTERNS = [
|
|
379
|
+
# Positional patterns that redact sensitive credentials based on their location
|
|
380
|
+
(r'(Bearer\s+)\S+', r'\1*****'), # access tokens and JWTs in auth header
|
|
381
|
+
(r'([?&]refresh_token=)[^&]*', r'\1*****'), # refresh token URL parameter
|
|
382
|
+
(r'([?&]client_secret=)[^&]*', r'\1*****'), # client secret URL parameter
|
|
383
|
+
(r'([?&]key=)[^&]*', r'\1*****'), # API key URL parameter
|
|
384
|
+
(r'([?&]code=)[^&]*', r'\1*****'), # auth code URL parameter
|
|
385
|
+
|
|
386
|
+
# Pattern match patterns that redact sensitive credentials based on known credential pattern
|
|
387
|
+
(r'ya29.[0-9A-Za-z-_]+', '*****'), # Access token
|
|
388
|
+
(r'1%2F%2F[0-9A-Za-z-_]{100}|1%2F%2F[0-9A-Za-z-_]{64}|1%2F%2F[0-9A-Za-z-_]{43}', '*****'), # Refresh token
|
|
389
|
+
(r'4/[0-9A-Za-z-_]+', '*****'), # Auth code
|
|
390
|
+
(r'GOCSPX-[0-9a-zA-Z-_]{28}', '*****'), # Client secret
|
|
391
|
+
(r'AIza[0-9A-Za-z-_]{35}', '*****'), # API key
|
|
392
|
+
(r'eyJ[a-zA-Z0-9\-_]+\.eyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]*', '*****'), # JWT
|
|
393
|
+
]
|
|
394
|
+
|
|
395
|
+
def redactable_debug_print(*args):
|
|
396
|
+
processed_args = []
|
|
397
|
+
for arg in args:
|
|
398
|
+
if arg.startswith('b\''):
|
|
399
|
+
sbytes = arg[2:-1]
|
|
400
|
+
sbytes = bytes(sbytes, 'utf-8')
|
|
401
|
+
arg = sbytes.decode()
|
|
402
|
+
arg = arg.replace('\\r\\n', "\n ")
|
|
403
|
+
if GC.Values[GC.DEBUG_REDACTION]:
|
|
404
|
+
for pattern, replace in DEBUG_REDACTION_PATTERNS:
|
|
405
|
+
arg = re.sub(pattern, replace, arg)
|
|
406
|
+
processed_args.append(arg)
|
|
407
|
+
print(*processed_args)
|
|
408
|
+
|
|
381
409
|
# Multiprocessing lock
|
|
382
410
|
mplock = None
|
|
383
411
|
|
|
@@ -972,14 +1000,14 @@ SUSPENDED_CHOICE_MAP = {'notsuspended': False, 'suspended': True}
|
|
|
972
1000
|
def _getIsSuspended(myarg):
|
|
973
1001
|
if myarg in SUSPENDED_CHOICE_MAP:
|
|
974
1002
|
return SUSPENDED_CHOICE_MAP[myarg]
|
|
975
|
-
return getBoolean()
|
|
1003
|
+
return getBoolean() #issuspended
|
|
976
1004
|
|
|
977
1005
|
ARCHIVED_ARGUMENTS = {'notarchived', 'archived', 'isarchived'}
|
|
978
1006
|
ARCHIVED_CHOICE_MAP = {'notarchived': False, 'archived': True}
|
|
979
1007
|
def _getIsArchived(myarg):
|
|
980
1008
|
if myarg in ARCHIVED_CHOICE_MAP:
|
|
981
1009
|
return ARCHIVED_CHOICE_MAP[myarg]
|
|
982
|
-
return getBoolean()
|
|
1010
|
+
return getBoolean() #isarchived
|
|
983
1011
|
|
|
984
1012
|
def _getOptionalIsSuspendedIsArchived():
|
|
985
1013
|
isSuspended = isArchived = None
|
|
@@ -1552,10 +1580,12 @@ def getOrderBySortOrder(choiceMap, defaultSortOrderChoice='ASCENDING', mapSortOr
|
|
|
1552
1580
|
return (getChoice(choiceMap, mapChoice=True),
|
|
1553
1581
|
getChoice(SORTORDER_CHOICE_MAP, defaultChoice=defaultSortOrderChoice, mapChoice=mapSortOrderChoice))
|
|
1554
1582
|
|
|
1555
|
-
def orgUnitPathQuery(path, isSuspended):
|
|
1583
|
+
def orgUnitPathQuery(path, isSuspended, isArchived):
|
|
1556
1584
|
query = "orgUnitPath='{0}'".format(path.replace("'", "\\'")) if path != '/' else ''
|
|
1557
1585
|
if isSuspended is not None:
|
|
1558
1586
|
query += f' isSuspended={isSuspended}'
|
|
1587
|
+
if isArchived is not None:
|
|
1588
|
+
query += f' isArchived={isArchived}'
|
|
1559
1589
|
return query
|
|
1560
1590
|
|
|
1561
1591
|
def makeOrgUnitPathAbsolute(path):
|
|
@@ -1843,13 +1873,13 @@ def getStringWithCRsNLsOrFile():
|
|
|
1843
1873
|
return (unescapeCRsNLs(getString(Cmd.OB_STRING, minLen=0)), UTF8, False)
|
|
1844
1874
|
|
|
1845
1875
|
def todaysDate():
|
|
1846
|
-
return
|
|
1847
|
-
|
|
1876
|
+
return arrow.Arrow(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
|
|
1877
|
+
tzinfo=GC.Values[GC.TIMEZONE])
|
|
1848
1878
|
|
|
1849
1879
|
def todaysTime():
|
|
1850
|
-
return
|
|
1851
|
-
|
|
1852
|
-
|
|
1880
|
+
return arrow.Arrow(GM.Globals[GM.DATETIME_NOW].year, GM.Globals[GM.DATETIME_NOW].month, GM.Globals[GM.DATETIME_NOW].day,
|
|
1881
|
+
GM.Globals[GM.DATETIME_NOW].hour, GM.Globals[GM.DATETIME_NOW].minute,
|
|
1882
|
+
tzinfo=GC.Values[GC.TIMEZONE])
|
|
1853
1883
|
|
|
1854
1884
|
def getDelta(argstr, pattern):
|
|
1855
1885
|
if argstr == 'NOW':
|
|
@@ -1862,22 +1892,22 @@ def getDelta(argstr, pattern):
|
|
|
1862
1892
|
sign = tg.group(1)
|
|
1863
1893
|
delta = int(tg.group(2))
|
|
1864
1894
|
unit = tg.group(3)
|
|
1865
|
-
if
|
|
1866
|
-
|
|
1867
|
-
elif unit == 'w':
|
|
1868
|
-
deltaTime = datetime.timedelta(weeks=delta)
|
|
1869
|
-
elif unit == 'd':
|
|
1870
|
-
deltaTime = datetime.timedelta(days=delta)
|
|
1871
|
-
elif unit == 'h':
|
|
1872
|
-
deltaTime = datetime.timedelta(hours=delta)
|
|
1873
|
-
elif unit == 'm':
|
|
1874
|
-
deltaTime = datetime.timedelta(minutes=delta)
|
|
1895
|
+
if sign == '-':
|
|
1896
|
+
delta = -delta
|
|
1875
1897
|
baseTime = todaysDate()
|
|
1876
1898
|
if unit in {'h', 'm'}:
|
|
1877
|
-
baseTime = baseTime
|
|
1878
|
-
if
|
|
1879
|
-
return baseTime
|
|
1880
|
-
|
|
1899
|
+
baseTime = baseTime.shift(hours=GM.Globals[GM.DATETIME_NOW].hour, minutes=GM.Globals[GM.DATETIME_NOW].minute)
|
|
1900
|
+
if unit == 'y':
|
|
1901
|
+
return baseTime.shift(days=delta*365)
|
|
1902
|
+
if unit == 'w':
|
|
1903
|
+
return baseTime.shift(weeks=delta)
|
|
1904
|
+
if unit == 'd':
|
|
1905
|
+
return baseTime.shift(days=delta)
|
|
1906
|
+
if unit == 'h':
|
|
1907
|
+
return baseTime.shift(hours=delta)
|
|
1908
|
+
if unit == 'm':
|
|
1909
|
+
return baseTime.shift(minutes=delta)
|
|
1910
|
+
return baseTime
|
|
1881
1911
|
|
|
1882
1912
|
DELTA_DATE_PATTERN = re.compile(r'^([+-])(\d+)([dwy])$')
|
|
1883
1913
|
DELTA_DATE_FORMAT_REQUIRED = '(+|-)<Number>(d|w|y)'
|
|
@@ -1916,7 +1946,7 @@ def getYYYYMMDD(minLen=1, returnTimeStamp=False, returnDateTime=False, alternate
|
|
|
1916
1946
|
elif argstr == 'NEVER':
|
|
1917
1947
|
argstr = NEVER_DATE
|
|
1918
1948
|
try:
|
|
1919
|
-
dateTime =
|
|
1949
|
+
dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
1920
1950
|
Cmd.Advance()
|
|
1921
1951
|
if returnTimeStamp:
|
|
1922
1952
|
return time.mktime(dateTime.timetuple())*1000
|
|
@@ -1938,7 +1968,7 @@ def getHHMM():
|
|
|
1938
1968
|
argstr = Cmd.Current().strip().upper()
|
|
1939
1969
|
if argstr:
|
|
1940
1970
|
try:
|
|
1941
|
-
|
|
1971
|
+
arrow.Arrow.strptime(argstr, HHMM_FORMAT)
|
|
1942
1972
|
Cmd.Advance()
|
|
1943
1973
|
return argstr
|
|
1944
1974
|
except ValueError:
|
|
@@ -1958,7 +1988,7 @@ def getYYYYMMDD_HHMM():
|
|
|
1958
1988
|
argstr = NEVER_DATETIME
|
|
1959
1989
|
argstr = argstr.replace('T', ' ')
|
|
1960
1990
|
try:
|
|
1961
|
-
|
|
1991
|
+
arrow.Arrow.strptime(argstr, YYYYMMDD_HHMM_FORMAT)
|
|
1962
1992
|
Cmd.Advance()
|
|
1963
1993
|
return argstr
|
|
1964
1994
|
except ValueError:
|
|
@@ -1977,10 +2007,10 @@ def getDateOrDeltaFromNow(returnDateTime=False):
|
|
|
1977
2007
|
argstr = 'TODAY'
|
|
1978
2008
|
argDate = getDeltaDate(argstr)
|
|
1979
2009
|
elif argstr == 'NEVER':
|
|
1980
|
-
argDate =
|
|
2010
|
+
argDate = arrow.Arrow.strptime(NEVER_DATE, YYYYMMDD_FORMAT)
|
|
1981
2011
|
elif YYYYMMDD_PATTERN.match(argstr):
|
|
1982
2012
|
try:
|
|
1983
|
-
argDate =
|
|
2013
|
+
argDate = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
1984
2014
|
except ValueError:
|
|
1985
2015
|
invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
|
|
1986
2016
|
else:
|
|
@@ -1988,12 +2018,12 @@ def getDateOrDeltaFromNow(returnDateTime=False):
|
|
|
1988
2018
|
Cmd.Advance()
|
|
1989
2019
|
if not returnDateTime:
|
|
1990
2020
|
return argDate.strftime(YYYYMMDD_FORMAT)
|
|
1991
|
-
return (
|
|
2021
|
+
return (arrow.Arrow(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE]),
|
|
1992
2022
|
GC.Values[GC.TIMEZONE], argDate.strftime(YYYYMMDD_FORMAT))
|
|
1993
2023
|
missingArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
|
|
1994
2024
|
|
|
1995
2025
|
YYYYMMDDTHHMMSS_FORMAT_REQUIRED = 'yyyy-mm-ddThh:mm:ss[.fff](Z|(+|-(hh:mm)))'
|
|
1996
|
-
TIMEZONE_FORMAT_REQUIRED = '
|
|
2026
|
+
TIMEZONE_FORMAT_REQUIRED = 'utc|z|local|(+|-(hh:mm))|<ValidTimezoneName>'
|
|
1997
2027
|
|
|
1998
2028
|
def getTimeOrDeltaFromNow(returnDateTime=False):
|
|
1999
2029
|
if Cmd.ArgumentsRemaining():
|
|
@@ -2005,7 +2035,7 @@ def getTimeOrDeltaFromNow(returnDateTime=False):
|
|
|
2005
2035
|
argstr = NEVER_TIME
|
|
2006
2036
|
elif YYYYMMDD_PATTERN.match(argstr):
|
|
2007
2037
|
try:
|
|
2008
|
-
dateTime =
|
|
2038
|
+
dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
2009
2039
|
except ValueError:
|
|
2010
2040
|
invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
|
|
2011
2041
|
try:
|
|
@@ -2013,12 +2043,12 @@ def getTimeOrDeltaFromNow(returnDateTime=False):
|
|
|
2013
2043
|
except OverflowError:
|
|
2014
2044
|
pass
|
|
2015
2045
|
try:
|
|
2016
|
-
fullDateTime
|
|
2046
|
+
fullDateTime = arrow.get(argstr)
|
|
2017
2047
|
Cmd.Advance()
|
|
2018
2048
|
if not returnDateTime:
|
|
2019
2049
|
return argstr.replace(' ', 'T')
|
|
2020
|
-
return (fullDateTime,
|
|
2021
|
-
except (
|
|
2050
|
+
return (fullDateTime, fullDateTime.tzinfo, argstr.replace(' ', 'T'))
|
|
2051
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
2022
2052
|
pass
|
|
2023
2053
|
invalidArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
|
|
2024
2054
|
missingArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
|
|
@@ -2031,19 +2061,19 @@ def getRowFilterDateOrDeltaFromNow(argstr):
|
|
|
2031
2061
|
deltaDate = getDelta(argstr, DELTA_DATE_PATTERN)
|
|
2032
2062
|
if deltaDate is None:
|
|
2033
2063
|
return (False, DELTA_DATE_FORMAT_REQUIRED)
|
|
2034
|
-
argstr = ISOformatTimeStamp(deltaDate.replace(tzinfo=
|
|
2064
|
+
argstr = ISOformatTimeStamp(deltaDate.replace(tzinfo='UTC'))
|
|
2035
2065
|
elif argstr == 'NEVER' or YYYYMMDD_PATTERN.match(argstr):
|
|
2036
2066
|
if argstr == 'NEVER':
|
|
2037
2067
|
argstr = NEVER_DATE
|
|
2038
2068
|
try:
|
|
2039
|
-
dateTime =
|
|
2069
|
+
dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
2040
2070
|
except ValueError:
|
|
2041
2071
|
return (False, YYYYMMDD_FORMAT_REQUIRED)
|
|
2042
|
-
argstr = ISOformatTimeStamp(dateTime.replace(tzinfo=
|
|
2072
|
+
argstr = ISOformatTimeStamp(dateTime.replace(tzinfo='UTC'))
|
|
2043
2073
|
try:
|
|
2044
|
-
|
|
2074
|
+
arrow.get(argstr)
|
|
2045
2075
|
return (True, argstr.replace(' ', 'T'))
|
|
2046
|
-
except (
|
|
2076
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
2047
2077
|
return (False, YYYYMMDD_FORMAT_REQUIRED)
|
|
2048
2078
|
|
|
2049
2079
|
def getRowFilterTimeOrDeltaFromNow(argstr):
|
|
@@ -2057,14 +2087,14 @@ def getRowFilterTimeOrDeltaFromNow(argstr):
|
|
|
2057
2087
|
argstr = NEVER_TIME
|
|
2058
2088
|
elif YYYYMMDD_PATTERN.match(argstr):
|
|
2059
2089
|
try:
|
|
2060
|
-
dateTime =
|
|
2090
|
+
dateTime = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
2061
2091
|
except ValueError:
|
|
2062
2092
|
return (False, YYYYMMDD_FORMAT_REQUIRED)
|
|
2063
2093
|
argstr = ISOformatTimeStamp(dateTime.replace(tzinfo=GC.Values[GC.TIMEZONE]))
|
|
2064
2094
|
try:
|
|
2065
|
-
|
|
2095
|
+
arrow.get(argstr)
|
|
2066
2096
|
return (True, argstr.replace(' ', 'T'))
|
|
2067
|
-
except (
|
|
2097
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
2068
2098
|
return (False, YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
|
|
2069
2099
|
|
|
2070
2100
|
def mapQueryRelativeTimes(query, keywords):
|
|
@@ -2097,9 +2127,9 @@ class StartEndTime():
|
|
|
2097
2127
|
self.endDateTime, _, self.endTime = self._getValueOrDeltaFromNow(True)
|
|
2098
2128
|
elif myarg == 'yesterday':
|
|
2099
2129
|
currDate = todaysDate()
|
|
2100
|
-
self.startDateTime = currDate
|
|
2130
|
+
self.startDateTime = currDate.shift(days=-1)
|
|
2101
2131
|
self.startTime = ISOformatTimeStamp(self.startDateTime)
|
|
2102
|
-
self.endDateTime = currDate
|
|
2132
|
+
self.endDateTime = currDate.shift(seconds=-1)
|
|
2103
2133
|
self.endTime = ISOformatTimeStamp(self.endDateTime)
|
|
2104
2134
|
elif myarg == 'today':
|
|
2105
2135
|
currDate = todaysDate()
|
|
@@ -2114,12 +2144,12 @@ class StartEndTime():
|
|
|
2114
2144
|
else:
|
|
2115
2145
|
firstMonth = getInteger(minVal=1, maxVal=6)
|
|
2116
2146
|
currDate = todaysDate()
|
|
2117
|
-
self.startDateTime = currDate
|
|
2147
|
+
self.startDateTime = currDate.replace(day=1, hour=0, minute=0, second=0, microsecond=0).shift(months=-firstMonth)
|
|
2118
2148
|
self.startTime = ISOformatTimeStamp(self.startDateTime)
|
|
2119
2149
|
if myarg == 'thismonth':
|
|
2120
2150
|
self.endDateTime = todaysTime()
|
|
2121
2151
|
else:
|
|
2122
|
-
self.endDateTime = currDate
|
|
2152
|
+
self.endDateTime = currDate.replace(day=1, hour=23, minute=59, second=59, microsecond=0).shift(days=-1)
|
|
2123
2153
|
self.endTime = ISOformatTimeStamp(self.endDateTime)
|
|
2124
2154
|
if self.startDateTime and self.endDateTime and self.endDateTime < self.startDateTime:
|
|
2125
2155
|
Cmd.Backup()
|
|
@@ -2335,7 +2365,7 @@ def formatLocalTime(dateTimeStr):
|
|
|
2335
2365
|
if dateTimeStr in {NEVER_TIME, NEVER_TIME_NOMS}:
|
|
2336
2366
|
return GC.Values[GC.NEVER_TIME]
|
|
2337
2367
|
try:
|
|
2338
|
-
timestamp
|
|
2368
|
+
timestamp = arrow.get(dateTimeStr)
|
|
2339
2369
|
if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
|
|
2340
2370
|
if GM.Globals[GM.CONVERT_TO_LOCAL_TIME]:
|
|
2341
2371
|
return ISOformatTimeStamp(timestamp.astimezone(GC.Values[GC.TIMEZONE]))
|
|
@@ -2343,27 +2373,27 @@ def formatLocalTime(dateTimeStr):
|
|
|
2343
2373
|
if GM.Globals[GM.CONVERT_TO_LOCAL_TIME]:
|
|
2344
2374
|
return timestamp.astimezone(GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
|
|
2345
2375
|
return timestamp.strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
|
|
2346
|
-
except (
|
|
2376
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
2347
2377
|
return dateTimeStr
|
|
2348
2378
|
|
|
2349
2379
|
def formatLocalSecondsTimestamp(timestamp):
|
|
2350
2380
|
if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
|
|
2351
|
-
return ISOformatTimeStamp(
|
|
2352
|
-
return
|
|
2381
|
+
return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]))
|
|
2382
|
+
return arrow.Arrow.fromtimestamp(int(timestamp), GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
|
|
2353
2383
|
|
|
2354
2384
|
def formatLocalTimestamp(timestamp):
|
|
2355
2385
|
if not GC.Values[GC.OUTPUT_TIMEFORMAT]:
|
|
2356
|
-
return ISOformatTimeStamp(
|
|
2357
|
-
return
|
|
2386
|
+
return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]))
|
|
2387
|
+
return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_TIMEFORMAT])
|
|
2358
2388
|
|
|
2359
2389
|
def formatLocalTimestampUTC(timestamp):
|
|
2360
|
-
return ISOformatTimeStamp(
|
|
2390
|
+
return ISOformatTimeStamp(arrow.Arrow.fromtimestamp(int(timestamp)//1000, 'UTC'))
|
|
2361
2391
|
|
|
2362
2392
|
def formatLocalDatestamp(timestamp):
|
|
2363
2393
|
try:
|
|
2364
2394
|
if not GC.Values[GC.OUTPUT_DATEFORMAT]:
|
|
2365
|
-
return
|
|
2366
|
-
return
|
|
2395
|
+
return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(YYYYMMDD_FORMAT)
|
|
2396
|
+
return arrow.Arrow.fromtimestamp(int(timestamp)//1000, GC.Values[GC.TIMEZONE]).strftime(GC.Values[GC.OUTPUT_DATEFORMAT])
|
|
2367
2397
|
except OverflowError:
|
|
2368
2398
|
return NEVER_DATE
|
|
2369
2399
|
|
|
@@ -3050,9 +3080,13 @@ def getGDocData(gformat):
|
|
|
3050
3080
|
mimeType = GDOC_FORMAT_MIME_TYPES[gformat]
|
|
3051
3081
|
user = getEmailAddress()
|
|
3052
3082
|
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
|
3053
|
-
|
|
3083
|
+
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
|
3084
|
+
_, drive = buildGAPIServiceObject(API.DRIVE3, user)
|
|
3085
|
+
else:
|
|
3086
|
+
drive = buildGAPIObject(API.DRIVE3)
|
|
3054
3087
|
if not drive:
|
|
3055
3088
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
|
3089
|
+
_, _, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity, drive=drive)
|
|
3056
3090
|
if jcount == 0:
|
|
3057
3091
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
|
3058
3092
|
if jcount > 1:
|
|
@@ -3106,14 +3140,21 @@ def getGSheetData():
|
|
|
3106
3140
|
user = getEmailAddress()
|
|
3107
3141
|
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
|
3108
3142
|
sheetEntity = getSheetEntity(False)
|
|
3109
|
-
|
|
3143
|
+
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
|
3144
|
+
user, drive = buildGAPIServiceObject(API.DRIVE3, user)
|
|
3145
|
+
else:
|
|
3146
|
+
drive = buildGAPIObject(API.DRIVE3)
|
|
3110
3147
|
if not drive:
|
|
3111
3148
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
|
3149
|
+
_, _, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity, drive=drive)
|
|
3112
3150
|
if jcount == 0:
|
|
3113
3151
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
|
3114
3152
|
if jcount > 1:
|
|
3115
3153
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.MULTIPLE_ENTITIES_FOUND.format(Ent.Plural(Ent.DRIVE_FILE), jcount, ','.join(fileIdEntity['list'])))
|
|
3116
|
-
|
|
3154
|
+
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
|
3155
|
+
_, sheet = buildGAPIServiceObject(API.SHEETS, user)
|
|
3156
|
+
else:
|
|
3157
|
+
sheet = buildGAPIObject(API.SHEETS)
|
|
3117
3158
|
if not sheet:
|
|
3118
3159
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
|
3119
3160
|
fileId = fileIdEntity['list'][0]
|
|
@@ -3706,19 +3747,19 @@ def SetGlobalVariables():
|
|
|
3706
3747
|
return stringlist
|
|
3707
3748
|
|
|
3708
3749
|
def _getCfgTimezone(sectionName, itemName):
|
|
3709
|
-
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName)
|
|
3710
|
-
if value
|
|
3750
|
+
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName))
|
|
3751
|
+
if value.lower() in {'utc', 'z'}:
|
|
3711
3752
|
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
|
|
3712
|
-
return
|
|
3753
|
+
return arrow.now('utc').tzinfo
|
|
3713
3754
|
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = True
|
|
3714
|
-
if value == 'local':
|
|
3715
|
-
return
|
|
3755
|
+
if value.lower() == 'local':
|
|
3756
|
+
return arrow.now(value).tzinfo
|
|
3716
3757
|
try:
|
|
3717
|
-
return
|
|
3718
|
-
except (
|
|
3758
|
+
return arrow.now(value).tzinfo
|
|
3759
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
3719
3760
|
_printValueError(sectionName, itemName, value, f'{Msg.EXPECTED}: {TIMEZONE_FORMAT_REQUIRED}')
|
|
3720
3761
|
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
|
|
3721
|
-
return
|
|
3762
|
+
return arrow.now('utc').tzinfo
|
|
3722
3763
|
|
|
3723
3764
|
def _getCfgDirectory(sectionName, itemName):
|
|
3724
3765
|
dirPath = os.path.expanduser(_stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName)))
|
|
@@ -3736,10 +3777,7 @@ def SetGlobalVariables():
|
|
|
3736
3777
|
if value and not os.path.isabs(value):
|
|
3737
3778
|
value = os.path.expanduser(os.path.join(_getCfgDirectory(sectionName, GC.CONFIG_DIR), value))
|
|
3738
3779
|
elif not value and itemName == GC.CACERTS_PEM:
|
|
3739
|
-
|
|
3740
|
-
value = os.path.join(sys._MEIPASS, GC.FN_CACERTS_PEM) #pylint: disable=no-member
|
|
3741
|
-
else:
|
|
3742
|
-
value = os.path.join(GM.Globals[GM.GAM_PATH], GC.FN_CACERTS_PEM)
|
|
3780
|
+
value = os.path.join(GM.Globals[GM.GAM_PATH], GC.FN_CACERTS_PEM)
|
|
3743
3781
|
return value
|
|
3744
3782
|
|
|
3745
3783
|
def _readGamCfgFile(config, fileName):
|
|
@@ -4047,7 +4085,7 @@ def SetGlobalVariables():
|
|
|
4047
4085
|
GC.Values[itemName] = _getCfgDirectory(sectionName, itemName)
|
|
4048
4086
|
elif varType == GC.TYPE_TIMEZONE:
|
|
4049
4087
|
GC.Values[itemName] = _getCfgTimezone(sectionName, itemName)
|
|
4050
|
-
GM.Globals[GM.DATETIME_NOW] =
|
|
4088
|
+
GM.Globals[GM.DATETIME_NOW] = arrow.now(GC.Values[GC.TIMEZONE])
|
|
4051
4089
|
# Everything else except row filters
|
|
4052
4090
|
for itemName, itemEntry in sorted(GC.VAR_INFO.items()):
|
|
4053
4091
|
varType = itemEntry[GC.VAR_TYPE]
|
|
@@ -4117,6 +4155,8 @@ def SetGlobalVariables():
|
|
|
4117
4155
|
GM.Globals[GM.OAUTH2_TXT_LOCK] = f'{GC.Values[GC.OAUTH2_TXT]}.lock'
|
|
4118
4156
|
# Override httplib2 settings
|
|
4119
4157
|
httplib2.debuglevel = GC.Values[GC.DEBUG_LEVEL]
|
|
4158
|
+
# Use our own print function for http.client so we can redact and cleanup
|
|
4159
|
+
http.client.print = redactable_debug_print
|
|
4120
4160
|
# Reset global variables if required
|
|
4121
4161
|
if prevExtraArgsTxt != GC.Values[GC.EXTRA_ARGS]:
|
|
4122
4162
|
GM.Globals[GM.EXTRA_ARGS_LIST] = [('prettyPrint', GC.Values[GC.DEBUG_LEVEL] > 0)]
|
|
@@ -4393,12 +4433,11 @@ _DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
|
|
4393
4433
|
class signjwtJWTCredentials(google.auth.jwt.Credentials):
|
|
4394
4434
|
''' Class used for DASA '''
|
|
4395
4435
|
def _make_jwt(self):
|
|
4396
|
-
now =
|
|
4397
|
-
|
|
4398
|
-
expiry = now + lifetime
|
|
4436
|
+
now = arrow.utcnow()
|
|
4437
|
+
expiry = now.shift(seconds=self._token_lifetime)
|
|
4399
4438
|
payload = {
|
|
4400
|
-
"iat":
|
|
4401
|
-
"exp":
|
|
4439
|
+
"iat": now.int_timestamp,
|
|
4440
|
+
"exp": expiry.int_timestamp,
|
|
4402
4441
|
"iss": self._issuer,
|
|
4403
4442
|
"sub": self._subject,
|
|
4404
4443
|
}
|
|
@@ -4406,7 +4445,7 @@ class signjwtJWTCredentials(google.auth.jwt.Credentials):
|
|
|
4406
4445
|
payload["aud"] = self._audience
|
|
4407
4446
|
payload.update(self._additional_claims)
|
|
4408
4447
|
jwt = self._signer.sign(payload)
|
|
4409
|
-
return jwt, expiry
|
|
4448
|
+
return jwt, expiry.naive
|
|
4410
4449
|
|
|
4411
4450
|
# Some Workforce Identity Federation endpoints such as GitHub Actions
|
|
4412
4451
|
# only allow TLS 1.2 as of April 2023.
|
|
@@ -4419,15 +4458,14 @@ class signjwtCredentials(google.oauth2.service_account.Credentials):
|
|
|
4419
4458
|
''' Class used for DwD '''
|
|
4420
4459
|
|
|
4421
4460
|
def _make_authorization_grant_assertion(self):
|
|
4422
|
-
now =
|
|
4423
|
-
|
|
4424
|
-
expiry = now + lifetime
|
|
4461
|
+
now = arrow.utcnow()
|
|
4462
|
+
expiry = now.shift(seconds=_DEFAULT_TOKEN_LIFETIME_SECS)
|
|
4425
4463
|
payload = {
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4464
|
+
"iat": now.int_timestamp,
|
|
4465
|
+
"exp": expiry.int_timestamp,
|
|
4466
|
+
"iss": self._service_account_email,
|
|
4467
|
+
"aud": API.GOOGLE_OAUTH2_TOKEN_ENDPOINT,
|
|
4468
|
+
"scope": google.auth._helpers.scopes_to_string(self._scopes or ()),
|
|
4431
4469
|
}
|
|
4432
4470
|
payload.update(self._additional_claims)
|
|
4433
4471
|
# The subject can be a user email for domain-wide delegation.
|
|
@@ -4541,7 +4579,7 @@ def getOauth2TxtCredentials(exitOnError=True, api=None, noDASA=False, refreshOnl
|
|
|
4541
4579
|
creds.token = jsonDict['access_token']
|
|
4542
4580
|
creds._id_token = jsonDict['id_token_jwt']
|
|
4543
4581
|
GM.Globals[GM.DECODED_ID_TOKEN] = jsonDict['id_token']
|
|
4544
|
-
creds.expiry =
|
|
4582
|
+
creds.expiry = arrow.Arrow.strptime(token_expiry, YYYYMMDDTHHMMSSZ_FORMAT, tzinfo='UTC').naive
|
|
4545
4583
|
return (not noScopes, creds)
|
|
4546
4584
|
if jsonDict and exitOnError:
|
|
4547
4585
|
invalidOauth2TxtExit(Msg.INVALID)
|
|
@@ -4770,7 +4808,7 @@ def getService(api, httpObj):
|
|
|
4770
4808
|
waitOnFailure(n, triesLimit, INVALID_JSON_RC, str(e))
|
|
4771
4809
|
continue
|
|
4772
4810
|
systemErrorExit(INVALID_JSON_RC, str(e))
|
|
4773
|
-
except (
|
|
4811
|
+
except (http.client.ResponseNotReady, OSError, googleapiclient.errors.HttpError) as e:
|
|
4774
4812
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
|
4775
4813
|
if n != triesLimit:
|
|
4776
4814
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
|
@@ -4805,10 +4843,7 @@ def defaultSvcAcctScopes():
|
|
|
4805
4843
|
saScopes[scope['api']].append(scope['scope'])
|
|
4806
4844
|
else:
|
|
4807
4845
|
saScopes[scope['api']].extend(scope['scope'])
|
|
4808
|
-
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
|
4809
4846
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
|
4810
|
-
saScopes[API.DRIVETD] = saScopes[API.DRIVE3]
|
|
4811
|
-
saScopes[API.SHEETSTD] = saScopes[API.SHEETS]
|
|
4812
4847
|
return saScopes
|
|
4813
4848
|
|
|
4814
4849
|
def _getSvcAcctData():
|
|
@@ -5086,7 +5121,7 @@ def callGData(service, function,
|
|
|
5086
5121
|
e = e.args[0]
|
|
5087
5122
|
handleOAuthTokenError(e, GDATA.SERVICE_NOT_APPLICABLE in throwErrors)
|
|
5088
5123
|
raise GDATA.ERROR_CODE_EXCEPTION_MAP[GDATA.SERVICE_NOT_APPLICABLE](str(e))
|
|
5089
|
-
except (
|
|
5124
|
+
except (http.client.ResponseNotReady, OSError) as e:
|
|
5090
5125
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
|
5091
5126
|
if n != triesLimit:
|
|
5092
5127
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
|
@@ -5396,7 +5431,7 @@ def callGAPI(service, function,
|
|
|
5396
5431
|
e = e.args[0]
|
|
5397
5432
|
handleOAuthTokenError(e, GAPI.SERVICE_NOT_AVAILABLE in throwReasons)
|
|
5398
5433
|
raise GAPI.REASON_EXCEPTION_MAP[GAPI.SERVICE_NOT_AVAILABLE](str(e))
|
|
5399
|
-
except (
|
|
5434
|
+
except (http.client.ResponseNotReady, OSError) as e:
|
|
5400
5435
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
|
5401
5436
|
if n != triesLimit:
|
|
5402
5437
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
|
@@ -5615,6 +5650,12 @@ def getSaUser(user):
|
|
|
5615
5650
|
GM.Globals[GM.CURRENT_CLIENT_API_SCOPES] = currentClientAPIScopes
|
|
5616
5651
|
return userEmail
|
|
5617
5652
|
|
|
5653
|
+
def chooseSaAPI(api1, api2):
|
|
5654
|
+
_getSvcAcctData()
|
|
5655
|
+
if api1 in GM.Globals[GM.SVCACCT_SCOPES]:
|
|
5656
|
+
return api1
|
|
5657
|
+
return api2
|
|
5658
|
+
|
|
5618
5659
|
def buildGAPIServiceObject(api, user, i=0, count=0, displayError=True):
|
|
5619
5660
|
userEmail = getSaUser(user)
|
|
5620
5661
|
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
|
@@ -6255,63 +6296,77 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6255
6296
|
_showInvalidEntity(Ent.USER, user)
|
|
6256
6297
|
if GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]:
|
|
6257
6298
|
return entityList
|
|
6258
|
-
elif entityType in
|
|
6299
|
+
elif entityType in Cmd.ALL_USER_ENTITY_TYPES:
|
|
6259
6300
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6260
|
-
if entityType == Cmd.ENTITY_ALL_USERS and isSuspended is not None:
|
|
6261
|
-
|
|
6301
|
+
if entityType == Cmd.ENTITY_ALL_USERS and ((isSuspended is not None) or (isArchived is not None)):
|
|
6302
|
+
if isSuspended is not None:
|
|
6303
|
+
query = f'isSuspended={isSuspended}'
|
|
6304
|
+
if isArchived is not None:
|
|
6305
|
+
query += f' isArchived={isArchived}'
|
|
6306
|
+
else:
|
|
6307
|
+
query = f'isArchived={isArchived}'
|
|
6262
6308
|
else:
|
|
6263
6309
|
query = Cmd.ALL_USERS_QUERY_MAP[entityType]
|
|
6264
|
-
printGettingAllAccountEntities(Ent.USER)
|
|
6310
|
+
printGettingAllAccountEntities(Ent.USER, query=query)
|
|
6265
6311
|
try:
|
|
6266
6312
|
result = callGAPIpages(cd.users(), 'list', 'users',
|
|
6267
6313
|
pageMessage=getPageMessage(),
|
|
6268
6314
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
6269
6315
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
6270
|
-
customer=GC.Values[GC.CUSTOMER_ID],
|
|
6271
|
-
|
|
6316
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email',
|
|
6317
|
+
fields='nextPageToken,users(primaryEmail)',
|
|
6272
6318
|
maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
6273
6319
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
|
6274
6320
|
accessErrorExit(cd)
|
|
6275
|
-
entityList = [user['primaryEmail'] for user in result
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
query
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6321
|
+
entityList = [user['primaryEmail'] for user in result]
|
|
6322
|
+
elif entityType == Cmd.ENTITY_ALL_USERS_ARCH_OR_SUSP:
|
|
6323
|
+
cd = buildGAPIObject(API.DIRECTORY)
|
|
6324
|
+
for query in ['isSuspended=True', 'isArchived=True']:
|
|
6325
|
+
printGettingAllAccountEntities(Ent.USER, query)
|
|
6326
|
+
try:
|
|
6327
|
+
result = callGAPIpages(cd.users(), 'list', 'users',
|
|
6328
|
+
pageMessage=getPageMessage(),
|
|
6329
|
+
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
6330
|
+
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
6331
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=query, orderBy='email',
|
|
6332
|
+
fields='nextPageToken,users(primaryEmail)',
|
|
6333
|
+
maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
6334
|
+
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
|
6335
|
+
accessErrorExit(cd)
|
|
6336
|
+
entitySet |= {user['primaryEmail'] for user in result}
|
|
6337
|
+
entityList = sorted(list(entitySet))
|
|
6338
|
+
elif entityType in Cmd.DOMAIN_ENTITY_TYPES:
|
|
6339
|
+
if entityType == Cmd.ENTITY_DOMAINS and ((isSuspended is not None) or (isArchived is not None)):
|
|
6340
|
+
if isSuspended is not None:
|
|
6341
|
+
query = f'isSuspended={isSuspended}'
|
|
6342
|
+
if isArchived is not None:
|
|
6343
|
+
query += f' isArchived={isArchived}'
|
|
6344
|
+
else:
|
|
6345
|
+
query = f'isArchived={isArchived}'
|
|
6284
6346
|
else:
|
|
6285
|
-
query =
|
|
6347
|
+
query = Cmd.DOMAINS_QUERY_MAP[entityType]
|
|
6286
6348
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6287
6349
|
domains = convertEntityToList(entity)
|
|
6288
6350
|
for domain in domains:
|
|
6289
|
-
printGettingAllEntityItemsForWhom(Ent.USER, domain, entityType=Ent.DOMAIN)
|
|
6351
|
+
printGettingAllEntityItemsForWhom(Ent.USER, domain, query=query, entityType=Ent.DOMAIN)
|
|
6290
6352
|
try:
|
|
6291
6353
|
result = callGAPIpages(cd.users(), 'list', 'users',
|
|
6292
6354
|
pageMessage=getPageMessageForWhom(),
|
|
6293
6355
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND, GAPI.FORBIDDEN],
|
|
6294
6356
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
6295
|
-
domain=domain,
|
|
6296
|
-
|
|
6357
|
+
domain=domain, query=query, orderBy='email',
|
|
6358
|
+
fields='nextPageToken,users(primaryEmail)',
|
|
6297
6359
|
maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
6298
6360
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden):
|
|
6299
6361
|
checkEntityDNEorAccessErrorExit(cd, Ent.DOMAIN, domain)
|
|
6300
6362
|
_incrEntityDoesNotExist(Ent.DOMAIN)
|
|
6301
6363
|
continue
|
|
6302
|
-
entityList
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS,
|
|
6306
|
-
Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP,
|
|
6307
|
-
Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE}:
|
|
6308
|
-
if entityType in {Cmd.ENTITY_GROUP_NS, Cmd.ENTITY_GROUPS_NS}:
|
|
6309
|
-
isSuspended = False
|
|
6310
|
-
elif entityType in {Cmd.ENTITY_GROUP_SUSP, Cmd.ENTITY_GROUPS_SUSP}:
|
|
6311
|
-
isSuspended = True
|
|
6364
|
+
entityList.extend([user['primaryEmail'] for user in result])
|
|
6365
|
+
elif entityType in Cmd.GROUP_ENTITY_TYPES or entityType in Cmd.GROUPS_ENTITY_TYPES:
|
|
6366
|
+
isArchived, isSuspended = Cmd.GROUPS_QUERY_MAP.get(entityType, (isArchived, isSuspended))
|
|
6312
6367
|
includeDerivedMembership = entityType in {Cmd.ENTITY_GROUP_INDE, Cmd.ENTITY_GROUPS_INDE}
|
|
6313
6368
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6314
|
-
groups = convertEntityToList(entity, nonListEntityType=entityType in
|
|
6369
|
+
groups = convertEntityToList(entity, nonListEntityType=entityType in Cmd.GROUP_ENTITY_TYPES)
|
|
6315
6370
|
for group in groups:
|
|
6316
6371
|
if validateEmailAddressOrUID(group, checkPeople=False):
|
|
6317
6372
|
group = normalizeEmailAddressOrUID(group)
|
|
@@ -6337,11 +6392,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6337
6392
|
entityList.append(email)
|
|
6338
6393
|
else:
|
|
6339
6394
|
_showInvalidEntity(Ent.GROUP, group)
|
|
6340
|
-
elif entityType in
|
|
6341
|
-
|
|
6342
|
-
isSuspended = False
|
|
6343
|
-
elif entityType == Cmd.ENTITY_GROUP_USERS_SUSP:
|
|
6344
|
-
isSuspended = True
|
|
6395
|
+
elif entityType in Cmd.GROUP_USERS_ENTITY_TYPES:
|
|
6396
|
+
isArchived, isSuspended = Cmd.GROUP_USERS_QUERY_MAP.get(entityType, (isArchived, isSuspended))
|
|
6345
6397
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6346
6398
|
groups = convertEntityToList(entity)
|
|
6347
6399
|
includeDerivedMembership = False
|
|
@@ -6432,21 +6484,13 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6432
6484
|
_addCIGroupUsersToUsers(name, groupEmail, recursive)
|
|
6433
6485
|
else:
|
|
6434
6486
|
_showInvalidEntity(Ent.GROUP, group)
|
|
6435
|
-
elif entityType in
|
|
6436
|
-
|
|
6437
|
-
Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}:
|
|
6438
|
-
if entityType in {Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_AND_CHILDREN_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS}:
|
|
6439
|
-
isSuspended = False
|
|
6440
|
-
elif entityType in {Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP}:
|
|
6441
|
-
isSuspended = True
|
|
6487
|
+
elif entityType in Cmd.OU_ENTITY_TYPES or entityType in Cmd.OUS_ENTITY_TYPES:
|
|
6488
|
+
isArchived, isSuspended = Cmd.OU_QUERY_MAP.get(entityType, (isArchived, isSuspended))
|
|
6442
6489
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6443
|
-
ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in
|
|
6444
|
-
|
|
6445
|
-
Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OU_AND_CHILDREN_SUSP})
|
|
6446
|
-
directlyInOU = entityType in {Cmd.ENTITY_OU, Cmd.ENTITY_OUS, Cmd.ENTITY_OU_NS, Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OU_SUSP, Cmd.ENTITY_OUS_SUSP}
|
|
6490
|
+
ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.OU_ENTITY_TYPES)
|
|
6491
|
+
directlyInOU = entityType in Cmd.OU_DIRECT_ENTITY_TYPES
|
|
6447
6492
|
qualifier = Msg.DIRECTLY_IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT)) if directlyInOU else Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT))
|
|
6448
|
-
fields = 'nextPageToken,users(primaryEmail,orgUnitPath
|
|
6449
|
-
prevLen = 0
|
|
6493
|
+
fields = 'nextPageToken,users(primaryEmail,orgUnitPath)' if directlyInOU else 'nextPageToken,users(primaryEmail)'
|
|
6450
6494
|
for ou in ous:
|
|
6451
6495
|
ou = makeOrgUnitPathAbsolute(ou)
|
|
6452
6496
|
if ou.startswith('id:'):
|
|
@@ -6471,22 +6515,17 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6471
6515
|
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
|
|
6472
6516
|
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
6473
6517
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
6474
|
-
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended), orderBy='email',
|
|
6518
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(ou, isSuspended, isArchived), orderBy='email',
|
|
6475
6519
|
fields=fields, maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
6476
6520
|
for users in feed:
|
|
6477
6521
|
if directlyInOU:
|
|
6478
6522
|
for user in users:
|
|
6479
|
-
if ouLower == user.get('orgUnitPath', '').lower()
|
|
6523
|
+
if ouLower == user.get('orgUnitPath', '').lower():
|
|
6480
6524
|
usersInOU += 1
|
|
6481
6525
|
entityList.append(user['primaryEmail'])
|
|
6482
|
-
|
|
6526
|
+
else:
|
|
6483
6527
|
entityList.extend([user['primaryEmail'] for user in users])
|
|
6484
6528
|
usersInOU += len(users)
|
|
6485
|
-
else:
|
|
6486
|
-
for user in users:
|
|
6487
|
-
if isArchived == user['archived']:
|
|
6488
|
-
usersInOU += 1
|
|
6489
|
-
entityList.append(user['primaryEmail'])
|
|
6490
6529
|
setGettingAllEntityItemsForWhom(Ent.USER, ou, qualifier=qualifier)
|
|
6491
6530
|
printGotEntityItemsForWhom(usersInOU)
|
|
6492
6531
|
except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError, GAPI.badRequest,
|
|
@@ -6496,7 +6535,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6496
6535
|
elif entityType in {Cmd.ENTITY_QUERY, Cmd.ENTITY_QUERIES}:
|
|
6497
6536
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6498
6537
|
queries = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType == Cmd.ENTITY_QUERY)
|
|
6499
|
-
prevLen = 0
|
|
6500
6538
|
for query in queries:
|
|
6501
6539
|
printGettingAllAccountEntities(Ent.USER, query)
|
|
6502
6540
|
try:
|
|
@@ -6520,9 +6558,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6520
6558
|
email not in entitySet):
|
|
6521
6559
|
entitySet.add(email)
|
|
6522
6560
|
entityList.append(email)
|
|
6523
|
-
totalLen = len(entityList)
|
|
6524
|
-
printGotAccountEntities(totalLen-prevLen)
|
|
6525
|
-
prevLen = totalLen
|
|
6526
6561
|
elif entityType == Cmd.ENTITY_LICENSES:
|
|
6527
6562
|
skusList = []
|
|
6528
6563
|
for item in entity.split(','):
|
|
@@ -6581,8 +6616,7 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6581
6616
|
ClientAPIAccessDeniedExit(str(e))
|
|
6582
6617
|
elif entityType == Cmd.ENTITY_CROS:
|
|
6583
6618
|
buildGAPIObject(API.DIRECTORY)
|
|
6584
|
-
|
|
6585
|
-
for deviceId in result:
|
|
6619
|
+
for deviceId in convertEntityToList(entity):
|
|
6586
6620
|
if deviceId not in entitySet:
|
|
6587
6621
|
entitySet.add(deviceId)
|
|
6588
6622
|
entityList.append(deviceId)
|
|
@@ -6606,7 +6640,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6606
6640
|
nonListEntityType=entityType == Cmd.ENTITY_CROS_QUERY)
|
|
6607
6641
|
if entityType == Cmd.ENTITY_CROS_SN:
|
|
6608
6642
|
queries = [f'id:{query}' for query in queries]
|
|
6609
|
-
prevLen = 0
|
|
6610
6643
|
for query in queries:
|
|
6611
6644
|
printGettingAllAccountEntities(Ent.CROS_DEVICE, query)
|
|
6612
6645
|
try:
|
|
@@ -6627,25 +6660,15 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6627
6660
|
if deviceId not in entitySet:
|
|
6628
6661
|
entitySet.add(deviceId)
|
|
6629
6662
|
entityList.append(deviceId)
|
|
6630
|
-
|
|
6631
|
-
printGotAccountEntities(totalLen-prevLen)
|
|
6632
|
-
prevLen = totalLen
|
|
6633
|
-
elif entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN, Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN,
|
|
6634
|
-
Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
|
|
6635
|
-
Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}:
|
|
6663
|
+
elif entityType in Cmd.CROS_OU_ENTITY_TYPES or entityType in Cmd.CROS_OUS_ENTITY_TYPES:
|
|
6636
6664
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
6637
|
-
ous = convertEntityToList(entity, shlexSplit=True,
|
|
6638
|
-
nonListEntityType=entityType in {Cmd.ENTITY_CROS_OU, Cmd.ENTITY_CROS_OU_AND_CHILDREN,
|
|
6639
|
-
Cmd.ENTITY_CROS_OU_QUERY, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY,
|
|
6640
|
-
Cmd.ENTITY_CROS_OU_QUERIES, Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES})
|
|
6665
|
+
ous = convertEntityToList(entity, shlexSplit=True, nonListEntityType=entityType in Cmd.CROS_OU_ENTITY_TYPES)
|
|
6641
6666
|
numOus = len(ous)
|
|
6642
|
-
includeChildOrgunits = entityType in
|
|
6643
|
-
Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERY, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
|
|
6644
|
-
Cmd.ENTITY_CROS_OU_AND_CHILDREN_QUERIES, Cmd.ENTITY_CROS_OUS_AND_CHILDREN_QUERIES}
|
|
6667
|
+
includeChildOrgunits = entityType in Cmd.CROS_OU_CHILDREN_ENTITY_TYPES
|
|
6645
6668
|
allQualifier = Msg.DIRECTLY_IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus)) if not includeChildOrgunits else Msg.IN_THE.format(Ent.Choose(Ent.ORGANIZATIONAL_UNIT, numOus))
|
|
6646
|
-
if entityType in
|
|
6669
|
+
if entityType in Cmd.CROS_OU_QUERY_ENTITY_TYPES:
|
|
6647
6670
|
queries = getQueries('query')
|
|
6648
|
-
elif entityType in
|
|
6671
|
+
elif entityType in Cmd.CROS_OU_QUERIES_ENTITY_TYPES:
|
|
6649
6672
|
queries = getQueries('queries')
|
|
6650
6673
|
else:
|
|
6651
6674
|
queries = [None]
|
|
@@ -6718,7 +6741,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6718
6741
|
customerId = _getCustomerIdNoC()
|
|
6719
6742
|
queries = convertEntityToList(entity, shlexSplit=entityType == Cmd.ENTITY_BROWSER_QUERIES,
|
|
6720
6743
|
nonListEntityType=entityType == Cmd.ENTITY_BROWSER_QUERY)
|
|
6721
|
-
prevLen = 0
|
|
6722
6744
|
for query in queries:
|
|
6723
6745
|
printGettingAllAccountEntities(Ent.CHROME_BROWSER, query)
|
|
6724
6746
|
try:
|
|
@@ -6738,9 +6760,6 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
6738
6760
|
if deviceId not in entitySet:
|
|
6739
6761
|
entitySet.add(deviceId)
|
|
6740
6762
|
entityList.append(deviceId)
|
|
6741
|
-
totalLen = len(entityList)
|
|
6742
|
-
printGotAccountEntities(totalLen-prevLen)
|
|
6743
|
-
prevLen = totalLen
|
|
6744
6763
|
else:
|
|
6745
6764
|
systemErrorExit(UNKNOWN_ERROR_RC, 'getItemsToModify coding error')
|
|
6746
6765
|
for errorType in [ENTITY_ERROR_DNE, ENTITY_ERROR_INVALID]:
|
|
@@ -7053,10 +7072,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
|
|
|
7053
7072
|
choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES
|
|
7054
7073
|
entityType = mapEntityType(getChoice(choices), typeMap)
|
|
7055
7074
|
return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS,
|
|
7056
|
-
getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in
|
|
7057
|
-
Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS,
|
|
7058
|
-
Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP,
|
|
7059
|
-
Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN])))
|
|
7075
|
+
getItemsToModify(entityType, getEntitiesFromFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN})))
|
|
7060
7076
|
if entitySelector == Cmd.ENTITY_SELECTOR_CSVDATAFILE:
|
|
7061
7077
|
if userAllowed:
|
|
7062
7078
|
choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS]
|
|
@@ -7064,10 +7080,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
|
|
|
7064
7080
|
choices += Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES
|
|
7065
7081
|
entityType = mapEntityType(getChoice(choices), typeMap)
|
|
7066
7082
|
return (Cmd.ENTITY_USERS if entityType not in Cmd.CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES else Cmd.ENTITY_CROS,
|
|
7067
|
-
getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in
|
|
7068
|
-
Cmd.ENTITY_OUS_NS, Cmd.ENTITY_OUS_AND_CHILDREN_NS,
|
|
7069
|
-
Cmd.ENTITY_OUS_SUSP, Cmd.ENTITY_OUS_AND_CHILDREN_SUSP,
|
|
7070
|
-
Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN])))
|
|
7083
|
+
getItemsToModify(entityType, getEntitiesFromCSVFile(shlexSplit=entityType in Cmd.OUS_ENTITY_TYPES | {Cmd.ENTITY_CROS_OUS, Cmd.ENTITY_CROS_OUS_AND_CHILDREN})))
|
|
7071
7084
|
if entitySelector == Cmd.ENTITY_SELECTOR_CSVKMD:
|
|
7072
7085
|
if userAllowed:
|
|
7073
7086
|
choices += Cmd.USER_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY] else [Cmd.ENTITY_USERS]
|
|
@@ -7110,10 +7123,7 @@ def getEntityToModify(defaultEntityType=None, browserAllowed=False, crosAllowed=
|
|
|
7110
7123
|
if not GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY]:
|
|
7111
7124
|
buildGAPIObject(API.DIRECTORY)
|
|
7112
7125
|
if entityClass == Cmd.ENTITY_USERS:
|
|
7113
|
-
if entityType in
|
|
7114
|
-
Cmd.ENTITY_GROUP_USERS_NS, Cmd.ENTITY_GROUP_USERS_SUSP,
|
|
7115
|
-
Cmd.ENTITY_GROUP_USERS_SELECT,
|
|
7116
|
-
Cmd.ENTITY_CIGROUP_USERS]:
|
|
7126
|
+
if entityType in Cmd.GROUP_USERS_ENTITY_TYPES | {Cmd.ENTITY_CIGROUP_USERS}:
|
|
7117
7127
|
# Skip over sub-arguments
|
|
7118
7128
|
while Cmd.ArgumentsRemaining():
|
|
7119
7129
|
myarg = getArgument()
|
|
@@ -7431,15 +7441,15 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
|
|
|
7431
7441
|
def stripTimeFromDateTime(rowDate):
|
|
7432
7442
|
if YYYYMMDD_PATTERN.match(rowDate):
|
|
7433
7443
|
try:
|
|
7434
|
-
rowTime =
|
|
7444
|
+
rowTime = arrow.Arrow.strptime(rowDate, YYYYMMDD_FORMAT)
|
|
7435
7445
|
except ValueError:
|
|
7436
7446
|
return None
|
|
7437
7447
|
else:
|
|
7438
7448
|
try:
|
|
7439
|
-
rowTime
|
|
7440
|
-
except (
|
|
7449
|
+
rowTime = arrow.get(rowDate)
|
|
7450
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
7441
7451
|
return None
|
|
7442
|
-
return ISOformatTimeStamp(
|
|
7452
|
+
return ISOformatTimeStamp(arrow.Arrow(rowTime.year, rowTime.month, rowTime.day, tzinfo='UTC'))
|
|
7443
7453
|
|
|
7444
7454
|
def rowDateTimeFilterMatch(dateMode, op, filterDate):
|
|
7445
7455
|
def checkMatch(rowDate):
|
|
@@ -7501,8 +7511,8 @@ def RowFilterMatch(row, titlesList, rowFilter, rowFilterModeAll, rowDropFilter,
|
|
|
7501
7511
|
if YYYYMMDD_PATTERN.match(rowDate):
|
|
7502
7512
|
return None
|
|
7503
7513
|
try:
|
|
7504
|
-
rowTime
|
|
7505
|
-
except (
|
|
7514
|
+
rowTime = arrow.get(rowDate)
|
|
7515
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
7506
7516
|
return None
|
|
7507
7517
|
return f'{rowTime.hour:02d}:{rowTime.minute:02d}'
|
|
7508
7518
|
|
|
@@ -7905,6 +7915,13 @@ class CSVPrintFile():
|
|
|
7905
7915
|
if title not in self.titlesSet:
|
|
7906
7916
|
self.AddTitle(title)
|
|
7907
7917
|
|
|
7918
|
+
def InsertTitles(self, position, titles):
|
|
7919
|
+
for title in titles if isinstance(titles, list) else [titles]:
|
|
7920
|
+
if title not in self.titlesSet:
|
|
7921
|
+
self.titlesSet.add(title)
|
|
7922
|
+
self.titlesList.insert(position, title)
|
|
7923
|
+
position += 1
|
|
7924
|
+
|
|
7908
7925
|
def SetTitles(self, titles):
|
|
7909
7926
|
self.titlesSet = set()
|
|
7910
7927
|
self.titlesList = []
|
|
@@ -8060,7 +8077,7 @@ class CSVPrintFile():
|
|
|
8060
8077
|
|
|
8061
8078
|
def getDriveObject():
|
|
8062
8079
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
|
8063
|
-
_, drive = buildGAPIServiceObject(API.DRIVETD, self.todrive['user'])
|
|
8080
|
+
_, drive = buildGAPIServiceObject(chooseSaAPI(API.DRIVETD, API.DRIVE3), self.todrive['user'])
|
|
8064
8081
|
if not drive:
|
|
8065
8082
|
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
|
8066
8083
|
else:
|
|
@@ -8213,7 +8230,7 @@ class CSVPrintFile():
|
|
|
8213
8230
|
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
|
8214
8231
|
invalidTodriveFileIdExit([], f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}', tdfileidLocation)
|
|
8215
8232
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
|
8216
|
-
_, sheet = buildGAPIServiceObject(API.SHEETSTD, self.todrive['user'])
|
|
8233
|
+
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), self.todrive['user'])
|
|
8217
8234
|
if sheet is None:
|
|
8218
8235
|
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
|
8219
8236
|
else:
|
|
@@ -8703,10 +8720,10 @@ class CSVPrintFile():
|
|
|
8703
8720
|
sheetTitle = self.todrive['sheetEntity']['sheetTitle']
|
|
8704
8721
|
else:
|
|
8705
8722
|
sheetTitle = self.todrive['sheettitle']
|
|
8706
|
-
tdbasetime = tdtime =
|
|
8723
|
+
tdbasetime = tdtime = arrow.now(GC.Values[GC.TIMEZONE])
|
|
8707
8724
|
if self.todrive['daysoffset'] is not None or self.todrive['hoursoffset'] is not None:
|
|
8708
|
-
tdtime = tdbasetime
|
|
8709
|
-
|
|
8725
|
+
tdtime = tdbasetime.shift(days=-self.todrive['daysoffset'] if self.todrive['daysoffset'] is not None else 0,
|
|
8726
|
+
hours=-self.todrive['hoursoffset'] if self.todrive['hoursoffset'] is not None else 0)
|
|
8710
8727
|
if self.todrive['timestamp']:
|
|
8711
8728
|
if title:
|
|
8712
8729
|
title += ' - '
|
|
@@ -8716,8 +8733,8 @@ class CSVPrintFile():
|
|
|
8716
8733
|
title += tdtime.strftime(self.todrive['timeformat'])
|
|
8717
8734
|
if self.todrive['sheettimestamp']:
|
|
8718
8735
|
if self.todrive['sheetdaysoffset'] is not None or self.todrive['sheethoursoffset'] is not None:
|
|
8719
|
-
tdtime = tdbasetime
|
|
8720
|
-
|
|
8736
|
+
tdtime = tdbasetime.shift(days=-self.todrive['sheetdaysoffset'] if self.todrive['sheetdaysoffset'] is not None else 0,
|
|
8737
|
+
hours=-self.todrive['sheethoursoffset'] if self.todrive['sheethoursoffset'] is not None else 0)
|
|
8721
8738
|
if sheetTitle:
|
|
8722
8739
|
sheetTitle += ' - '
|
|
8723
8740
|
if not self.todrive['sheettimeformat']:
|
|
@@ -8726,7 +8743,7 @@ class CSVPrintFile():
|
|
|
8726
8743
|
sheetTitle += tdtime.strftime(self.todrive['sheettimeformat'])
|
|
8727
8744
|
action = Act.Get()
|
|
8728
8745
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
|
8729
|
-
user, drive = buildGAPIServiceObject(API.DRIVETD, self.todrive['user'])
|
|
8746
|
+
user, drive = buildGAPIServiceObject(chooseSaAPI(API.DRIVETD, API.DRIVE3), self.todrive['user'])
|
|
8730
8747
|
if not drive:
|
|
8731
8748
|
closeFile(csvFile)
|
|
8732
8749
|
return
|
|
@@ -8759,7 +8776,7 @@ class CSVPrintFile():
|
|
|
8759
8776
|
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
|
8760
8777
|
todriveCSVErrorExit(entityValueList, f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}')
|
|
8761
8778
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
|
8762
|
-
_, sheet = buildGAPIServiceObject(API.SHEETSTD, user)
|
|
8779
|
+
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), user)
|
|
8763
8780
|
if sheet is None:
|
|
8764
8781
|
return
|
|
8765
8782
|
else:
|
|
@@ -8907,7 +8924,7 @@ class CSVPrintFile():
|
|
|
8907
8924
|
(self.todrive['sheetEntity'] or self.todrive['locale'] or self.todrive['timeZone'] or
|
|
8908
8925
|
self.todrive['sheettitle'] or self.todrive['cellwrap'] or self.todrive['cellnumberformat'])):
|
|
8909
8926
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
|
8910
|
-
_, sheet = buildGAPIServiceObject(API.SHEETSTD, user)
|
|
8927
|
+
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), user)
|
|
8911
8928
|
if sheet is None:
|
|
8912
8929
|
return
|
|
8913
8930
|
else:
|
|
@@ -9160,12 +9177,12 @@ def flattenJSON(topStructure, flattened=None,
|
|
|
9160
9177
|
# Show a json object
|
|
9161
9178
|
def showJSON(showName, showValue, skipObjects=None, timeObjects=None,
|
|
9162
9179
|
simpleLists=None, dictObjectsKey=None, sortDictKeys=True):
|
|
9163
|
-
def _show(objectName, objectValue, subObjectKey, level, subSkipObjects):
|
|
9180
|
+
def _show(objectName, objectValue, subObjectKey, subObjectName, level, subSkipObjects):
|
|
9164
9181
|
if objectName in subSkipObjects:
|
|
9165
9182
|
return
|
|
9166
9183
|
if objectName is not None:
|
|
9167
9184
|
printJSONKey(objectName)
|
|
9168
|
-
|
|
9185
|
+
subObjectKey = dictObjectsKey.get(objectName)
|
|
9169
9186
|
if isinstance(objectValue, list):
|
|
9170
9187
|
if objectName in simpleLists:
|
|
9171
9188
|
printJSONValue(' '.join(objectValue))
|
|
@@ -9183,10 +9200,12 @@ def showJSON(showName, showValue, skipObjects=None, timeObjects=None,
|
|
|
9183
9200
|
if isinstance(subValue, (str, bool, float, int)):
|
|
9184
9201
|
printKeyValueList([subValue])
|
|
9185
9202
|
else:
|
|
9186
|
-
_show(None, subValue, subObjectKey, level+1, DEFAULT_SKIP_OBJECTS)
|
|
9203
|
+
_show(None, subValue, subObjectKey, objectName, level+1, DEFAULT_SKIP_OBJECTS)
|
|
9187
9204
|
if objectName is not None:
|
|
9188
9205
|
Ind.Decrement()
|
|
9189
9206
|
elif isinstance(objectValue, dict):
|
|
9207
|
+
if not subObjectKey:
|
|
9208
|
+
subObjectKey = dictObjectsKey.get(subObjectName)
|
|
9190
9209
|
indentAfterFirst = unindentAfterLast = False
|
|
9191
9210
|
if objectName is not None:
|
|
9192
9211
|
printBlankLine()
|
|
@@ -9194,13 +9213,23 @@ def showJSON(showName, showValue, skipObjects=None, timeObjects=None,
|
|
|
9194
9213
|
elif level > 0:
|
|
9195
9214
|
indentAfterFirst = unindentAfterLast = True
|
|
9196
9215
|
subObjects = sorted(objectValue) if sortDictKeys else objectValue.keys()
|
|
9197
|
-
if subObjectKey
|
|
9198
|
-
subObjects
|
|
9199
|
-
|
|
9200
|
-
|
|
9216
|
+
if subObjectKey:
|
|
9217
|
+
if subObjectKey in subObjects:
|
|
9218
|
+
subObjects.remove(subObjectKey)
|
|
9219
|
+
subObjects.insert(0, subObjectKey)
|
|
9220
|
+
subObjectKey = None
|
|
9221
|
+
elif subObjectName == 'permissions': # subObjectKey in displayName
|
|
9222
|
+
if 'id' in objectValue:
|
|
9223
|
+
if objectValue['id'] == 'anyone':
|
|
9224
|
+
objectValue[subObjectKey] = 'Anyone'
|
|
9225
|
+
elif objectValue['id'] == 'anyoneWithLink':
|
|
9226
|
+
objectValue[subObjectKey] = 'Anyone with Link'
|
|
9227
|
+
else:
|
|
9228
|
+
objectValue[subObjectKey] = objectValue['id']
|
|
9229
|
+
subObjects.insert(0, subObjectKey)
|
|
9201
9230
|
for subObject in subObjects:
|
|
9202
9231
|
if subObject not in subSkipObjects:
|
|
9203
|
-
_show(subObject, objectValue[subObject], subObjectKey, level+1, DEFAULT_SKIP_OBJECTS)
|
|
9232
|
+
_show(subObject, objectValue[subObject], subObjectKey, None, level+1, DEFAULT_SKIP_OBJECTS)
|
|
9204
9233
|
if indentAfterFirst:
|
|
9205
9234
|
Ind.Increment()
|
|
9206
9235
|
indentAfterFirst = False
|
|
@@ -9227,7 +9256,7 @@ def showJSON(showName, showValue, skipObjects=None, timeObjects=None,
|
|
|
9227
9256
|
timeObjects = timeObjects or set()
|
|
9228
9257
|
simpleLists = simpleLists or set()
|
|
9229
9258
|
dictObjectsKey = dictObjectsKey or {}
|
|
9230
|
-
_show(showName, showValue, None, 0, DEFAULT_SKIP_OBJECTS.union(skipObjects or set()))
|
|
9259
|
+
_show(showName, showValue, None, None, 0, DEFAULT_SKIP_OBJECTS.union(skipObjects or set()))
|
|
9231
9260
|
|
|
9232
9261
|
class FormatJSONQuoteChar():
|
|
9233
9262
|
|
|
@@ -9306,7 +9335,7 @@ def getLocalGoogleTimeOffset(testLocation=GOOGLE_TIMECHECK_LOCATION):
|
|
|
9306
9335
|
for prot in ['http', 'https']:
|
|
9307
9336
|
try:
|
|
9308
9337
|
headerData = httpObj.request(f'{prot}://'+testLocation, 'HEAD')
|
|
9309
|
-
googleUTC =
|
|
9338
|
+
googleUTC = arrow.Arrow.strptime(headerData[0]['date'], '%a, %d %b %Y %H:%M:%S %Z', tzinfo='UTC')
|
|
9310
9339
|
except (httplib2.HttpLib2Error, RuntimeError) as e:
|
|
9311
9340
|
handleServerError(e)
|
|
9312
9341
|
except httplib2.socks.HTTPError as e:
|
|
@@ -9319,7 +9348,7 @@ def getLocalGoogleTimeOffset(testLocation=GOOGLE_TIMECHECK_LOCATION):
|
|
|
9319
9348
|
if prot == 'http':
|
|
9320
9349
|
continue
|
|
9321
9350
|
systemErrorExit(NETWORK_ERROR_RC, Msg.INVALID_HTTP_HEADER.format(str(headerData)))
|
|
9322
|
-
offset = remainder = int(abs((
|
|
9351
|
+
offset = remainder = int(abs((arrow.utcnow()-googleUTC).total_seconds()))
|
|
9323
9352
|
if offset < MAX_LOCAL_GOOGLE_TIME_OFFSET and prot == 'http':
|
|
9324
9353
|
continue
|
|
9325
9354
|
timeoff = []
|
|
@@ -9369,6 +9398,7 @@ MACOS_CODENAMES = {
|
|
|
9369
9398
|
13: 'Ventura',
|
|
9370
9399
|
14: 'Sonoma',
|
|
9371
9400
|
15: 'Sequoia',
|
|
9401
|
+
26: 'Tahoe',
|
|
9372
9402
|
}
|
|
9373
9403
|
|
|
9374
9404
|
def getOSPlatform():
|
|
@@ -9378,7 +9408,7 @@ def getOSPlatform():
|
|
|
9378
9408
|
elif myos == 'Windows':
|
|
9379
9409
|
pltfrm = ' '.join(platform.win32_ver())
|
|
9380
9410
|
elif myos == 'Darwin':
|
|
9381
|
-
myos = '
|
|
9411
|
+
myos = 'macOS'
|
|
9382
9412
|
mac_ver = platform.mac_ver()[0]
|
|
9383
9413
|
major_ver = int(mac_ver.split('.')[0]) # macver 10.14.6 == major_ver 10
|
|
9384
9414
|
minor_ver = int(mac_ver.split('.')[1]) # macver 10.14.6 == minor_ver 14
|
|
@@ -9619,7 +9649,7 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|
|
9619
9649
|
clearRowFilters = False
|
|
9620
9650
|
# if sys.platform.startswith('win'):
|
|
9621
9651
|
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
9622
|
-
if multiprocessing.get_start_method()
|
|
9652
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9623
9653
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
9624
9654
|
Cmd = glclargs.GamCLArgs()
|
|
9625
9655
|
else:
|
|
@@ -9661,7 +9691,7 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|
|
9661
9691
|
Cmd.InitializeArguments(dataItem)
|
|
9662
9692
|
elif dataType == GM.REDIRECT_QUEUE_GLOBALS:
|
|
9663
9693
|
GM.Globals = dataItem
|
|
9664
|
-
if multiprocessing.get_start_method()
|
|
9694
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9665
9695
|
reopenSTDFile(GM.STDOUT)
|
|
9666
9696
|
reopenSTDFile(GM.STDERR)
|
|
9667
9697
|
elif dataType == GM.REDIRECT_QUEUE_VALUES:
|
|
@@ -9707,7 +9737,7 @@ def initializeCSVFileQueueHandler(mpManager, mpQueueStdout, mpQueueStderr):
|
|
|
9707
9737
|
def terminateCSVFileQueueHandler(mpQueue, mpQueueHandler):
|
|
9708
9738
|
GM.Globals[GM.PARSER] = None
|
|
9709
9739
|
GM.Globals[GM.CSVFILE][GM.REDIRECT_QUEUE] = None
|
|
9710
|
-
if multiprocessing.get_start_method()
|
|
9740
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9711
9741
|
mpQueue.put((GM.REDIRECT_QUEUE_ARGS, Cmd.AllArguments()))
|
|
9712
9742
|
savedValues = saveNonPickleableValues()
|
|
9713
9743
|
mpQueue.put((GM.REDIRECT_QUEUE_GLOBALS, GM.Globals))
|
|
@@ -9737,13 +9767,13 @@ def StdQueueHandler(mpQueue, stdtype, gmGlobals, gcValues):
|
|
|
9737
9767
|
|
|
9738
9768
|
# if sys.platform.startswith('win'):
|
|
9739
9769
|
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
9740
|
-
if multiprocessing.get_start_method()
|
|
9770
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9741
9771
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
9742
9772
|
GM.Globals = gmGlobals.copy()
|
|
9743
9773
|
GC.Values = gcValues.copy()
|
|
9744
9774
|
pid0DataItem = [KEYBOARD_INTERRUPT_RC, None]
|
|
9745
9775
|
pidData = {}
|
|
9746
|
-
if multiprocessing.get_start_method()
|
|
9776
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9747
9777
|
if GM.Globals[stdtype][GM.REDIRECT_NAME] == 'null':
|
|
9748
9778
|
fd = open(os.devnull, GM.Globals[stdtype][GM.REDIRECT_MODE], encoding=UTF8)
|
|
9749
9779
|
elif GM.Globals[stdtype][GM.REDIRECT_NAME] == '-':
|
|
@@ -9831,7 +9861,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
|
|
9831
9861
|
with mplock:
|
|
9832
9862
|
initializeLogging()
|
|
9833
9863
|
# if sys.platform.startswith('win'):
|
|
9834
|
-
if multiprocessing.get_start_method()
|
|
9864
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9835
9865
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
9836
9866
|
GM.Globals[GM.API_CALLS_RETRY_DATA] = {}
|
|
9837
9867
|
GM.Globals[GM.CMDLOG_LOGGER] = None
|
|
@@ -9972,7 +10002,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|
|
9972
10002
|
mpManager = multiprocessing.Manager()
|
|
9973
10003
|
l = mpManager.Lock()
|
|
9974
10004
|
try:
|
|
9975
|
-
if multiprocessing.get_start_method()
|
|
10005
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9976
10006
|
pool = mpManager.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
|
9977
10007
|
else:
|
|
9978
10008
|
pool = multiprocessing.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
|
@@ -9981,7 +10011,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|
|
9981
10011
|
except AssertionError as e:
|
|
9982
10012
|
Cmd.SetLocation(0)
|
|
9983
10013
|
usageErrorExit(str(e))
|
|
9984
|
-
if multiprocessing.get_start_method()
|
|
10014
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
9985
10015
|
savedValues = saveNonPickleableValues()
|
|
9986
10016
|
if GM.Globals[GM.STDOUT][GM.REDIRECT_MULTIPROCESS]:
|
|
9987
10017
|
mpQueueStdout, mpQueueHandlerStdout = initializeStdQueueHandler(mpManager, GM.STDOUT, GM.Globals, GC.Values)
|
|
@@ -9996,7 +10026,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|
|
9996
10026
|
mpQueueStderr = mpQueueStdout
|
|
9997
10027
|
else:
|
|
9998
10028
|
mpQueueStderr = None
|
|
9999
|
-
if multiprocessing.get_start_method()
|
|
10029
|
+
if multiprocessing.get_start_method() != 'fork':
|
|
10000
10030
|
restoreNonPickleableValues(savedValues)
|
|
10001
10031
|
if mpQueueStdout:
|
|
10002
10032
|
mpQueueStdout.put((0, GM.REDIRECT_QUEUE_DATA, GM.Globals[GM.STDOUT][GM.REDIRECT_MULTI_FD].getvalue()))
|
|
@@ -10156,8 +10186,8 @@ def threadBatchWorker(showCmds=False, numItems=0):
|
|
|
10156
10186
|
batchWriteStderr(f'{currentISOformatTimeStamp()},{pid}/{numItems},Error,{str(e)},{logCmd}\n')
|
|
10157
10187
|
GM.Globals[GM.TBATCH_QUEUE].task_done()
|
|
10158
10188
|
|
|
10159
|
-
BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD]
|
|
10160
|
-
TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD]
|
|
10189
|
+
BATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD]
|
|
10190
|
+
TBATCH_COMMANDS = [Cmd.GAM_CMD, Cmd.COMMIT_BATCH_CMD, Cmd.EXECUTE_CMD, Cmd.PRINT_CMD, Cmd.SLEEP_CMD, Cmd.DATETIME_CMD, Cmd.SET_CMD, Cmd.CLEAR_CMD]
|
|
10161
10191
|
|
|
10162
10192
|
def ThreadBatchGAMCommands(items, showCmds):
|
|
10163
10193
|
if not items:
|
|
@@ -10269,6 +10299,14 @@ def doBatch(threadBatch=False):
|
|
|
10269
10299
|
continue
|
|
10270
10300
|
if argv:
|
|
10271
10301
|
cmd = argv[0].strip().lower()
|
|
10302
|
+
if cmd == Cmd.DATETIME_CMD:
|
|
10303
|
+
if len(argv) == 2:
|
|
10304
|
+
kwValues['datetime'] = todaysTime().strftime(argv[1])
|
|
10305
|
+
else:
|
|
10306
|
+
writeStderr(f'Command: >>>{Cmd.QuotedArgumentList([argv[0]])}<<< {Cmd.QuotedArgumentList(argv[1:])}\n')
|
|
10307
|
+
writeStderr(f'{ERROR_PREFIX}{Cmd.ARGUMENT_ERROR_NAMES[Cmd.ARGUMENT_INVALID][1]}: {Msg.EXPECTED} <{Cmd.DATETIME_CMD} DateTimeFormat>)>\n')
|
|
10308
|
+
errors += 1
|
|
10309
|
+
continue
|
|
10272
10310
|
if cmd == Cmd.SET_CMD:
|
|
10273
10311
|
if len(argv) == 3:
|
|
10274
10312
|
kwValues[argv[1]] = argv[2]
|
|
@@ -11033,7 +11071,7 @@ class Credentials(google.oauth2.credentials.Credentials):
|
|
|
11033
11071
|
expiry = info.get('token_expiry')
|
|
11034
11072
|
if expiry:
|
|
11035
11073
|
# Convert the raw expiry to datetime
|
|
11036
|
-
expiry =
|
|
11074
|
+
expiry = arrow.Arrow.strptime(expiry, YYYYMMDDTHHMMSSZ_FORMAT, tzinfo='UTC').naive
|
|
11037
11075
|
id_token_data = info.get('decoded_id_token')
|
|
11038
11076
|
|
|
11039
11077
|
# Provide backwards compatibility with field names when loading from JSON.
|
|
@@ -11152,7 +11190,7 @@ class Credentials(google.oauth2.credentials.Credentials):
|
|
|
11152
11190
|
|
|
11153
11191
|
def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
|
|
11154
11192
|
client_id, client_secret = getOAuthClientIDAndSecret()
|
|
11155
|
-
scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11193
|
+
scopesList = API.getClientScopesList(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11156
11194
|
if not currentScopes or verifyScopes:
|
|
11157
11195
|
selectedScopes = getScopesFromUser(scopesList, True, currentScopes)
|
|
11158
11196
|
if selectedScopes is None:
|
|
@@ -11198,7 +11236,7 @@ def doOAuthCreate():
|
|
|
11198
11236
|
else:
|
|
11199
11237
|
login_hint = None
|
|
11200
11238
|
scopes = []
|
|
11201
|
-
scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11239
|
+
scopesList = API.getClientScopesList(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11202
11240
|
while Cmd.ArgumentsRemaining():
|
|
11203
11241
|
myarg = getArgument()
|
|
11204
11242
|
if myarg == 'admin':
|
|
@@ -11214,7 +11252,9 @@ def doOAuthCreate():
|
|
|
11214
11252
|
scopes.append(uscope)
|
|
11215
11253
|
break
|
|
11216
11254
|
else:
|
|
11217
|
-
invalidChoiceExit(uscope,
|
|
11255
|
+
invalidChoiceExit(uscope,
|
|
11256
|
+
API.getClientScopesURLs(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS]),
|
|
11257
|
+
True)
|
|
11218
11258
|
else:
|
|
11219
11259
|
unknownArgumentExit()
|
|
11220
11260
|
if len(scopes) == 0:
|
|
@@ -11297,7 +11337,7 @@ def doOAuthInfo():
|
|
|
11297
11337
|
if 'email' in token_info:
|
|
11298
11338
|
printKeyValueList(['Google Workspace Admin', f'{token_info["email"]}'])
|
|
11299
11339
|
if 'expires_in' in token_info:
|
|
11300
|
-
printKeyValueList(['Expires', ISOformatTimeStamp((
|
|
11340
|
+
printKeyValueList(['Expires', ISOformatTimeStamp(arrow.now(GC.Values[GC.TIMEZONE]).shift(seconds=token_info['expires_in']))])
|
|
11301
11341
|
if showDetails:
|
|
11302
11342
|
for k, v in sorted(token_info.items()):
|
|
11303
11343
|
if k not in ['email', 'expires_in', 'issued_to', 'scope']:
|
|
@@ -11325,7 +11365,7 @@ def doOAuthUpdate():
|
|
|
11325
11365
|
if 'scopes' in jsonDict:
|
|
11326
11366
|
currentScopes = jsonDict['scopes']
|
|
11327
11367
|
else:
|
|
11328
|
-
currentScopes = API.getClientScopesURLs(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11368
|
+
currentScopes = API.getClientScopesURLs(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
|
11329
11369
|
else:
|
|
11330
11370
|
currentScopes = []
|
|
11331
11371
|
except (AttributeError, IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
|
@@ -12355,8 +12395,6 @@ def checkServiceAccount(users):
|
|
|
12355
12395
|
saScopes[scope['api']].append(scope['roscope'])
|
|
12356
12396
|
checkScopesSet.add(scope['roscope'])
|
|
12357
12397
|
i += 1
|
|
12358
|
-
if API.DRIVEACTIVITY in saScopes and API.DRIVE3 in saScopes:
|
|
12359
|
-
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
|
12360
12398
|
if API.DRIVE3 in saScopes:
|
|
12361
12399
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
|
12362
12400
|
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = saScopes
|
|
@@ -12413,7 +12451,7 @@ def checkServiceAccount(users):
|
|
|
12413
12451
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID, GAPI.NOT_FOUND,
|
|
12414
12452
|
GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
12415
12453
|
name=name, fields='validAfterTime')
|
|
12416
|
-
key_created
|
|
12454
|
+
key_created = arrow.get(key['validAfterTime'])
|
|
12417
12455
|
key_age = todaysTime()-key_created
|
|
12418
12456
|
printPassFail(Msg.SERVICE_ACCOUNT_PRIVATE_KEY_AGE.format(key_age.days), testWarn if key_age.days > 30 else testPass)
|
|
12419
12457
|
except GAPI.permissionDenied:
|
|
@@ -12668,15 +12706,15 @@ def _generatePrivateKeyAndPublicCert(projectId, clientEmail, name, key_size, b64
|
|
|
12668
12706
|
_validate=False)]))
|
|
12669
12707
|
# Gooogle seems to enforce the not before date strictly. Set the not before
|
|
12670
12708
|
# date to be UTC two minutes ago which should cover any clock skew.
|
|
12671
|
-
now =
|
|
12672
|
-
builder = builder.not_valid_before(now
|
|
12709
|
+
now = arrow.utcnow()
|
|
12710
|
+
builder = builder.not_valid_before(now.shift(minutes=-2).naive)
|
|
12673
12711
|
# Google defaults to 12/31/9999 date for end time if there's no
|
|
12674
12712
|
# policy to restrict key age
|
|
12675
12713
|
if validityHours:
|
|
12676
|
-
expires = now
|
|
12714
|
+
expires = now.shift(hours=validityHours, minutes=-2).naive
|
|
12677
12715
|
builder = builder.not_valid_after(expires)
|
|
12678
12716
|
else:
|
|
12679
|
-
builder = builder.not_valid_after(
|
|
12717
|
+
builder = builder.not_valid_after(arrow.Arrow(9999, 12, 31, 23, 59).naive)
|
|
12680
12718
|
builder = builder.serial_number(x509.random_serial_number())
|
|
12681
12719
|
builder = builder.public_key(public_key)
|
|
12682
12720
|
builder = builder.add_extension(x509.BasicConstraints(ca=False, path_length=None), critical=True)
|
|
@@ -13053,7 +13091,7 @@ def _showMailboxMonitorRequestStatus(request, i=0, count=0):
|
|
|
13053
13091
|
def doCreateMonitor():
|
|
13054
13092
|
auditObject, parameters = getAuditParameters(emailAddressRequired=True, requestIdRequired=False, destUserRequired=True)
|
|
13055
13093
|
#end_date defaults to 30 days in the future...
|
|
13056
|
-
end_date =
|
|
13094
|
+
end_date = GM.Globals[GM.DATETIME_NOW].shift(days=30).strftime(YYYYMMDD_HHMM_FORMAT)
|
|
13057
13095
|
begin_date = None
|
|
13058
13096
|
incoming_headers_only = outgoing_headers_only = drafts_headers_only = chats_headers_only = False
|
|
13059
13097
|
drafts = chats = True
|
|
@@ -13226,7 +13264,7 @@ def _adjustTryDate(errMsg, numDateChanges, limitDateChanges, prevTryDate):
|
|
|
13226
13264
|
else:
|
|
13227
13265
|
match_date = re.match('End date greater than LastReportedDate.', errMsg)
|
|
13228
13266
|
if match_date:
|
|
13229
|
-
tryDateTime =
|
|
13267
|
+
tryDateTime = arrow.Arrow.strptime(prevTryDate, YYYYMMDD_FORMAT).shift(days=-1)
|
|
13230
13268
|
tryDate = tryDateTime.strftime(YYYYMMDD_FORMAT)
|
|
13231
13269
|
if (not match_date) or (numDateChanges > limitDateChanges >= 0):
|
|
13232
13270
|
printWarningMessage(DATA_NOT_AVALIABLE_RC, errMsg)
|
|
@@ -13237,18 +13275,17 @@ def _checkDataRequiredServices(result, tryDate, dataRequiredServices, parameterS
|
|
|
13237
13275
|
# -1: Data not available:
|
|
13238
13276
|
# 0: Backup to earlier date
|
|
13239
13277
|
# 1: Data available
|
|
13240
|
-
oneDay = datetime.timedelta(days=1)
|
|
13241
13278
|
dataWarnings = result.get('warnings', [])
|
|
13242
13279
|
usageReports = result.get('usageReports', [])
|
|
13243
13280
|
# move to day before if we don't have at least one usageReport with parameters
|
|
13244
13281
|
if not usageReports or not usageReports[0].get('parameters', []):
|
|
13245
|
-
tryDateTime =
|
|
13282
|
+
tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
|
|
13246
13283
|
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
|
|
13247
13284
|
for warning in dataWarnings:
|
|
13248
13285
|
if warning['code'] == 'PARTIAL_DATA_AVAILABLE':
|
|
13249
13286
|
for app in warning['data']:
|
|
13250
13287
|
if app['key'] == 'application' and app['value'] != 'docs' and app['value'] in dataRequiredServices:
|
|
13251
|
-
tryDateTime =
|
|
13288
|
+
tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
|
|
13252
13289
|
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
|
|
13253
13290
|
elif warning['code'] == 'DATA_NOT_AVAILABLE':
|
|
13254
13291
|
for app in warning['data']:
|
|
@@ -13265,11 +13302,11 @@ def _checkDataRequiredServices(result, tryDate, dataRequiredServices, parameterS
|
|
|
13265
13302
|
if not requiredServices:
|
|
13266
13303
|
break
|
|
13267
13304
|
else:
|
|
13268
|
-
tryDateTime =
|
|
13305
|
+
tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
|
|
13269
13306
|
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
|
|
13270
13307
|
if checkUserEmail:
|
|
13271
13308
|
if 'entity' not in usageReports[0] or 'userEmail' not in usageReports[0]['entity']:
|
|
13272
|
-
tryDateTime =
|
|
13309
|
+
tryDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT).shift(days=-1)
|
|
13273
13310
|
return (0, tryDateTime.strftime(YYYYMMDD_FORMAT), None)
|
|
13274
13311
|
return (1, tryDate, usageReports)
|
|
13275
13312
|
|
|
@@ -13361,7 +13398,7 @@ def getUserOrgUnits(cd, orgUnit, orgUnitId):
|
|
|
13361
13398
|
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
|
|
13362
13399
|
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
13363
13400
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
13364
|
-
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None), orderBy='email',
|
|
13401
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnit, None, None), orderBy='email',
|
|
13365
13402
|
fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
13366
13403
|
userOrgUnits = {}
|
|
13367
13404
|
for user in result:
|
|
@@ -13383,13 +13420,17 @@ REPORTS_PARAMETERS_SIMPLE_TYPES = ['intValue', 'boolValue', 'datetimeValue', 'st
|
|
|
13383
13420
|
# [(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
|
|
13384
13421
|
# [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
|
|
13385
13422
|
# thismonth|(previousmonths <Integer>)]
|
|
13423
|
+
# [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
|
13386
13424
|
# [fields|parameters <String>)]
|
|
13387
13425
|
# [convertmbtogb]
|
|
13426
|
+
# (addcsvdata <FieldName> <String>)*
|
|
13388
13427
|
# gam report usage customer [todrive <ToDriveAttribute>*]
|
|
13389
13428
|
# [([start|startdate <Date>] [end|enddate <Date>])|(range <Date> <Date>)|
|
|
13390
13429
|
# thismonth|(previousmonths <Integer>)]
|
|
13430
|
+
# [skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
|
13391
13431
|
# [fields|parameters <String>)]
|
|
13392
13432
|
# [convertmbtogb]
|
|
13433
|
+
# (addcsvdata <FieldName> <String>)*
|
|
13393
13434
|
def doReportUsage():
|
|
13394
13435
|
def usageEntitySelectors():
|
|
13395
13436
|
selectorChoices = Cmd.USER_ENTITY_SELECTORS+Cmd.USER_CSVDATA_ENTITY_SELECTORS
|
|
@@ -13409,8 +13450,8 @@ def doReportUsage():
|
|
|
13409
13450
|
invalidArgumentExit(DELTA_DATE_FORMAT_REQUIRED)
|
|
13410
13451
|
return deltaDate
|
|
13411
13452
|
try:
|
|
13412
|
-
argDate =
|
|
13413
|
-
return
|
|
13453
|
+
argDate = arrow.Arrow.strptime(argstr, YYYYMMDD_FORMAT)
|
|
13454
|
+
return arrow.Arrow(argDate.year, argDate.month, argDate.day, tzinfo=GC.Values[GC.TIMEZONE])
|
|
13414
13455
|
except ValueError:
|
|
13415
13456
|
Cmd.Backup()
|
|
13416
13457
|
invalidArgumentExit(YYYYMMDD_FORMAT_REQUIRED)
|
|
@@ -13441,7 +13482,7 @@ def doReportUsage():
|
|
|
13441
13482
|
startEndTime = StartEndTime('startdate', 'enddate', 'date')
|
|
13442
13483
|
skipDayNumbers = []
|
|
13443
13484
|
skipDates = set()
|
|
13444
|
-
|
|
13485
|
+
addCSVData = {}
|
|
13445
13486
|
while Cmd.ArgumentsRemaining():
|
|
13446
13487
|
myarg = getArgument()
|
|
13447
13488
|
if csvPF and myarg == 'todrive':
|
|
@@ -13479,10 +13520,10 @@ def doReportUsage():
|
|
|
13479
13520
|
usageErrorExit(Msg.INVALID_DATE_TIME_RANGE.format(myarg, skipEnd, myarg, skipStart))
|
|
13480
13521
|
while skipStartDate <= skipEndDate:
|
|
13481
13522
|
skipDates.add(skipStartDate)
|
|
13482
|
-
skipStartDate
|
|
13523
|
+
skipStartDate = skipStartDate.shift(days=1)
|
|
13483
13524
|
elif myarg == 'skipdaysofweek':
|
|
13484
|
-
skipdaynames = getString(Cmd.OB_STRING).split(',')
|
|
13485
|
-
dow = [d.lower() for d in
|
|
13525
|
+
skipdaynames = getString(Cmd.OB_STRING).lower().split(',')
|
|
13526
|
+
dow = [d.lower() for d in DAYS_OF_WEEK]
|
|
13486
13527
|
skipDayNumbers = [dow.index(d) for d in skipdaynames if d in dow]
|
|
13487
13528
|
elif userReports and myarg == 'user':
|
|
13488
13529
|
userKey = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
@@ -13496,12 +13537,15 @@ def doReportUsage():
|
|
|
13496
13537
|
select = True
|
|
13497
13538
|
elif myarg == 'convertmbtogb':
|
|
13498
13539
|
convertMbToGb = True
|
|
13540
|
+
elif myarg == 'addcsvdata':
|
|
13541
|
+
k = getString(Cmd.OB_STRING)
|
|
13542
|
+
addCSVData[k] = getString(Cmd.OB_STRING, minLen=0)
|
|
13499
13543
|
else:
|
|
13500
13544
|
unknownArgumentExit()
|
|
13501
13545
|
if startEndTime.endDateTime is None:
|
|
13502
13546
|
startEndTime.endDateTime = todaysDate()
|
|
13503
13547
|
if startEndTime.startDateTime is None:
|
|
13504
|
-
startEndTime.startDateTime = startEndTime.endDateTime
|
|
13548
|
+
startEndTime.startDateTime = startEndTime.endDateTime.shift(days=-30)
|
|
13505
13549
|
startDateTime = startEndTime.startDateTime
|
|
13506
13550
|
startDate = startDateTime.strftime(YYYYMMDD_FORMAT)
|
|
13507
13551
|
endDateTime = startEndTime.endDateTime
|
|
@@ -13529,15 +13573,17 @@ def doReportUsage():
|
|
|
13529
13573
|
titles.append('orgUnitPath')
|
|
13530
13574
|
else:
|
|
13531
13575
|
pageMessage = None
|
|
13576
|
+
if addCSVData:
|
|
13577
|
+
titles.extend(sorted(addCSVData.keys()))
|
|
13532
13578
|
csvPF.SetTitles(titles)
|
|
13533
13579
|
csvPF.SetSortAllTitles()
|
|
13534
13580
|
parameters = ','.join(parameters) if parameters else None
|
|
13535
13581
|
while startDateTime <= endDateTime:
|
|
13536
13582
|
if startDateTime.weekday() in skipDayNumbers or startDateTime in skipDates:
|
|
13537
|
-
startDateTime
|
|
13583
|
+
startDateTime = startDateTime.shift(days=1)
|
|
13538
13584
|
continue
|
|
13539
13585
|
useDate = startDateTime.strftime(YYYYMMDD_FORMAT)
|
|
13540
|
-
startDateTime
|
|
13586
|
+
startDateTime = startDateTime.shift(days=1)
|
|
13541
13587
|
try:
|
|
13542
13588
|
for kwarg in kwargs:
|
|
13543
13589
|
if userReports:
|
|
@@ -13563,6 +13609,8 @@ def doReportUsage():
|
|
|
13563
13609
|
row['orgUnitPath'] = userOrgUnits.get(row['user'], UNKNOWN)
|
|
13564
13610
|
else:
|
|
13565
13611
|
row['user'] = UNKNOWN
|
|
13612
|
+
if addCSVData:
|
|
13613
|
+
row.update(addCSVData)
|
|
13566
13614
|
for item in entity.get('parameters', []):
|
|
13567
13615
|
if 'name' not in item:
|
|
13568
13616
|
continue
|
|
@@ -13694,12 +13742,14 @@ REPORT_ACTIVITIES_TIME_OBJECTS = {'time'}
|
|
|
13694
13742
|
# [aggregatebydate|aggregatebyuser [Boolean]]
|
|
13695
13743
|
# [maxresults <Number>]
|
|
13696
13744
|
# [convertmbtogb]
|
|
13745
|
+
# (addcsvdata <FieldName> <String>)*
|
|
13697
13746
|
# gam report customers|customer|domain [todrive <ToDriveAttribute>*]
|
|
13698
13747
|
# [(date <Date>)|(range <Date> <Date>)|
|
|
13699
13748
|
# yesterday|today|thismonth|(previousmonths <Integer>)]
|
|
13700
13749
|
# [nodatechange | (fulldatarequired all|<CustomerServiceNameList>)]
|
|
13701
13750
|
# [(fields|parameters <String>)|(services <CustomerServiceNameList>)] [noauthorizedapps]
|
|
13702
13751
|
# [convertmbtogb]
|
|
13752
|
+
# (addcsvdata <FieldName> <String>)*
|
|
13703
13753
|
def doReport():
|
|
13704
13754
|
def processUserUsage(usage, lastDate):
|
|
13705
13755
|
if not usage:
|
|
@@ -13717,6 +13767,8 @@ def doReport():
|
|
|
13717
13767
|
row['orgUnitPath'] = userOrgUnits.get(row['email'], UNKNOWN)
|
|
13718
13768
|
else:
|
|
13719
13769
|
row['email'] = UNKNOWN
|
|
13770
|
+
if addCSVData:
|
|
13771
|
+
row.update(addCSVData)
|
|
13720
13772
|
for item in user_report.get('parameters', []):
|
|
13721
13773
|
if 'name' not in item:
|
|
13722
13774
|
continue
|
|
@@ -13728,7 +13780,7 @@ def doReport():
|
|
|
13728
13780
|
mg = DISABLED_REASON_TIME_PATTERN.match(item['stringValue'])
|
|
13729
13781
|
if mg:
|
|
13730
13782
|
try:
|
|
13731
|
-
disabledTime = formatLocalTime(
|
|
13783
|
+
disabledTime = formatLocalTime(arrow.Arrow.strptime(mg.group(1), '%Y/%m/%d-%H:%M:%S').replace(tzinfo='UTC').strftime(YYYYMMDDTHHMMSSZ_FORMAT))
|
|
13732
13784
|
row['accounts:disabled_time'] = disabledTime
|
|
13733
13785
|
csvPF.AddTitles('accounts:disabled_time')
|
|
13734
13786
|
except ValueError:
|
|
@@ -13808,6 +13860,8 @@ def doReport():
|
|
|
13808
13860
|
return (False, lastDate)
|
|
13809
13861
|
lastDate = usage[0]['date']
|
|
13810
13862
|
row = {'date': lastDate}
|
|
13863
|
+
if addCSVData:
|
|
13864
|
+
row.update(addCSVData)
|
|
13811
13865
|
for item in usage[0].get('parameters', []):
|
|
13812
13866
|
if 'name' not in item:
|
|
13813
13867
|
continue
|
|
@@ -13882,12 +13936,16 @@ def doReport():
|
|
|
13882
13936
|
continue
|
|
13883
13937
|
for ptype in REPORTS_PARAMETERS_SIMPLE_TYPES:
|
|
13884
13938
|
if ptype in item:
|
|
13939
|
+
row = {'date': lastDate}
|
|
13940
|
+
if addCSVData:
|
|
13941
|
+
row.update(addCSVData)
|
|
13885
13942
|
if ptype != 'datetimeValue':
|
|
13886
13943
|
if convertMbToGb and name.endswith('_in_mb'):
|
|
13887
13944
|
name = convertReportMBtoGB(name, item)
|
|
13888
|
-
|
|
13945
|
+
row.update({'name': name, 'value': item[ptype]})
|
|
13889
13946
|
else:
|
|
13890
|
-
|
|
13947
|
+
row.update({'name': name, 'value': formatLocalTime(item[ptype])})
|
|
13948
|
+
csvPF.WriteRow(row)
|
|
13891
13949
|
break
|
|
13892
13950
|
else:
|
|
13893
13951
|
if 'msgValue' in item:
|
|
@@ -13896,6 +13954,8 @@ def doReport():
|
|
|
13896
13954
|
continue
|
|
13897
13955
|
for subitem in item['msgValue']:
|
|
13898
13956
|
app = {'date': lastDate}
|
|
13957
|
+
if addCSVData:
|
|
13958
|
+
app.update(addCSVData)
|
|
13899
13959
|
for an_item in subitem:
|
|
13900
13960
|
if an_item == 'client_name':
|
|
13901
13961
|
app['name'] = f'App: {escapeCRsNLs(subitem[an_item])}'
|
|
@@ -13908,7 +13968,11 @@ def doReport():
|
|
|
13908
13968
|
values = []
|
|
13909
13969
|
for subitem in item['msgValue']:
|
|
13910
13970
|
values.append(f'{subitem["version_number"]}:{subitem["num_devices"]}')
|
|
13911
|
-
|
|
13971
|
+
row = {'date': lastDate}
|
|
13972
|
+
if addCSVData:
|
|
13973
|
+
row.update(addCSVData)
|
|
13974
|
+
row.update({'name': name, 'value': ' '.join(sorted(values, reverse=True))})
|
|
13975
|
+
csvPF.WriteRow(row)
|
|
13912
13976
|
else:
|
|
13913
13977
|
values = []
|
|
13914
13978
|
for subitem in item['msgValue']:
|
|
@@ -13923,7 +13987,11 @@ def doReport():
|
|
|
13923
13987
|
values.append(f'{myvalue}:{mycount}')
|
|
13924
13988
|
else:
|
|
13925
13989
|
continue
|
|
13926
|
-
|
|
13990
|
+
row = {'date': lastDate}
|
|
13991
|
+
if addCSVData:
|
|
13992
|
+
row.update(addCSVData)
|
|
13993
|
+
row.update({'name': name, 'value': ' '.join(sorted(values, reverse=True))})
|
|
13994
|
+
csvPF.WriteRow(row)
|
|
13927
13995
|
csvPF.SortRowsTwoTitles('date', 'name', False)
|
|
13928
13996
|
if authorizedApps:
|
|
13929
13997
|
csvPF.AddTitle('client_id')
|
|
@@ -13963,7 +14031,6 @@ def doReport():
|
|
|
13963
14031
|
eventCounts = {}
|
|
13964
14032
|
eventNames = []
|
|
13965
14033
|
startEndTime = StartEndTime('start', 'end')
|
|
13966
|
-
oneDay = datetime.timedelta(days=1)
|
|
13967
14034
|
filterTimes = {}
|
|
13968
14035
|
maxActivities = maxEvents = 0
|
|
13969
14036
|
maxResults = 1000
|
|
@@ -14074,7 +14141,7 @@ def doReport():
|
|
|
14074
14141
|
eventRowFilter = True
|
|
14075
14142
|
elif activityReports and myarg == 'groupidfilter':
|
|
14076
14143
|
groupIdFilter = getString(Cmd.OB_STRING)
|
|
14077
|
-
elif
|
|
14144
|
+
elif myarg == 'addcsvdata':
|
|
14078
14145
|
k = getString(Cmd.OB_STRING)
|
|
14079
14146
|
addCSVData[k] = getString(Cmd.OB_STRING, minLen=0)
|
|
14080
14147
|
elif activityReports and myarg == 'shownoactivities':
|
|
@@ -14139,6 +14206,8 @@ def doReport():
|
|
|
14139
14206
|
titles = ['email'] if not showOrgUnit else ['email', 'orgUnitPath']
|
|
14140
14207
|
else:
|
|
14141
14208
|
titles = ['email', 'date'] if not showOrgUnit else ['email', 'orgUnitPath', 'date']
|
|
14209
|
+
if addCSVData:
|
|
14210
|
+
titles.extend(sorted(addCSVData.keys()))
|
|
14142
14211
|
csvPF.SetTitles(titles)
|
|
14143
14212
|
csvPF.SetSortAllTitles()
|
|
14144
14213
|
i = 0
|
|
@@ -14175,7 +14244,7 @@ def doReport():
|
|
|
14175
14244
|
if fullData == 0:
|
|
14176
14245
|
if numDateChanges > limitDateChanges >= 0:
|
|
14177
14246
|
break
|
|
14178
|
-
startDateTime = endDateTime =
|
|
14247
|
+
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
|
|
14179
14248
|
continue
|
|
14180
14249
|
if not select and userKey == 'all':
|
|
14181
14250
|
pageMessage = getPageMessageForWhom(forWhom, showDate=tryDate)
|
|
@@ -14204,7 +14273,7 @@ def doReport():
|
|
|
14204
14273
|
tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
|
|
14205
14274
|
if not tryDate:
|
|
14206
14275
|
break
|
|
14207
|
-
startDateTime = endDateTime =
|
|
14276
|
+
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
|
|
14208
14277
|
continue
|
|
14209
14278
|
except GAPI.invalidInput as e:
|
|
14210
14279
|
systemErrorExit(GOOGLE_API_ERROR_RC, str(e))
|
|
@@ -14217,7 +14286,7 @@ def doReport():
|
|
|
14217
14286
|
break
|
|
14218
14287
|
except GAPI.forbidden as e:
|
|
14219
14288
|
accessErrorExit(None, str(e))
|
|
14220
|
-
startDateTime
|
|
14289
|
+
startDateTime = startDateTime.shift(days=1)
|
|
14221
14290
|
if exitUserLoop:
|
|
14222
14291
|
break
|
|
14223
14292
|
if user != 'all' and lastDate is None and GC.Values[GC.CSV_OUTPUT_USERS_AUDIT]:
|
|
@@ -14225,6 +14294,8 @@ def doReport():
|
|
|
14225
14294
|
if aggregateByDate:
|
|
14226
14295
|
for usageDate, events in eventCounts.items():
|
|
14227
14296
|
row = {'date': usageDate}
|
|
14297
|
+
if addCSVData:
|
|
14298
|
+
row.update(addCSVData)
|
|
14228
14299
|
for event, count in events.items():
|
|
14229
14300
|
if convertMbToGb and event.endswith('_in_gb'):
|
|
14230
14301
|
count = f'{count/1024:.2f}'
|
|
@@ -14237,6 +14308,8 @@ def doReport():
|
|
|
14237
14308
|
row = {'email': email}
|
|
14238
14309
|
if showOrgUnit:
|
|
14239
14310
|
row['orgUnitPath'] = userOrgUnits.get(email, UNKNOWN)
|
|
14311
|
+
if addCSVData:
|
|
14312
|
+
row.update(addCSVData)
|
|
14240
14313
|
for event, count in events.items():
|
|
14241
14314
|
if convertMbToGb and event.endswith('_in_gb'):
|
|
14242
14315
|
count = f'{count/1024:.2f}'
|
|
@@ -14251,6 +14324,8 @@ def doReport():
|
|
|
14251
14324
|
if startEndTime.startDateTime is None:
|
|
14252
14325
|
startEndTime.startDateTime = startEndTime.endDateTime = todaysDate()
|
|
14253
14326
|
csvPF.SetTitles('date')
|
|
14327
|
+
if addCSVData:
|
|
14328
|
+
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
14254
14329
|
if not userCustomerRange or (startEndTime.startDateTime == startEndTime.endDateTime):
|
|
14255
14330
|
csvPF.AddTitles(['name', 'value'])
|
|
14256
14331
|
authorizedApps = []
|
|
@@ -14274,7 +14349,7 @@ def doReport():
|
|
|
14274
14349
|
if fullData == 0:
|
|
14275
14350
|
if numDateChanges > limitDateChanges >= 0:
|
|
14276
14351
|
break
|
|
14277
|
-
startDateTime = endDateTime =
|
|
14352
|
+
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
|
|
14278
14353
|
continue
|
|
14279
14354
|
usage = callGAPIpages(service, 'get', 'usageReports',
|
|
14280
14355
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.FORBIDDEN],
|
|
@@ -14292,13 +14367,13 @@ def doReport():
|
|
|
14292
14367
|
tryDate = _adjustTryDate(str(e), numDateChanges, limitDateChanges, tryDate)
|
|
14293
14368
|
if not tryDate:
|
|
14294
14369
|
break
|
|
14295
|
-
startDateTime = endDateTime =
|
|
14370
|
+
startDateTime = endDateTime = arrow.Arrow.strptime(tryDate, YYYYMMDD_FORMAT)
|
|
14296
14371
|
continue
|
|
14297
14372
|
except GAPI.invalidInput as e:
|
|
14298
14373
|
systemErrorExit(GOOGLE_API_ERROR_RC, str(e))
|
|
14299
14374
|
except GAPI.forbidden as e:
|
|
14300
14375
|
accessErrorExit(None, str(e))
|
|
14301
|
-
startDateTime
|
|
14376
|
+
startDateTime = startDateTime.shift(days=1)
|
|
14302
14377
|
csvPF.writeCSVfile(f'Customer Report - {tryDate}')
|
|
14303
14378
|
else: # activityReports
|
|
14304
14379
|
csvPF.SetTitles('name')
|
|
@@ -14368,8 +14443,8 @@ def doReport():
|
|
|
14368
14443
|
eventTime = activity.get('id', {}).get('time', UNKNOWN)
|
|
14369
14444
|
if eventTime != UNKNOWN:
|
|
14370
14445
|
try:
|
|
14371
|
-
eventTime
|
|
14372
|
-
except (
|
|
14446
|
+
eventTime = arrow.get(eventTime)
|
|
14447
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
14373
14448
|
eventTime = UNKNOWN
|
|
14374
14449
|
if eventTime != UNKNOWN:
|
|
14375
14450
|
eventDate = eventTime.strftime(YYYYMMDD_FORMAT)
|
|
@@ -14393,7 +14468,7 @@ def doReport():
|
|
|
14393
14468
|
if val is not None:
|
|
14394
14469
|
val = int(val)
|
|
14395
14470
|
if val >= 62135683200:
|
|
14396
|
-
event[item['name']] = ISOformatTimeStamp(
|
|
14471
|
+
event[item['name']] = ISOformatTimeStamp(arrow.Arrow.fromtimestamp(val-62135683200, GC.Values[GC.TIMEZONE]))
|
|
14397
14472
|
else:
|
|
14398
14473
|
event[item['name']] = val
|
|
14399
14474
|
else:
|
|
@@ -16285,7 +16360,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|
|
16285
16360
|
while True:
|
|
16286
16361
|
try:
|
|
16287
16362
|
result = callGAPI(rep.customerUsageReports(), 'get',
|
|
16288
|
-
throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
16363
|
+
throwReasons=[GAPI.INVALID, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
16289
16364
|
date=tryDate, customerId=customerInfo['id'],
|
|
16290
16365
|
fields='warnings,usageReports', parameters=parameters)
|
|
16291
16366
|
usageReports = numUsersAvailable(result)
|
|
@@ -16298,7 +16373,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|
|
16298
16373
|
if fullData == 0:
|
|
16299
16374
|
continue
|
|
16300
16375
|
break
|
|
16301
|
-
except GAPI.invalid as e:
|
|
16376
|
+
except (GAPI.invalid, GAPI.failedPrecondition) as e:
|
|
16302
16377
|
tryDate = _adjustTryDate(str(e), 0, -1, tryDate)
|
|
16303
16378
|
if not tryDate:
|
|
16304
16379
|
return
|
|
@@ -17012,22 +17087,39 @@ def doDeleteAdmin():
|
|
|
17012
17087
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
17013
17088
|
ClientAPIAccessDeniedExit(str(e))
|
|
17014
17089
|
|
|
17015
|
-
|
|
17090
|
+
ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP = {
|
|
17016
17091
|
'user': 'assignedToUser',
|
|
17017
17092
|
'group': 'assignedToGroup',
|
|
17018
17093
|
'serviceaccount': 'assignedToServiceAccount',
|
|
17094
|
+
'unknown': 'assignedToUnknown',
|
|
17019
17095
|
}
|
|
17096
|
+
ALL_ASSIGNEE_TYPES = ['user', 'group', 'serviceaccount']
|
|
17097
|
+
|
|
17020
17098
|
PRINT_ADMIN_FIELDS = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
|
|
17021
17099
|
PRINT_ADMIN_TITLES = ['roleAssignmentId', 'roleId', 'role',
|
|
17022
17100
|
'assignedTo', 'assignedToUser', 'assignedToGroup', 'assignedToServiceAccount', 'assignedToUnknown',
|
|
17023
17101
|
'scopeType', 'orgUnitId', 'orgUnit']
|
|
17024
17102
|
|
|
17025
17103
|
# gam print admins [todrive <ToDriveAttribute>*]
|
|
17026
|
-
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
|
17027
|
-
# [
|
|
17104
|
+
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
|
17105
|
+
# [types <AdminAssigneeTypeList>]
|
|
17106
|
+
# [recursive] [condition] [privileges] [oneitemperrow]
|
|
17028
17107
|
# gam show admins
|
|
17029
|
-
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
|
17108
|
+
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
|
17109
|
+
# [types <AdminAssigneeTypeList>]
|
|
17110
|
+
# [recursive] [condition] [privileges]
|
|
17030
17111
|
def doPrintShowAdmins():
|
|
17112
|
+
def _getAssigneeTypes(myarg):
|
|
17113
|
+
if myarg in {'type', 'types'}:
|
|
17114
|
+
for gtype in getString(Cmd.OB_ADMIN_ASSIGNEE_TYPE_LIST).lower().replace(',', ' ').split():
|
|
17115
|
+
if gtype in ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP:
|
|
17116
|
+
typesSet.add(ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP[gtype])
|
|
17117
|
+
else:
|
|
17118
|
+
invalidChoiceExit(gtype, ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP, True)
|
|
17119
|
+
else:
|
|
17120
|
+
return False
|
|
17121
|
+
return True
|
|
17122
|
+
|
|
17031
17123
|
def _getPrivileges(admin):
|
|
17032
17124
|
if showPrivileges:
|
|
17033
17125
|
roleId = admin['roleId']
|
|
@@ -17055,17 +17147,14 @@ def doPrintShowAdmins():
|
|
|
17055
17147
|
assignedTo = admin['assignedTo']
|
|
17056
17148
|
admin['assignedToUnknown'] = False
|
|
17057
17149
|
if assignedTo not in assignedToIdEmailMap:
|
|
17058
|
-
|
|
17059
|
-
|
|
17060
|
-
|
|
17061
|
-
|
|
17062
|
-
if not assignedToField and assigneeType in ASSIGNEE_EMAILTYPE_TOFIELD_MAP:
|
|
17063
|
-
assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP[assigneeType]
|
|
17064
|
-
if assigneeType == 'unknown':
|
|
17065
|
-
assignedToField = 'assignedToUnknown'
|
|
17150
|
+
emailTypes = ALL_ASSIGNEE_TYPES if admin.get('assigneeType', '') != 'group' else ['group']
|
|
17151
|
+
assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}', cd, sal, emailTypes=emailTypes)
|
|
17152
|
+
assignedToField = ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP.get(assigneeType, 'assignedToUnknown')
|
|
17153
|
+
if assignedToField == 'assignedToUnknown':
|
|
17066
17154
|
assigneeEmail = True
|
|
17067
17155
|
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
|
|
17068
17156
|
admin[assignedToIdEmailMap[assignedTo]['assignedToField']] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
|
|
17157
|
+
admin['assignedToField'] = assignedToIdEmailMap[assignedTo]['assignedToField']
|
|
17069
17158
|
if privileges is not None:
|
|
17070
17159
|
admin.update(privileges)
|
|
17071
17160
|
if 'orgUnitId' in admin:
|
|
@@ -17075,16 +17164,22 @@ def doPrintShowAdmins():
|
|
|
17075
17164
|
admin['condition'] = 'securitygroup'
|
|
17076
17165
|
elif admin['condition'] == NONSECURITY_GROUP_CONDITION:
|
|
17077
17166
|
admin['condition'] = 'nonsecuritygroup'
|
|
17167
|
+
if debug:
|
|
17168
|
+
print('******', admin['assignedTo'], admin.get('assigneeType', 'no type'),
|
|
17169
|
+
admin['assignedToField'], not typesSet or admin['assignedToField'] in typesSet)
|
|
17170
|
+
return not typesSet or admin['assignedToField'] in typesSet
|
|
17078
17171
|
|
|
17079
17172
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
17080
17173
|
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
|
|
17081
17174
|
csvPF = CSVPrintFile(PRINT_ADMIN_TITLES) if Act.csvFormat() else None
|
|
17082
17175
|
roleId = None
|
|
17083
17176
|
userKey = None
|
|
17084
|
-
oneItemPerRow = showPrivileges = False
|
|
17177
|
+
debug = oneItemPerRow = recursive = showPrivileges = False
|
|
17178
|
+
typesSet = set()
|
|
17085
17179
|
kwargs = {}
|
|
17086
17180
|
rolePrivileges = {}
|
|
17087
|
-
|
|
17181
|
+
allGroupRoles = ','.join(sorted(ALL_GROUP_ROLES))
|
|
17182
|
+
fieldsList = PRINT_ADMIN_FIELDS+['assigneeType']
|
|
17088
17183
|
assignedToIdEmailMap = {}
|
|
17089
17184
|
while Cmd.ArgumentsRemaining():
|
|
17090
17185
|
myarg = getArgument()
|
|
@@ -17094,6 +17189,16 @@ def doPrintShowAdmins():
|
|
|
17094
17189
|
userKey = kwargs['userKey'] = getEmailAddress()
|
|
17095
17190
|
elif myarg == 'role':
|
|
17096
17191
|
_, roleId = getRoleId()
|
|
17192
|
+
elif _getAssigneeTypes(myarg):
|
|
17193
|
+
pass
|
|
17194
|
+
elif myarg == 'recursive':
|
|
17195
|
+
recursive = True
|
|
17196
|
+
memberOptions = initMemberOptions()
|
|
17197
|
+
memberOptions[MEMBEROPTION_INCLUDEDERIVEDMEMBERSHIP] = True
|
|
17198
|
+
memberOptions[MEMBEROPTION_DISPLAYMATCH] = False
|
|
17199
|
+
memberDisplayOptions = initIPSGMGroupMemberDisplayOptions()
|
|
17200
|
+
for role in [Ent.ROLE_MEMBER, Ent.ROLE_MANAGER, Ent.ROLE_OWNER]:
|
|
17201
|
+
memberDisplayOptions[role]['show'] = True
|
|
17097
17202
|
elif myarg == 'condition':
|
|
17098
17203
|
fieldsList.append('condition')
|
|
17099
17204
|
if csvPF:
|
|
@@ -17102,6 +17207,8 @@ def doPrintShowAdmins():
|
|
|
17102
17207
|
showPrivileges = True
|
|
17103
17208
|
elif myarg == 'oneitemperrow':
|
|
17104
17209
|
oneItemPerRow = True
|
|
17210
|
+
elif myarg == 'debug':
|
|
17211
|
+
debug = True
|
|
17105
17212
|
else:
|
|
17106
17213
|
unknownArgumentExit()
|
|
17107
17214
|
if roleId and not kwargs:
|
|
@@ -17113,7 +17220,7 @@ def doPrintShowAdmins():
|
|
|
17113
17220
|
admins = callGAPIpages(cd.roleAssignments(), 'list', 'items',
|
|
17114
17221
|
pageMessage=getPageMessage(),
|
|
17115
17222
|
throwReasons=[GAPI.INVALID, GAPI.USER_NOT_FOUND,
|
|
17116
|
-
GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE,
|
|
17223
|
+
GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE,
|
|
17117
17224
|
GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
|
|
17118
17225
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
17119
17226
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
@@ -17121,39 +17228,75 @@ def doPrintShowAdmins():
|
|
|
17121
17228
|
except (GAPI.invalid, GAPI.userNotFound):
|
|
17122
17229
|
entityUnknownWarning(Ent.ADMINISTRATOR, userKey)
|
|
17123
17230
|
return
|
|
17124
|
-
except
|
|
17125
|
-
entityActionFailedExit([Ent.
|
|
17231
|
+
except GAPI.notFound as e:
|
|
17232
|
+
entityActionFailedExit([Ent.ADMIN_ROLE, kwargs['roleId']], str(e))
|
|
17233
|
+
except GAPI.serviceNotAvailable as e:
|
|
17234
|
+
entityActionFailedExit([Ent.ADMINISTRATOR, userKey], str(e))
|
|
17126
17235
|
except (GAPI.badRequest, GAPI.customerNotFound):
|
|
17127
17236
|
accessErrorExit(cd)
|
|
17128
17237
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
17129
17238
|
ClientAPIAccessDeniedExit(str(e))
|
|
17239
|
+
count = len(admins)
|
|
17240
|
+
groupMembers = {}
|
|
17241
|
+
expandedAdmins = []
|
|
17242
|
+
i = 0
|
|
17243
|
+
for admin in admins:
|
|
17244
|
+
i += 1
|
|
17245
|
+
if roleId and roleId != admin['roleId']:
|
|
17246
|
+
continue
|
|
17247
|
+
assignedTo = admin['assignedTo']
|
|
17248
|
+
if admin['assigneeType'] != 'group' or not recursive:
|
|
17249
|
+
if _setNamesFromIds(admin, _getPrivileges(admin)):
|
|
17250
|
+
expandedAdmins.append(admin)
|
|
17251
|
+
continue
|
|
17252
|
+
if assignedTo not in groupMembers:
|
|
17253
|
+
membersList = []
|
|
17254
|
+
membersSet = set()
|
|
17255
|
+
level = 0
|
|
17256
|
+
getGroupMembers(cd, assignedTo, allGroupRoles, membersList, membersSet, i, count,
|
|
17257
|
+
memberOptions, memberDisplayOptions, level, {Ent.TYPE_USER})
|
|
17258
|
+
groupMembers[assignedTo] = membersList[:]
|
|
17259
|
+
if not _setNamesFromIds(admin, _getPrivileges(admin)):
|
|
17260
|
+
continue
|
|
17261
|
+
if not groupMembers[assignedTo]:
|
|
17262
|
+
expandedAdmins.append(admin)
|
|
17263
|
+
continue
|
|
17264
|
+
admin['assigneeType'] = 'user'
|
|
17265
|
+
admin['assignedToGroup'] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
|
|
17266
|
+
for member in groupMembers[assignedTo]:
|
|
17267
|
+
userAdmin = admin.copy()
|
|
17268
|
+
userAdmin['assignedTo'] = member['id']
|
|
17269
|
+
_setNamesFromIds(userAdmin, _getPrivileges(admin))
|
|
17270
|
+
expandedAdmins.append(userAdmin)
|
|
17271
|
+
admins = expandedAdmins
|
|
17272
|
+
count = len(expandedAdmins)
|
|
17130
17273
|
if not csvPF:
|
|
17131
|
-
count = len(admins)
|
|
17132
17274
|
performActionNumItems(count, Ent.ADMIN_ROLE_ASSIGNMENT)
|
|
17133
17275
|
Ind.Increment()
|
|
17134
17276
|
i = 0
|
|
17135
|
-
for admin in
|
|
17277
|
+
for admin in expandedAdmins:
|
|
17136
17278
|
i += 1
|
|
17137
|
-
if roleId and roleId != admin['roleId']:
|
|
17138
|
-
continue
|
|
17139
|
-
_setNamesFromIds(admin, _getPrivileges(admin))
|
|
17140
17279
|
printEntity([Ent.ADMIN_ROLE_ASSIGNMENT, admin['roleAssignmentId']], i, count)
|
|
17141
17280
|
Ind.Increment()
|
|
17142
17281
|
for field in PRINT_ADMIN_TITLES:
|
|
17143
17282
|
if field in admin:
|
|
17144
|
-
if field == 'roleAssignmentId':
|
|
17283
|
+
if (field == 'roleAssignmentId') or (field == 'assignedToUnknown' and not admin[field]):
|
|
17145
17284
|
continue
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
|
|
17149
|
-
|
|
17285
|
+
printKeyValueList([field, admin[field]])
|
|
17286
|
+
if showPrivileges:
|
|
17287
|
+
rolePrivileges = admin.get('rolePrivileges', [])
|
|
17288
|
+
jcount = len(rolePrivileges)
|
|
17289
|
+
if jcount > 0:
|
|
17290
|
+
printKeyValueList(['rolePrivileges', jcount])
|
|
17291
|
+
Ind.Increment()
|
|
17292
|
+
showJSON(None, rolePrivileges)
|
|
17293
|
+
Ind.Decrement()
|
|
17150
17294
|
Ind.Decrement()
|
|
17151
17295
|
Ind.Decrement()
|
|
17152
17296
|
else:
|
|
17153
|
-
for admin in
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
_setNamesFromIds(admin, _getPrivileges(admin))
|
|
17297
|
+
for admin in expandedAdmins:
|
|
17298
|
+
admin.pop('assigneeType', None)
|
|
17299
|
+
admin.pop('assignedToField', None)
|
|
17157
17300
|
if not oneItemPerRow or 'rolePrivileges' not in admin:
|
|
17158
17301
|
csvPF.WriteRowTitles(flattenJSON(admin))
|
|
17159
17302
|
else:
|
|
@@ -17856,10 +17999,29 @@ ORG_FIELD_INFO_ORDER = ['orgUnitId', 'name', 'description', 'parentOrgUnitPath',
|
|
|
17856
17999
|
ORG_FIELDS_WITH_CRS_NLS = {'description'}
|
|
17857
18000
|
|
|
17858
18001
|
def _doInfoOrgs(entityList):
|
|
18002
|
+
def _printUsers(entityType, orgUnitPath, isSuspended, isArchived):
|
|
18003
|
+
users = callGAPIpages(cd.users(), 'list', 'users',
|
|
18004
|
+
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
18005
|
+
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
18006
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, isSuspended, isArchived), orderBy='email',
|
|
18007
|
+
fields='nextPageToken,users(primaryEmail,orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
18008
|
+
printEntitiesCount(entityType, None)
|
|
18009
|
+
usersInOU = 0
|
|
18010
|
+
Ind.Increment()
|
|
18011
|
+
orgUnitPath = orgUnitPath.lower()
|
|
18012
|
+
for user in users:
|
|
18013
|
+
if orgUnitPath == user['orgUnitPath'].lower():
|
|
18014
|
+
printKeyValueList([user['primaryEmail']])
|
|
18015
|
+
usersInOU += 1
|
|
18016
|
+
elif showChildren:
|
|
18017
|
+
printKeyValueList([f'{user["primaryEmail"]} (child)'])
|
|
18018
|
+
usersInOU += 1
|
|
18019
|
+
Ind.Decrement()
|
|
18020
|
+
printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU])
|
|
18021
|
+
|
|
17859
18022
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
17860
18023
|
getUsers = True
|
|
17861
|
-
isSuspended = None
|
|
17862
|
-
entityType = Ent.USER
|
|
18024
|
+
isSuspended = isArchived = None
|
|
17863
18025
|
showChildren = False
|
|
17864
18026
|
while Cmd.ArgumentsRemaining():
|
|
17865
18027
|
myarg = getArgument()
|
|
@@ -17867,7 +18029,8 @@ def _doInfoOrgs(entityList):
|
|
|
17867
18029
|
getUsers = False
|
|
17868
18030
|
elif myarg in SUSPENDED_ARGUMENTS:
|
|
17869
18031
|
isSuspended = _getIsSuspended(myarg)
|
|
17870
|
-
|
|
18032
|
+
elif myarg in ARCHIVED_ARGUMENTS:
|
|
18033
|
+
isArchived = _getIsArchived(myarg)
|
|
17871
18034
|
elif myarg in {'children', 'child'}:
|
|
17872
18035
|
showChildren = True
|
|
17873
18036
|
else:
|
|
@@ -17898,38 +18061,29 @@ def _doInfoOrgs(entityList):
|
|
|
17898
18061
|
printKeyValueWithCRsNLs(field, value)
|
|
17899
18062
|
if getUsers:
|
|
17900
18063
|
orgUnitPath = result['orgUnitPath']
|
|
17901
|
-
|
|
17902
|
-
|
|
17903
|
-
|
|
17904
|
-
|
|
17905
|
-
|
|
17906
|
-
|
|
17907
|
-
|
|
17908
|
-
Ind.Increment()
|
|
17909
|
-
orgUnitPath = orgUnitPath.lower()
|
|
17910
|
-
for user in users:
|
|
17911
|
-
if orgUnitPath == user['orgUnitPath'].lower():
|
|
17912
|
-
printKeyValueList([user['primaryEmail']])
|
|
17913
|
-
usersInOU += 1
|
|
17914
|
-
elif showChildren:
|
|
17915
|
-
printKeyValueList([f'{user["primaryEmail"]} (child)'])
|
|
17916
|
-
usersInOU += 1
|
|
17917
|
-
Ind.Decrement()
|
|
17918
|
-
printKeyValueList([Msg.TOTAL_ITEMS_IN_ENTITY.format(Ent.Plural(entityType), Ent.Singular(Ent.ORGANIZATIONAL_UNIT)), usersInOU])
|
|
18064
|
+
if isArchived is None and isSuspended is None:
|
|
18065
|
+
_printUsers(Ent.USER, orgUnitPath, None, None)
|
|
18066
|
+
else:
|
|
18067
|
+
if isArchived is not None:
|
|
18068
|
+
_printUsers(Ent.USER_ARCHIVED if isArchived else Ent.USER_NOT_ARCHIVED, orgUnitPath, None, isArchived)
|
|
18069
|
+
if isSuspended is not None:
|
|
18070
|
+
_printUsers(Ent.USER_SUSPENDED if isSuspended else Ent.USER_NOT_SUSPENDED, orgUnitPath, isSuspended, None)
|
|
17919
18071
|
Ind.Decrement()
|
|
17920
18072
|
except (GAPI.invalidInput, GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.backendError):
|
|
17921
18073
|
entityActionFailedWarning([Ent.ORGANIZATIONAL_UNIT, orgUnitPath], Msg.DOES_NOT_EXIST, i, count)
|
|
17922
18074
|
except (GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
|
|
17923
18075
|
checkEntityAFDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
|
|
17924
18076
|
|
|
17925
|
-
# gam info
|
|
17926
|
-
|
|
17927
|
-
_doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True))
|
|
17928
|
-
|
|
17929
|
-
# gam info org|ou <OrgUnitItem> [nousers|notsuspended|suspended] [children|child]
|
|
18077
|
+
# gam info org|ou <OrgUnitItem>
|
|
18078
|
+
# [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
|
|
17930
18079
|
def doInfoOrg():
|
|
17931
18080
|
_doInfoOrgs([getOrgUnitItem()])
|
|
17932
18081
|
|
|
18082
|
+
# gam info orgs|ous <OrgUnitEntity>
|
|
18083
|
+
# [nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
|
|
18084
|
+
def doInfoOrgs():
|
|
18085
|
+
_doInfoOrgs(getEntityList(Cmd.OB_ORGUNIT_ENTITY, shlexSplit=True))
|
|
18086
|
+
|
|
17933
18087
|
ORG_ARGUMENT_TO_FIELD_MAP = {
|
|
17934
18088
|
'blockinheritance': 'blockInheritance',
|
|
17935
18089
|
'inheritanceblocked': 'blockInheritance',
|
|
@@ -18159,7 +18313,7 @@ def doPrintOrgs():
|
|
|
18159
18313
|
return
|
|
18160
18314
|
if showUserCounts:
|
|
18161
18315
|
for orgUnit in orgUnits:
|
|
18162
|
-
userCounts[orgUnit['orgUnitPath']] = [0, 0]
|
|
18316
|
+
userCounts[orgUnit['orgUnitPath']] = {'suspended': [0, 0], 'archived': [0, 0], 'total': 0}
|
|
18163
18317
|
qualifier = Msg.IN_THE.format(Ent.Singular(Ent.ORGANIZATIONAL_UNIT))
|
|
18164
18318
|
printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, qualifier=qualifier, entityType=Ent.ORGANIZATIONAL_UNIT)
|
|
18165
18319
|
pageMessage = getPageMessageForWhom()
|
|
@@ -18169,12 +18323,14 @@ def doPrintOrgs():
|
|
|
18169
18323
|
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
|
|
18170
18324
|
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
18171
18325
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
18172
|
-
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None), orderBy='email',
|
|
18173
|
-
fields='nextPageToken,users(orgUnitPath,suspended)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
18326
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None), orderBy='email',
|
|
18327
|
+
fields='nextPageToken,users(orgUnitPath,suspended,archived)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
18174
18328
|
for users in feed:
|
|
18175
18329
|
for user in users:
|
|
18176
18330
|
if user['orgUnitPath'] in userCounts:
|
|
18177
|
-
userCounts[user['orgUnitPath']][user['suspended']] += 1
|
|
18331
|
+
userCounts[user['orgUnitPath']]['suspended'][user['suspended']] += 1
|
|
18332
|
+
userCounts[user['orgUnitPath']]['archived'][user['archived']] += 1
|
|
18333
|
+
userCounts[user['orgUnitPath']]['total'] += 1
|
|
18178
18334
|
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError,
|
|
18179
18335
|
GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
|
|
18180
18336
|
checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
|
|
@@ -18241,15 +18397,34 @@ def doPrintOrgs():
|
|
|
18241
18397
|
(maxCrOSCounts != -1 and total > maxCrOSCounts)):
|
|
18242
18398
|
continue
|
|
18243
18399
|
if showUserCounts:
|
|
18244
|
-
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}
|
|
18245
|
-
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}
|
|
18246
|
-
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}
|
|
18400
|
+
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotArchived'] = userCounts[orgUnitPath]['archived'][0]
|
|
18401
|
+
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Archived'] = userCounts[orgUnitPath]['archived'][1]
|
|
18402
|
+
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}NotSuspended'] = userCounts[orgUnitPath]['suspended'][0]
|
|
18403
|
+
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Suspended'] = userCounts[orgUnitPath]['suspended'][1]
|
|
18404
|
+
row[f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}Total'] = total = userCounts[orgUnitPath]['total']
|
|
18247
18405
|
if ((minUserCounts != -1 and total < minUserCounts) or
|
|
18248
18406
|
(maxUserCounts != -1 and total > maxUserCounts)):
|
|
18249
18407
|
continue
|
|
18250
18408
|
csvPF.WriteRowTitles(row)
|
|
18251
18409
|
else:
|
|
18252
18410
|
csvPF.WriteRow(row)
|
|
18411
|
+
if showCrOSCounts or showUserCounts:
|
|
18412
|
+
crosTitles = []
|
|
18413
|
+
userTitles = []
|
|
18414
|
+
allTitles = csvPF.titlesList[:]
|
|
18415
|
+
for title in allTitles:
|
|
18416
|
+
if showCrOSCounts and title.startswith(f'CrOS{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'):
|
|
18417
|
+
crosTitles.append(title)
|
|
18418
|
+
csvPF.RemoveTitles(title)
|
|
18419
|
+
if showUserCounts and title.startswith(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}'):
|
|
18420
|
+
userTitles.append(title)
|
|
18421
|
+
csvPF.RemoveTitles(title)
|
|
18422
|
+
if showCrOSCounts:
|
|
18423
|
+
csvPF.AddTitles(sorted(crosTitles))
|
|
18424
|
+
if showUserCounts:
|
|
18425
|
+
for title in ['NotArchived', 'Archived', 'NotSuspended', 'Suspended', 'Total']:
|
|
18426
|
+
csvPF.AddTitles(f'Users{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{title}')
|
|
18427
|
+
csvPF.SetSortTitles([])
|
|
18253
18428
|
csvPF.writeCSVfile('Orgs')
|
|
18254
18429
|
|
|
18255
18430
|
# gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [Boolean>]]
|
|
@@ -18459,7 +18634,7 @@ def doCheckOrgUnit():
|
|
|
18459
18634
|
throwReasons=[GAPI.INVALID_ORGUNIT, GAPI.ORGUNIT_NOT_FOUND,
|
|
18460
18635
|
GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
18461
18636
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
18462
|
-
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None),
|
|
18637
|
+
customer=GC.Values[GC.CUSTOMER_ID], query=orgUnitPathQuery(orgUnitPath, None, None),
|
|
18463
18638
|
fields='nextPageToken,users(orgUnitPath)', maxResults=GC.Values[GC.USER_MAX_RESULTS])
|
|
18464
18639
|
for users in feed:
|
|
18465
18640
|
for user in users:
|
|
@@ -18880,7 +19055,7 @@ def makeUserGroupDomainQueryFilters(kwargsDict):
|
|
|
18880
19055
|
kwargsQueries.append((kwargs, query))
|
|
18881
19056
|
return kwargsQueries
|
|
18882
19057
|
|
|
18883
|
-
def userFilters(kwargs, query, orgUnitPath, isSuspended):
|
|
19058
|
+
def userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived):
|
|
18884
19059
|
queryTitle = ''
|
|
18885
19060
|
if kwargs.get('domain'):
|
|
18886
19061
|
queryTitle += f'domain={kwargs["domain"]}, '
|
|
@@ -18899,6 +19074,12 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended):
|
|
|
18899
19074
|
else:
|
|
18900
19075
|
query += ' '
|
|
18901
19076
|
query += f'isSuspended={isSuspended}'
|
|
19077
|
+
if isArchived is not None:
|
|
19078
|
+
if query is None:
|
|
19079
|
+
query = ''
|
|
19080
|
+
else:
|
|
19081
|
+
query += ' '
|
|
19082
|
+
query += f'isArchived={isArchived}'
|
|
18902
19083
|
if query is not None:
|
|
18903
19084
|
queryTitle += f'query="{query}", '
|
|
18904
19085
|
if queryTitle:
|
|
@@ -18910,7 +19091,7 @@ def userFilters(kwargs, query, orgUnitPath, isSuspended):
|
|
|
18910
19091
|
# [limittoou <OrgUnitItem>])
|
|
18911
19092
|
# [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
|
|
18912
19093
|
# [select <UserTypeEntity>]
|
|
18913
|
-
# [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
19094
|
+
# [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
18914
19095
|
# [shownoneditable] [nogroups] [nousers]
|
|
18915
19096
|
# [onerowpertarget] [delimiter <Character>]
|
|
18916
19097
|
# [suppressnoaliasrows]
|
|
@@ -18956,7 +19137,7 @@ def doPrintAliases():
|
|
|
18956
19137
|
groups = []
|
|
18957
19138
|
users = []
|
|
18958
19139
|
aliasMatchPattern = re.compile(r'^.*$')
|
|
18959
|
-
isSuspended = orgUnitPath = None
|
|
19140
|
+
isArchived = isSuspended = orgUnitPath = None
|
|
18960
19141
|
addCSVData = {}
|
|
18961
19142
|
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
|
18962
19143
|
while Cmd.ArgumentsRemaining():
|
|
@@ -18982,6 +19163,8 @@ def doPrintAliases():
|
|
|
18982
19163
|
_, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
18983
19164
|
elif myarg == 'issuspended':
|
|
18984
19165
|
isSuspended = getBoolean()
|
|
19166
|
+
elif myarg == 'isarchived':
|
|
19167
|
+
isArchived = getBoolean()
|
|
18985
19168
|
elif myarg in {'user','users'}:
|
|
18986
19169
|
users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0)))
|
|
18987
19170
|
elif myarg in {'group', 'groups'}:
|
|
@@ -19016,7 +19199,7 @@ def doPrintAliases():
|
|
|
19016
19199
|
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
|
19017
19200
|
kwargs = kwargsQuery[0]
|
|
19018
19201
|
query = kwargsQuery[1]
|
|
19019
|
-
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
|
|
19202
|
+
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived)
|
|
19020
19203
|
printGettingAllAccountEntities(Ent.USER, pquery)
|
|
19021
19204
|
try:
|
|
19022
19205
|
entityList = callGAPIpages(cd.users(), 'list', 'users',
|
|
@@ -22418,12 +22601,12 @@ def infoUserPeopleContacts(users):
|
|
|
22418
22601
|
|
|
22419
22602
|
# gam <UserTypeEntity> print contacts [todrive <ToDriveAttribute>*] <PeoplePrintShowUserContactSelection>
|
|
22420
22603
|
# [showgroups|showgroupnameslist] [orderby firstname|lastname|(lastmodified ascending)|(lastnodified descending)
|
|
22421
|
-
# [
|
|
22422
|
-
# [formatjson [quotechar <Character>]]
|
|
22604
|
+
# [allfields|fields <PeopleFieldNameList>] [showmetadata]
|
|
22605
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
22423
22606
|
# gam <UserTypeEntity> show contacts <PeoplePrintShowUserContactSelection>
|
|
22424
22607
|
# [showgroups] [orderby firstname|lastname|(lastmodified ascending)|(lastnodified descending)
|
|
22425
|
-
# [
|
|
22426
|
-
# [formatjson]
|
|
22608
|
+
# [allfields|(fields <PeopleFieldNameList>)] [showmetadata]
|
|
22609
|
+
# [countsonly|formatjson]
|
|
22427
22610
|
def printShowUserPeopleContacts(users):
|
|
22428
22611
|
entityType = Ent.USER
|
|
22429
22612
|
entityTypeName = Ent.Singular(entityType)
|
|
@@ -22462,6 +22645,8 @@ def printShowUserPeopleContacts(users):
|
|
|
22462
22645
|
else:
|
|
22463
22646
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
22464
22647
|
if countsOnly:
|
|
22648
|
+
if csvPF:
|
|
22649
|
+
csvPF.SetFormatJSON(False)
|
|
22465
22650
|
fieldsList = ['emailAddresses']
|
|
22466
22651
|
if contactQuery['mainContacts']:
|
|
22467
22652
|
fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters)
|
|
@@ -22699,11 +22884,11 @@ def processUserPeopleOtherContacts(users):
|
|
|
22699
22884
|
Ind.Decrement()
|
|
22700
22885
|
|
|
22701
22886
|
# gam <UserTypeEntity> print othercontacts [todrive <ToDriveAttribute>*] <OtherContactSelection>
|
|
22702
|
-
# [
|
|
22703
|
-
# [formatjson [quotechar <Character>]]
|
|
22887
|
+
# [allfields|(fields <OtherContactFieldNameList>)] [showmetadata]
|
|
22888
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
22704
22889
|
# gam <UserTypeEntity> show othercontacts <OtherContactSelection>
|
|
22705
|
-
# [
|
|
22706
|
-
# [formatjson]
|
|
22890
|
+
# [allfields|(fields <OtherContactFieldNameList>)] [showmetadata]
|
|
22891
|
+
# [countsonly|formatjson]
|
|
22707
22892
|
def printShowUserPeopleOtherContacts(users):
|
|
22708
22893
|
entityType = Ent.USER
|
|
22709
22894
|
entityTypeName = Ent.Singular(entityType)
|
|
@@ -22733,6 +22918,8 @@ def printShowUserPeopleOtherContacts(users):
|
|
|
22733
22918
|
else:
|
|
22734
22919
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
22735
22920
|
if countsOnly:
|
|
22921
|
+
if csvPF:
|
|
22922
|
+
csvPF.SetFormatJSON(False)
|
|
22736
22923
|
fieldsList = ['emailAddresses']
|
|
22737
22924
|
fields = _getPersonFields(PEOPLE_OTHER_CONTACTS_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters)
|
|
22738
22925
|
i, count, users = getEntityArgument(users)
|
|
@@ -22802,6 +22989,8 @@ def _printShowPeople(source):
|
|
|
22802
22989
|
function = 'searchDirectoryPeople'
|
|
22803
22990
|
else:
|
|
22804
22991
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
22992
|
+
if countsOnly and csvPF:
|
|
22993
|
+
csvPF.SetFormatJSON(False)
|
|
22805
22994
|
fields = _getPersonFields(PEOPLE_FIELDS_CHOICE_MAP, PEOPLE_CONTACTS_DEFAULT_FIELDS, fieldsList, parameters)
|
|
22806
22995
|
printGettingAllEntityItemsForWhom(peopleEntityType, GC.Values[GC.DOMAIN], query=kwargs.get('query'))
|
|
22807
22996
|
try:
|
|
@@ -22839,29 +23028,24 @@ def doInfoDomainPeopleContacts():
|
|
|
22839
23028
|
# gam print people|peopleprofile [todrive <ToDriveAttribute>*]
|
|
22840
23029
|
# [query <String>]
|
|
22841
23030
|
# [mergesources <PeopleMergeSourceName>]
|
|
22842
|
-
# [countsonly]
|
|
22843
23031
|
# [allfields|(fields <PeopleFieldNameList>)] [showmetadata]
|
|
22844
|
-
# [formatjson [quotechar <Character>]]
|
|
23032
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
22845
23033
|
# gam show people|peopleprofile
|
|
22846
23034
|
# [query <String>]
|
|
22847
23035
|
# [mergesources <PeopleMergeSourceName>]
|
|
22848
|
-
# [countsonly]
|
|
22849
23036
|
# [allfields|(fields <PeopleFieldNameList>)] [showmetadata]
|
|
22850
|
-
# [
|
|
23037
|
+
# [countsonlyformatjson]
|
|
22851
23038
|
# gam print domaincontacts|peoplecontacts [todrive <ToDriveAttribute>*]
|
|
22852
23039
|
# [sources <PeopleSourceName>]
|
|
22853
23040
|
# [query <String>]
|
|
22854
23041
|
# [mergesources <PeopleMergeSourceName>]
|
|
22855
|
-
# [countsonly]
|
|
22856
23042
|
# [allfields|(fields <PeopleFieldNameList>)] [showmetadata]
|
|
22857
|
-
# [formatjson [quotechar <Character>]]
|
|
23043
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
22858
23044
|
# gam show domaincontacts|peoplecontacts
|
|
22859
23045
|
# [sources <PeopleSourceName>]
|
|
22860
23046
|
# [query <String>]
|
|
22861
23047
|
# [mergesources <PeopleMergeSourceName>]
|
|
22862
|
-
# [
|
|
22863
|
-
# [allfields|(fields <PeopleFieldNameList>)] [showmetadata]
|
|
22864
|
-
# [formatjson]
|
|
23048
|
+
# [countsonlyformatjson]
|
|
22865
23049
|
def doPrintShowDomainPeopleProfiles():
|
|
22866
23050
|
_printShowPeople('profile')
|
|
22867
23051
|
|
|
@@ -23848,7 +24032,7 @@ def _filterActiveTimeRanges(cros, selected, listLimit, startDate, endDate, activ
|
|
|
23848
24032
|
activeTimeRanges.reverse()
|
|
23849
24033
|
i = 0
|
|
23850
24034
|
for item in activeTimeRanges:
|
|
23851
|
-
activityDate =
|
|
24035
|
+
activityDate = arrow.Arrow.strptime(item['date'], YYYYMMDD_FORMAT)
|
|
23852
24036
|
if ((startDate is None) or (activityDate >= startDate)) and ((endDate is None) or (activityDate <= endDate)):
|
|
23853
24037
|
item['duration'] = formatMilliSeconds(item['activeTime'])
|
|
23854
24038
|
item['minutes'] = item['activeTime']//60000
|
|
@@ -23867,7 +24051,7 @@ def _filterDeviceFiles(cros, selected, listLimit, startTime, endTime):
|
|
|
23867
24051
|
filteredItems = []
|
|
23868
24052
|
i = 0
|
|
23869
24053
|
for item in cros.get('deviceFiles', []):
|
|
23870
|
-
timeValue
|
|
24054
|
+
timeValue = arrow.get(item['createTime'])
|
|
23871
24055
|
if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
|
|
23872
24056
|
item['createTime'] = formatLocalTime(item['createTime'])
|
|
23873
24057
|
filteredItems.append(item)
|
|
@@ -23884,7 +24068,7 @@ def _filterCPUStatusReports(cros, selected, listLimit, startTime, endTime):
|
|
|
23884
24068
|
filteredItems = []
|
|
23885
24069
|
i = 0
|
|
23886
24070
|
for item in cros.get('cpuStatusReports', []):
|
|
23887
|
-
timeValue
|
|
24071
|
+
timeValue = arrow.get(item['reportTime'])
|
|
23888
24072
|
if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
|
|
23889
24073
|
item['reportTime'] = formatLocalTime(item['reportTime'])
|
|
23890
24074
|
for tempInfo in item.get('cpuTemperatureInfo', []):
|
|
@@ -23905,7 +24089,7 @@ def _filterSystemRamFreeReports(cros, selected, listLimit, startTime, endTime):
|
|
|
23905
24089
|
filteredItems = []
|
|
23906
24090
|
i = 0
|
|
23907
24091
|
for item in cros.get('systemRamFreeReports', []):
|
|
23908
|
-
timeValue
|
|
24092
|
+
timeValue = arrow.get(item['reportTime'])
|
|
23909
24093
|
if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
|
|
23910
24094
|
item['reportTime'] = formatLocalTime(item['reportTime'])
|
|
23911
24095
|
item['systemRamFreeInfo'] = ','.join([str(x) for x in item['systemRamFreeInfo']])
|
|
@@ -23938,7 +24122,7 @@ def _filterScreenshotFiles(cros, selected, listLimit, startTime, endTime):
|
|
|
23938
24122
|
filteredItems = []
|
|
23939
24123
|
i = 0
|
|
23940
24124
|
for item in cros.get('screenshotFiles', []):
|
|
23941
|
-
timeValue
|
|
24125
|
+
timeValue = arrow.get(item['createTime'])
|
|
23942
24126
|
if ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime)):
|
|
23943
24127
|
item['createTime'] = formatLocalTime(item['createTime'])
|
|
23944
24128
|
filteredItems.append(item)
|
|
@@ -23975,7 +24159,7 @@ def _computeDVRstorageFreePercentage(cros):
|
|
|
23975
24159
|
|
|
23976
24160
|
def _getFilterDateTime():
|
|
23977
24161
|
filterDate = getYYYYMMDD(returnDateTime=True)
|
|
23978
|
-
return (filterDate, filterDate.replace(tzinfo=
|
|
24162
|
+
return (filterDate, filterDate.replace(tzinfo='UTC'))
|
|
23979
24163
|
|
|
23980
24164
|
CROS_FIELDS_CHOICE_MAP = {
|
|
23981
24165
|
'activetimeranges': ['activeTimeRanges.activeTime', 'activeTimeRanges.date'],
|
|
@@ -24093,6 +24277,7 @@ CROS_LIST_FIELDS_CHOICE_MAP = {
|
|
|
24093
24277
|
|
|
24094
24278
|
CROS_TIME_OBJECTS = {
|
|
24095
24279
|
'createTime',
|
|
24280
|
+
'extendedSupportStart',
|
|
24096
24281
|
'firstEnrollmentTime',
|
|
24097
24282
|
'lastDeprovisionTimestamp',
|
|
24098
24283
|
'lastEnrollmentTime',
|
|
@@ -24386,9 +24571,9 @@ def getDeviceFilesEntity():
|
|
|
24386
24571
|
else:
|
|
24387
24572
|
for timeItem in myarg.split(','):
|
|
24388
24573
|
try:
|
|
24389
|
-
timestamp
|
|
24574
|
+
timestamp = arrow.get(timeItem)
|
|
24390
24575
|
deviceFilesEntity['list'].append(ISOformatTimeStamp(timestamp.astimezone(GC.Values[GC.TIMEZONE])))
|
|
24391
|
-
except (
|
|
24576
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
24392
24577
|
Cmd.Backup()
|
|
24393
24578
|
invalidArgumentExit(YYYYMMDDTHHMMSS_FORMAT_REQUIRED)
|
|
24394
24579
|
return deviceFilesEntity
|
|
@@ -24415,14 +24600,14 @@ def _selectDeviceFiles(deviceId, deviceFiles, deviceFilesEntity):
|
|
|
24415
24600
|
count = 0
|
|
24416
24601
|
if deviceFilesEntity['time'][0] == 'before':
|
|
24417
24602
|
for deviceFile in deviceFiles:
|
|
24418
|
-
createTime
|
|
24603
|
+
createTime = arrow.get(deviceFile['createTime'])
|
|
24419
24604
|
if createTime >= dateTime:
|
|
24420
24605
|
break
|
|
24421
24606
|
count += 1
|
|
24422
24607
|
return deviceFiles[:count]
|
|
24423
24608
|
# if deviceFilesEntity['time'][0] == 'after':
|
|
24424
24609
|
for deviceFile in deviceFiles:
|
|
24425
|
-
createTime
|
|
24610
|
+
createTime = arrow.get(deviceFile['createTime'])
|
|
24426
24611
|
if createTime >= dateTime:
|
|
24427
24612
|
break
|
|
24428
24613
|
count += 1
|
|
@@ -24431,14 +24616,14 @@ def _selectDeviceFiles(deviceId, deviceFiles, deviceFilesEntity):
|
|
|
24431
24616
|
dateTime = deviceFilesEntity['range'][1]
|
|
24432
24617
|
spos = 0
|
|
24433
24618
|
for deviceFile in deviceFiles:
|
|
24434
|
-
createTime
|
|
24619
|
+
createTime = arrow.get(deviceFile['createTime'])
|
|
24435
24620
|
if createTime >= dateTime:
|
|
24436
24621
|
break
|
|
24437
24622
|
spos += 1
|
|
24438
24623
|
dateTime = deviceFilesEntity['range'][2]
|
|
24439
24624
|
epos = spos
|
|
24440
24625
|
for deviceFile in deviceFiles[spos:]:
|
|
24441
|
-
createTime
|
|
24626
|
+
createTime = arrow.get(deviceFile['createTime'])
|
|
24442
24627
|
if createTime >= dateTime:
|
|
24443
24628
|
break
|
|
24444
24629
|
epos += 1
|
|
@@ -25221,7 +25406,7 @@ def doInfoPrintShowCrOSTelemetry():
|
|
|
25221
25406
|
i = 0
|
|
25222
25407
|
for item in listItems:
|
|
25223
25408
|
if 'reportTime' in item:
|
|
25224
|
-
timeValue
|
|
25409
|
+
timeValue = arrow.get(item['reportTime'])
|
|
25225
25410
|
else:
|
|
25226
25411
|
timeValue = None
|
|
25227
25412
|
if (timeValue is None) or (((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime))):
|
|
@@ -26372,14 +26557,18 @@ CHAT_TIME_OBJECTS = {'createTime', 'deleteTime', 'eventTime', 'lastActiveTime',
|
|
|
26372
26557
|
def _showChatItem(citem, entityType, FJQC, i=0, count=0):
|
|
26373
26558
|
if entityType == Ent.CHAT_SPACE:
|
|
26374
26559
|
_cleanChatSpace(citem)
|
|
26560
|
+
dictObjectsKey = {None: 'displayName'}
|
|
26375
26561
|
elif entityType == Ent.CHAT_MESSAGE:
|
|
26376
26562
|
_cleanChatMessage(citem)
|
|
26563
|
+
dictObjectsKey = {None: 'text'}
|
|
26564
|
+
else:
|
|
26565
|
+
dictObjectsKey={}
|
|
26377
26566
|
if FJQC.formatJSON:
|
|
26378
26567
|
printLine(json.dumps(cleanJSON(citem, timeObjects=CHAT_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
|
|
26379
26568
|
return
|
|
26380
26569
|
printEntity([entityType, citem['name']], i, count)
|
|
26381
26570
|
Ind.Increment()
|
|
26382
|
-
showJSON(None, citem, timeObjects=CHAT_TIME_OBJECTS)
|
|
26571
|
+
showJSON(None, citem, timeObjects=CHAT_TIME_OBJECTS, dictObjectsKey=dictObjectsKey)
|
|
26383
26572
|
Ind.Decrement()
|
|
26384
26573
|
|
|
26385
26574
|
def _printChatItem(user, citem, parent, entityType, csvPF, FJQC, addCSVData=None):
|
|
@@ -26568,7 +26757,7 @@ def printShowChatEmojis(users):
|
|
|
26568
26757
|
pageMessage=_getChatPageMessage(Ent.CHAT_EMOJI, user, i, count, pfilter),
|
|
26569
26758
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
26570
26759
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
26571
|
-
pageSize=
|
|
26760
|
+
pageSize=GC.Values[GC.CHAT_MAX_RESULTS], filter=pfilter)
|
|
26572
26761
|
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
|
26573
26762
|
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
|
|
26574
26763
|
continue
|
|
@@ -26638,7 +26827,14 @@ def getChatSpaceParameters(myarg, body, typeChoicesMap, updateMask):
|
|
|
26638
26827
|
|
|
26639
26828
|
CHAT_MEMBER_ROLE_MAP = {
|
|
26640
26829
|
'member': 'ROLE_MEMBER',
|
|
26641
|
-
'manager': '
|
|
26830
|
+
'manager': 'ROLE_ASSISTANT_MANAGER',
|
|
26831
|
+
'owner': 'ROLE_MANAGER',
|
|
26832
|
+
}
|
|
26833
|
+
|
|
26834
|
+
CHAT_ROLE_ENTITY_TYPE_MAP = {
|
|
26835
|
+
'ROLE_MEMBER': Ent.CHAT_MEMBER_USER,
|
|
26836
|
+
'ROLE_ASSISTANT_MANAGER': Ent.CHAT_MANAGER_USER,
|
|
26837
|
+
'ROLE_MANAGER': Ent.CHAT_OWNER_USER,
|
|
26642
26838
|
}
|
|
26643
26839
|
|
|
26644
26840
|
CHAT_MEMBER_TYPE_MAP = {
|
|
@@ -26772,7 +26968,8 @@ CHAT_UPDATE_SPACE_TYPE_MAP = {
|
|
|
26772
26968
|
}
|
|
26773
26969
|
|
|
26774
26970
|
CHAT_SPACE_ROLE_PERMISSIONS_MAP = {
|
|
26775
|
-
'
|
|
26971
|
+
'owners': 'managersAllowed',
|
|
26972
|
+
'managers': 'assistantManagersAllowed',
|
|
26776
26973
|
'members': 'membersAllowed',
|
|
26777
26974
|
}
|
|
26778
26975
|
|
|
@@ -26792,13 +26989,13 @@ CHAT_UPDATE_SPACE_PERMISSIONS_MAP = {
|
|
|
26792
26989
|
# [type space]
|
|
26793
26990
|
# [description <String>] [guidelines|rules <String>]
|
|
26794
26991
|
# [history <Boolean>])
|
|
26795
|
-
# managemembersandgroups managers|members
|
|
26796
|
-
# modifyspacedetails managers|members
|
|
26797
|
-
# togglehistory managers|members
|
|
26798
|
-
# useatmentionall managers|members
|
|
26799
|
-
# manageapps managers|members
|
|
26800
|
-
# managewebhooks managers|members
|
|
26801
|
-
# replymessages managers|members
|
|
26992
|
+
# [managemembersandgroups owners|managers|members]
|
|
26993
|
+
# [modifyspacedetails owners|managers|members]
|
|
26994
|
+
# [togglehistory owners|managers|members]
|
|
26995
|
+
# [useatmentionall owners|managers|members]
|
|
26996
|
+
# [manageapps owners|managers|members]
|
|
26997
|
+
# [managewebhooks owners|managers|members]
|
|
26998
|
+
2# [replymessages owners|managers|members]
|
|
26802
26999
|
# [formatjson]
|
|
26803
27000
|
def updateChatSpace(users):
|
|
26804
27001
|
FJQC = FormatJSONQuoteChar()
|
|
@@ -26816,9 +27013,13 @@ def updateChatSpace(users):
|
|
|
26816
27013
|
body.setdefault('permissionSettings', {})
|
|
26817
27014
|
permissionSetting = CHAT_UPDATE_SPACE_PERMISSIONS_MAP[myarg]
|
|
26818
27015
|
role = getChoice(CHAT_SPACE_ROLE_PERMISSIONS_MAP, mapChoice=True)
|
|
26819
|
-
body['permissionSettings'][permissionSetting] = {
|
|
27016
|
+
body['permissionSettings'][permissionSetting] = {}
|
|
27017
|
+
body['permissionSettings'][permissionSetting][role] = True
|
|
26820
27018
|
if role == 'membersAllowed':
|
|
26821
|
-
body['permissionSettings'][permissionSetting]
|
|
27019
|
+
body['permissionSettings'][permissionSetting]['assistantManagersAllowed'] = True
|
|
27020
|
+
body['permissionSettings'][permissionSetting]['managersAllowed'] = True
|
|
27021
|
+
elif role == 'assistantManagersAllowed':
|
|
27022
|
+
body['permissionSettings'][permissionSetting]['managersAllowed'] = True
|
|
26822
27023
|
updateMask.add(f'permissionSettings.{permissionSetting}')
|
|
26823
27024
|
else:
|
|
26824
27025
|
FJQC.GetFormatJSON(myarg)
|
|
@@ -26995,7 +27196,6 @@ def _getChatSpaceSearchParms(myarg, queries, queryTimes, OBY):
|
|
|
26995
27196
|
return False
|
|
26996
27197
|
return True
|
|
26997
27198
|
|
|
26998
|
-
CHAT_PAGE_SIZE = 1000
|
|
26999
27199
|
CHAT_SPACES_ADMIN_ORDERBY_CHOICE_MAP = {
|
|
27000
27200
|
'createtime': 'createTime',
|
|
27001
27201
|
'lastactivetime': 'lastActiveTime',
|
|
@@ -27075,7 +27275,7 @@ def printShowChatSpaces(users):
|
|
|
27075
27275
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
|
|
27076
27276
|
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27077
27277
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27078
|
-
fields=fields, pageSize=
|
|
27278
|
+
fields=fields, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargsCS)
|
|
27079
27279
|
if showAccessSettings:
|
|
27080
27280
|
for space in spaces:
|
|
27081
27281
|
if space['spaceType'] == 'SPACE':
|
|
@@ -27141,7 +27341,7 @@ def _getChatSpaceMembers(cd, chatSpace, ciGroupName):
|
|
|
27141
27341
|
pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, 0, 0, qfilter),
|
|
27142
27342
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
27143
27343
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27144
|
-
parent=chatSpace, fields=fields, pageSize=
|
|
27344
|
+
parent=chatSpace, fields=fields, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargsUAA)
|
|
27145
27345
|
for member in members:
|
|
27146
27346
|
_getChatMemberEmail(cd, member)
|
|
27147
27347
|
gmember = {}
|
|
@@ -27193,12 +27393,12 @@ def getGroupMemberID(cd, group, groupList):
|
|
|
27193
27393
|
groupList.append(convertEmailAddressToUID(group, cd, emailType='group'))
|
|
27194
27394
|
|
|
27195
27395
|
# gam <UserTypeEntity> create chatmember <ChatSpace>
|
|
27196
|
-
# [type human|bot] [role member|manager]
|
|
27396
|
+
# [type human|bot] [role member|manager|owner]
|
|
27197
27397
|
# (user <UserItem>)* (members <UserTypeEntity>)*
|
|
27198
27398
|
# (group <GroupItem>)* (groups <GroupEntity>)*
|
|
27199
27399
|
# [formatjson|returnidonly]
|
|
27200
27400
|
# gam <UserItem> create chatmember asadmin <ChatSpace>
|
|
27201
|
-
# [type human|bot] [role member|manager]
|
|
27401
|
+
# [type human|bot] [role member|manager|owner]
|
|
27202
27402
|
# (user <UserItem>)* (members <UserTypeEntity>)*
|
|
27203
27403
|
# (group <GroupItem>)* (groups <GroupEntity>)*
|
|
27204
27404
|
# [formatjson|returnidonly]
|
|
@@ -27220,7 +27420,7 @@ def createChatMember(users):
|
|
|
27220
27420
|
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.NOT_FOUND, GAPI.INVALID, GAPI.INVALID_ARGUMENT,
|
|
27221
27421
|
GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27222
27422
|
parent=parent, body=body, **kwargsUAA)
|
|
27223
|
-
if role != 'ROLE_MEMBER' and entityType
|
|
27423
|
+
if role != 'ROLE_MEMBER' and entityType in (Ent.CHAT_MANAGER_USER, Ent.CHAT_OWNER_USER):
|
|
27224
27424
|
member = callGAPI(chat.spaces().members(), 'patch',
|
|
27225
27425
|
bailOnInternalError=True,
|
|
27226
27426
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
|
|
@@ -27278,7 +27478,7 @@ def createChatMember(users):
|
|
|
27278
27478
|
missingArgumentExit('space')
|
|
27279
27479
|
if not userList and not groupList:
|
|
27280
27480
|
missingArgumentExit('user|members|group|groups')
|
|
27281
|
-
userEntityType =
|
|
27481
|
+
userEntityType = CHAT_ROLE_ENTITY_TYPE_MAP[role]
|
|
27282
27482
|
userMembers = []
|
|
27283
27483
|
for user in userList:
|
|
27284
27484
|
userMembers.append({'member': {'name': f'users/{user}', 'type': mtype}})
|
|
@@ -27327,16 +27527,16 @@ def _deleteChatMembers(chat, kvList, jcount, memberNames, i, count, kwargsUAA):
|
|
|
27327
27527
|
# gam <UserItem> remove chatmember asadmin
|
|
27328
27528
|
# members <ChatMemberList>
|
|
27329
27529
|
# gam <UserTypeEntity> update chatmember <ChatSpace>
|
|
27330
|
-
# role member|manager
|
|
27530
|
+
# role member|manager|owner
|
|
27331
27531
|
# ((user <UserItem>)|(members <UserTypeEntity>))+
|
|
27332
27532
|
# gam <UserTypeEntity> modify chatmember
|
|
27333
|
-
# role member|manager
|
|
27533
|
+
# role member|manager|owner
|
|
27334
27534
|
# members <ChatMemberList>
|
|
27335
27535
|
# gam <UserItem> update chatmember asadmin<ChatSpace>
|
|
27336
|
-
# role member|manager
|
|
27536
|
+
# role member|manager|owner
|
|
27337
27537
|
# ((user <UserItem>)|(members <UserTypeEntity>))+
|
|
27338
27538
|
# gam <UserItem> modify chatmember asadmin
|
|
27339
|
-
# role member|manager
|
|
27539
|
+
# role member|manager|owner
|
|
27340
27540
|
# members <ChatMemberList>
|
|
27341
27541
|
def deleteUpdateChatMember(users):
|
|
27342
27542
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
@@ -27430,7 +27630,7 @@ def deleteUpdateChatMember(users):
|
|
|
27430
27630
|
CHAT_SYNC_PREVIEW_TITLES = ['space', 'member', 'role', 'action', 'message']
|
|
27431
27631
|
|
|
27432
27632
|
# gam <UserTypeEntity> sync chatmembers [asadmin] <ChatSpace>
|
|
27433
|
-
# [role member|manager] [type human|bot]
|
|
27633
|
+
# [role member|manager|owner] [type human|bot]
|
|
27434
27634
|
# [addonly|removeonly]
|
|
27435
27635
|
# [preview [actioncsv]]
|
|
27436
27636
|
# (users <UserTypeEntity>)* (groups <GroupEntity>)*
|
|
@@ -27535,7 +27735,7 @@ def syncChatMembers(users):
|
|
|
27535
27735
|
unknownArgumentExit()
|
|
27536
27736
|
if not parent:
|
|
27537
27737
|
missingArgumentExit('space')
|
|
27538
|
-
userEntityType =
|
|
27738
|
+
userEntityType = CHAT_ROLE_ENTITY_TYPE_MAP[role]
|
|
27539
27739
|
userMembers = {}
|
|
27540
27740
|
syncUsersSet = set()
|
|
27541
27741
|
for user in userList:
|
|
@@ -27565,7 +27765,7 @@ def syncChatMembers(users):
|
|
|
27565
27765
|
pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, i, count, qfilter),
|
|
27566
27766
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27567
27767
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27568
|
-
parent=parent, showGroups=groupsSpecified, pageSize=
|
|
27768
|
+
parent=parent, showGroups=groupsSpecified, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargs, **kwargsUAA)
|
|
27569
27769
|
for member in members:
|
|
27570
27770
|
if 'member' in member:
|
|
27571
27771
|
if member['member']['type'] == mtype and member['role'] == role:
|
|
@@ -27766,7 +27966,7 @@ def printShowChatMembers(users):
|
|
|
27766
27966
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
|
|
27767
27967
|
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27768
27968
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27769
|
-
fields="nextPageToken,spaces(name,displayName,spaceType,membershipCount)", pageSize=
|
|
27969
|
+
fields="nextPageToken,spaces(name,displayName,spaceType,membershipCount)", pageSize=GC.Values[GC.CHAT_MAX_RESULTS],
|
|
27770
27970
|
**kwargsCS)
|
|
27771
27971
|
for space in sorted(spaces, key=lambda k: k[sortName]):
|
|
27772
27972
|
if space['spaceType'] == 'SPACE' and 'membershipCount' in space:
|
|
@@ -27783,7 +27983,7 @@ def printShowChatMembers(users):
|
|
|
27783
27983
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.INTERNAL_ERROR,
|
|
27784
27984
|
GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27785
27985
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27786
|
-
fields="nextPageToken,spaces(name,displayName,spaceType,membershipCount)", pageSize=
|
|
27986
|
+
fields="nextPageToken,spaces(name,displayName,spaceType,membershipCount)", pageSize=GC.Values[GC.CHAT_MAX_RESULTS],
|
|
27787
27987
|
**kwargsCS)
|
|
27788
27988
|
for space in sorted(spaces, key=lambda k: k[sortName]):
|
|
27789
27989
|
# if 'membershipCount' in space:
|
|
@@ -27814,7 +28014,7 @@ def printShowChatMembers(users):
|
|
|
27814
28014
|
pageMessage=_getChatPageMessage(Ent.CHAT_MEMBER, user, j, jcount, qfilter),
|
|
27815
28015
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
27816
28016
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
27817
|
-
parent=parentName, fields=fields, pageSize=
|
|
28017
|
+
parent=parentName, fields=fields, pageSize=GC.Values[GC.CHAT_MAX_RESULTS], **kwargs, **kwargsUAA)
|
|
27818
28018
|
for member in members:
|
|
27819
28019
|
_getChatMemberEmail(cd, member)
|
|
27820
28020
|
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
|
@@ -27846,10 +28046,11 @@ def _getChatSenderEmail(cd, sender):
|
|
|
27846
28046
|
sender['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{senderUid}', cd, None, emailTypes=['user'])
|
|
27847
28047
|
|
|
27848
28048
|
def trimChatMessageIfRequired(body):
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
|
|
27852
|
-
|
|
28049
|
+
if 'text' in body:
|
|
28050
|
+
msgLen = len(body['text'])
|
|
28051
|
+
if msgLen > 4096:
|
|
28052
|
+
stderrWarningMsg(Msg.TRIMMED_MESSAGE_FROM_LENGTH_TO_MAXIMUM.format(msgLen, 4096))
|
|
28053
|
+
body['text'] = body['text'][:4095]
|
|
27853
28054
|
|
|
27854
28055
|
CHAT_MESSAGE_REPLY_OPTION_MAP = {
|
|
27855
28056
|
'fail': 'REPLY_MESSAGE_OR_FAIL',
|
|
@@ -27926,22 +28127,29 @@ def doCreateChatMessage():
|
|
|
27926
28127
|
createChatMessage([None])
|
|
27927
28128
|
|
|
27928
28129
|
# gam [<UserTypeMessage>] update chatmessage name <ChatMessage>
|
|
27929
|
-
# <ChatContent>
|
|
28130
|
+
# [<ChatContent>] [clearattachments <String>]
|
|
27930
28131
|
def updateChatMessage(users):
|
|
27931
28132
|
name = None
|
|
27932
28133
|
body = {}
|
|
28134
|
+
updateMask = []
|
|
28135
|
+
clearMsg = ''
|
|
27933
28136
|
while Cmd.ArgumentsRemaining():
|
|
27934
28137
|
myarg = getArgument()
|
|
27935
28138
|
if myarg == 'name':
|
|
27936
28139
|
name = getString(Cmd.OB_CHAT_MESSAGE)
|
|
27937
28140
|
elif myarg in SORF_TEXT_ARGUMENTS:
|
|
27938
28141
|
body['text'] = getStringOrFile(myarg, minLen=0, unescapeCRLF=True)[0]
|
|
28142
|
+
updateMask.append('text')
|
|
28143
|
+
elif myarg == 'clearattachments':
|
|
28144
|
+
clearMsg = getString(Cmd.OB_STRING, minLen=0)
|
|
28145
|
+
body['attachment'] = []
|
|
28146
|
+
updateMask.append('attachment')
|
|
27939
28147
|
else:
|
|
27940
28148
|
unknownArgumentExit()
|
|
27941
28149
|
if not name:
|
|
27942
28150
|
missingArgumentExit('name')
|
|
27943
|
-
if
|
|
27944
|
-
missingArgumentExit('text
|
|
28151
|
+
if not updateMask:
|
|
28152
|
+
missingArgumentExit('text|textfile|clearattachments')
|
|
27945
28153
|
trimChatMessageIfRequired(body)
|
|
27946
28154
|
i, count, users = getEntityArgument(users)
|
|
27947
28155
|
for user in users:
|
|
@@ -27950,9 +28158,19 @@ def updateChatMessage(users):
|
|
|
27950
28158
|
if not chat:
|
|
27951
28159
|
continue
|
|
27952
28160
|
try:
|
|
28161
|
+
if 'attachment' in updateMask and 'text' not in updateMask:
|
|
28162
|
+
resp = callGAPI(chat.spaces().messages(), 'get',
|
|
28163
|
+
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
28164
|
+
name=name, fields='text')
|
|
28165
|
+
body['text'] = resp.get('text', '')
|
|
28166
|
+
if clearMsg:
|
|
28167
|
+
body['text'] += clearMsg
|
|
28168
|
+
elif not body['text']:
|
|
28169
|
+
body['text'] = 'Attachments cleared'
|
|
28170
|
+
updateMask.append('text')
|
|
27953
28171
|
resp = callGAPI(chat.spaces().messages(), 'patch',
|
|
27954
28172
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.FAILED_PRECONDITION],
|
|
27955
|
-
name=name, updateMask='
|
|
28173
|
+
name=name, updateMask=','.join(updateMask), body=body)
|
|
27956
28174
|
kvList.extend([Ent.CHAT_THREAD, resp['thread']['name']])
|
|
27957
28175
|
entityActionPerformed(kvList, i, count)
|
|
27958
28176
|
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
|
@@ -28128,7 +28346,7 @@ def printShowChatMessages(users):
|
|
|
28128
28346
|
pageMessage=_getChatPageMessage(Ent.CHAT_MESSAGE, user, i, count, qfilter),
|
|
28129
28347
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
28130
28348
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
28131
|
-
pageSize=
|
|
28349
|
+
pageSize=GC.Values[GC.CHAT_MAX_RESULTS], parent=parentName, filter=pfilter, showDeleted=showDeleted,
|
|
28132
28350
|
fields=fields)
|
|
28133
28351
|
for message in messages:
|
|
28134
28352
|
if 'sender' in message:
|
|
@@ -28246,7 +28464,7 @@ def printShowChatEvents(users):
|
|
|
28246
28464
|
pageMessage=_getChatPageMessage(Ent.CHAT_EVENT, user, i, count, qfilter),
|
|
28247
28465
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
28248
28466
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
28249
|
-
pageSize=
|
|
28467
|
+
pageSize=GC.Values[GC.CHAT_MAX_RESULTS], parent=parentName, filter=pfilter)
|
|
28250
28468
|
except (GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
|
28251
28469
|
exitIfChatNotConfigured(chat, kvList, str(e), i, count)
|
|
28252
28470
|
continue
|
|
@@ -31701,6 +31919,65 @@ def doPrintShowChromeNeedsAttn():
|
|
|
31701
31919
|
if csvPF:
|
|
31702
31920
|
csvPF.writeCSVfile('Chrome Devices Needing Attention')
|
|
31703
31921
|
|
|
31922
|
+
CHROME_DEVICE_COUNTS_MODE_CHOICES = ['active', 'perboottype', 'perreleasechannel']
|
|
31923
|
+
CHROME_DEVICE_COUNTS_MODE_FUNCTIONS = {
|
|
31924
|
+
'active': 'countActiveDevices',
|
|
31925
|
+
'perboottype': 'countDevicesPerBootType',
|
|
31926
|
+
'perreleasechannel': 'countDevicesPerReleaseChannel'
|
|
31927
|
+
}
|
|
31928
|
+
CHROME_DEVICE_COUNTS_MODE_CSV_TITLE = {
|
|
31929
|
+
'active': 'Chrome Active Devices',
|
|
31930
|
+
'perboottype': 'Chrome Devices per Boot Type',
|
|
31931
|
+
'perreleasechannel': 'Chrome Devices per Release Channel'
|
|
31932
|
+
}
|
|
31933
|
+
|
|
31934
|
+
# gam print chromedevicecounts [todrive <ToDriveAttribute>*]
|
|
31935
|
+
# [mode active|perboottype|perreleasechannel] [date <Date>]
|
|
31936
|
+
# [formatjson [quotechar <Character>]]
|
|
31937
|
+
# gam show chromedevicecounts
|
|
31938
|
+
# [mode active|perboottype|perreleasechannel] [date <Date>]
|
|
31939
|
+
# [formatjson]
|
|
31940
|
+
def doPrintShowChromeDeviceCounts():
|
|
31941
|
+
cm = buildGAPIObject(API.CHROMEMANAGEMENT)
|
|
31942
|
+
customerId = _getCustomersCustomerIdWithC()
|
|
31943
|
+
csvPF = CSVPrintFile() if Act.csvFormat() else None
|
|
31944
|
+
FJQC = FormatJSONQuoteChar(csvPF)
|
|
31945
|
+
pdate = todaysDate()
|
|
31946
|
+
mode = 'active'
|
|
31947
|
+
while Cmd.ArgumentsRemaining():
|
|
31948
|
+
myarg = getArgument()
|
|
31949
|
+
if csvPF and myarg == 'todrive':
|
|
31950
|
+
csvPF.GetTodriveParameters()
|
|
31951
|
+
elif myarg == 'mode':
|
|
31952
|
+
mode = getChoice(CHROME_DEVICE_COUNTS_MODE_CHOICES)
|
|
31953
|
+
elif myarg == 'date':
|
|
31954
|
+
pdate = getYYYYMMDD(returnDateTime=True)
|
|
31955
|
+
else:
|
|
31956
|
+
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
31957
|
+
kwargs = {'date_day': pdate.day, 'date_month': pdate.month, 'date_year': pdate.year}
|
|
31958
|
+
try:
|
|
31959
|
+
counts = callGAPI(cm.customers().reports(), CHROME_DEVICE_COUNTS_MODE_FUNCTIONS[mode],
|
|
31960
|
+
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
31961
|
+
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
31962
|
+
customer=customerId, **kwargs)
|
|
31963
|
+
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
|
|
31964
|
+
entityActionFailedWarning([Ent.CHROME_DEVICE_COUNT, None], str(e))
|
|
31965
|
+
return
|
|
31966
|
+
for k, v in counts.items():
|
|
31967
|
+
counts[k] = int(v)
|
|
31968
|
+
if not csvPF:
|
|
31969
|
+
if not FJQC.formatJSON:
|
|
31970
|
+
showJSON(CHROME_DEVICE_COUNTS_MODE_CSV_TITLE[mode], counts)
|
|
31971
|
+
else:
|
|
31972
|
+
printLine(json.dumps(cleanJSON(counts), ensure_ascii=False, sort_keys=True))
|
|
31973
|
+
else:
|
|
31974
|
+
row = flattenJSON(counts)
|
|
31975
|
+
if not FJQC.formatJSON:
|
|
31976
|
+
csvPF.WriteRowTitles(row)
|
|
31977
|
+
elif csvPF.CheckRowTitles(row):
|
|
31978
|
+
csvPF.WriteRowNoFilter({'JSON': json.dumps(cleanJSON(counts), ensure_ascii=False, sort_keys=True)})
|
|
31979
|
+
csvPF.writeCSVfile(CHROME_DEVICE_COUNTS_MODE_CSV_TITLE[mode])
|
|
31980
|
+
|
|
31704
31981
|
CHROME_VERSIONS_TITLES = ['channel', 'system', 'deviceOsVersion']
|
|
31705
31982
|
|
|
31706
31983
|
# gam print chromeversions [todrive <ToDriveAttribute>*]
|
|
@@ -32861,6 +33138,8 @@ def doCreateGroup(ciGroupsAPI=False):
|
|
|
32861
33138
|
initialGroupConfig = 'WITH_INITIAL_OWNER'
|
|
32862
33139
|
elif ciGroupsAPI and myarg in {'security', 'makesecuritygroup'}:
|
|
32863
33140
|
body['labels'][CIGROUP_SECURITY_LABEL] = ''
|
|
33141
|
+
elif ciGroupsAPI and myarg in ['locked']:
|
|
33142
|
+
body['labels'][CIGROUP_LOCKED_LABEL] = ''
|
|
32864
33143
|
elif myarg == 'verifynotinvitable':
|
|
32865
33144
|
verifyNotInvitable = True
|
|
32866
33145
|
else:
|
|
@@ -35752,7 +36031,7 @@ def doPrintShowGroupTree():
|
|
|
35752
36031
|
# gam create cigroup <EmailAddress>
|
|
35753
36032
|
# [copyfrom <GroupItem>] <GroupAttribute>
|
|
35754
36033
|
# [makeowner] [alias|aliases <CIGroupAliasList>]
|
|
35755
|
-
# [security|makesecuritygroup]
|
|
36034
|
+
# [security|makesecuritygroup] [locked]
|
|
35756
36035
|
# [dynamic <QueryDynamicGroup>]
|
|
35757
36036
|
def doCreateCIGroup():
|
|
35758
36037
|
doCreateGroup(ciGroupsAPI=True)
|
|
@@ -35936,7 +36215,6 @@ def doUpdateCIGroups():
|
|
|
35936
36215
|
|
|
35937
36216
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
35938
36217
|
ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS)
|
|
35939
|
-
cib = None
|
|
35940
36218
|
entityType = Ent.CLOUD_IDENTITY_GROUP
|
|
35941
36219
|
csvPF = None
|
|
35942
36220
|
getBeforeUpdate = preview = False
|
|
@@ -36045,7 +36323,6 @@ def doUpdateCIGroups():
|
|
|
36045
36323
|
_, name, _ = convertGroupEmailToCloudID(ci, group, i, count)
|
|
36046
36324
|
if not name:
|
|
36047
36325
|
continue
|
|
36048
|
-
cipl = ci
|
|
36049
36326
|
twoUpdates = False
|
|
36050
36327
|
if 'labels' in ci_body or lockGroup is not None:
|
|
36051
36328
|
try:
|
|
@@ -36074,20 +36351,16 @@ def doUpdateCIGroups():
|
|
|
36074
36351
|
else:
|
|
36075
36352
|
if CIGROUP_LOCKED_LABEL in ci_body['labels']:
|
|
36076
36353
|
ci_body['labels'].pop(CIGROUP_LOCKED_LABEL)
|
|
36077
|
-
if CIGROUP_LOCKED_LABEL in ci_body['labels']:
|
|
36078
|
-
if cib is None:
|
|
36079
|
-
cib = buildGAPIObject(API.CLOUDIDENTITY_GROUPS_BETA)
|
|
36080
|
-
cipl = cib
|
|
36081
36354
|
if ci_body:
|
|
36082
36355
|
try:
|
|
36083
36356
|
if twoUpdates:
|
|
36084
36357
|
ci_body['labels'].pop(CIGROUP_LOCKED_LABEL)
|
|
36085
|
-
callGAPI(
|
|
36358
|
+
callGAPI(ci.groups(), 'patch',
|
|
36086
36359
|
throwReasons=GAPI.CIGROUP_UPDATE_THROW_REASONS,
|
|
36087
36360
|
retryReasons=GAPI.CIGROUP_RETRY_REASONS,
|
|
36088
36361
|
name=name, body=ci_body, updateMask=','.join(list(ci_body.keys())))
|
|
36089
36362
|
ci_body['labels'][CIGROUP_LOCKED_LABEL] = ''
|
|
36090
|
-
callGAPI(
|
|
36363
|
+
callGAPI(ci.groups(), 'patch',
|
|
36091
36364
|
throwReasons=GAPI.CIGROUP_UPDATE_THROW_REASONS,
|
|
36092
36365
|
retryReasons=GAPI.CIGROUP_RETRY_REASONS,
|
|
36093
36366
|
name=name, body=ci_body, updateMask=','.join(list(ci_body.keys())))
|
|
@@ -37071,20 +37344,17 @@ def doPrintCIGroups():
|
|
|
37071
37344
|
else:
|
|
37072
37345
|
getFullFieldsList = list(CIGROUP_FULL_FIELDS)
|
|
37073
37346
|
getFullFields = ','.join(getFullFieldsList)#
|
|
37074
|
-
cipl = ci
|
|
37075
37347
|
if query:
|
|
37076
37348
|
method = 'search'
|
|
37077
37349
|
if 'parent' not in query:
|
|
37078
37350
|
query += f" && parent == '{parent}'"
|
|
37079
37351
|
kwargs = {'query': query}
|
|
37080
|
-
if CIGROUP_LOCKED_LABEL in query:
|
|
37081
|
-
cipl = buildGAPIObject(API.CLOUDIDENTITY_GROUPS_BETA)
|
|
37082
37352
|
else:
|
|
37083
37353
|
method = 'list'
|
|
37084
37354
|
kwargs = {'parent': parent}
|
|
37085
37355
|
printGettingAllAccountEntities(Ent.CLOUD_IDENTITY_GROUP, query)
|
|
37086
37356
|
try:
|
|
37087
|
-
entityList = callGAPIpages(
|
|
37357
|
+
entityList = callGAPIpages(ci.groups(), method, 'groups',
|
|
37088
37358
|
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute=['groupKey', 'id'],
|
|
37089
37359
|
throwReasons=GAPI.CIGROUP_LIST_THROW_REASONS, retryReasons=GAPI.CIGROUP_RETRY_REASONS,
|
|
37090
37360
|
view='FULL', fields=fieldsnp, pageSize=pageSize, **kwargs)
|
|
@@ -40062,15 +40332,15 @@ def _getEventDaysOfWeek(event):
|
|
|
40062
40332
|
if attr in event:
|
|
40063
40333
|
if 'date' in event[attr]:
|
|
40064
40334
|
try:
|
|
40065
|
-
dateTime =
|
|
40066
|
-
event[attr]['dayOfWeek'] =
|
|
40335
|
+
dateTime = arrow.Arrow.strptime(event[attr]['date'], YYYYMMDD_FORMAT)
|
|
40336
|
+
event[attr]['dayOfWeek'] = DAYS_OF_WEEK[dateTime.weekday()]
|
|
40067
40337
|
except ValueError:
|
|
40068
40338
|
pass
|
|
40069
40339
|
elif 'dateTime' in event[attr]:
|
|
40070
40340
|
try:
|
|
40071
|
-
dateTime
|
|
40072
|
-
event[attr]['dayOfWeek'] =
|
|
40073
|
-
except (
|
|
40341
|
+
dateTime = arrow.get(event[attr]['dateTime'])
|
|
40342
|
+
event[attr]['dayOfWeek'] = DAYS_OF_WEEK[dateTime.weekday()]
|
|
40343
|
+
except (arrow.parser.ParserError, OverflowError):
|
|
40074
40344
|
pass
|
|
40075
40345
|
|
|
40076
40346
|
def _createCalendarEvents(user, origCal, function, calIds, count, body, parameters):
|
|
@@ -40108,7 +40378,7 @@ def _createCalendarEvents(user, origCal, function, calIds, count, body, paramete
|
|
|
40108
40378
|
else:
|
|
40109
40379
|
if parameters['showDayOfWeek']:
|
|
40110
40380
|
_getEventDaysOfWeek(event)
|
|
40111
|
-
_printCalendarEvent(user, calId, event, parameters['csvPF'], parameters['FJQC'])
|
|
40381
|
+
_printCalendarEvent(user, calId, event, parameters['csvPF'], parameters['FJQC'], {})
|
|
40112
40382
|
except (GAPI.invalid, GAPI.required, GAPI.timeRangeEmpty, GAPI.eventDurationExceedsLimit,
|
|
40113
40383
|
GAPI.requiredAccessLevel, GAPI.participantIsNeitherOrganizerNorAttendee,
|
|
40114
40384
|
GAPI.malformedWorkingLocationEvent, GAPI.badRequest) as e:
|
|
@@ -40212,7 +40482,7 @@ def _updateCalendarEvents(origUser, user, origCal, calIds, count, calendarEventE
|
|
|
40212
40482
|
else:
|
|
40213
40483
|
if parameters['showDayOfWeek']:
|
|
40214
40484
|
_getEventDaysOfWeek(event)
|
|
40215
|
-
_printCalendarEvent(user, calId, event, parameters['csvPF'], parameters['FJQC'])
|
|
40485
|
+
_printCalendarEvent(user, calId, event, parameters['csvPF'], parameters['FJQC'], {})
|
|
40216
40486
|
except (GAPI.notFound, GAPI.deleted) as e:
|
|
40217
40487
|
if not checkCalendarExists(cal, calId, j, jcount):
|
|
40218
40488
|
entityUnknownWarning(Ent.CALENDAR, calId, j, jcount)
|
|
@@ -40509,10 +40779,12 @@ def _showCalendarEvent(primaryEmail, calId, eventEntityType, event, k, kcount, F
|
|
|
40509
40779
|
showJSON(None, event, skipObjects)
|
|
40510
40780
|
Ind.Decrement()
|
|
40511
40781
|
|
|
40512
|
-
def _printCalendarEvent(user, calId, event, csvPF, FJQC):
|
|
40782
|
+
def _printCalendarEvent(user, calId, event, csvPF, FJQC, addCSVData):
|
|
40513
40783
|
row = {'calendarId': calId, 'id': event['id']}
|
|
40514
40784
|
if user:
|
|
40515
40785
|
row['primaryEmail'] = user
|
|
40786
|
+
if addCSVData:
|
|
40787
|
+
row.update(addCSVData)
|
|
40516
40788
|
flattenJSON(event, flattened=row, timeObjects=EVENT_TIME_OBJECTS)
|
|
40517
40789
|
if not FJQC.formatJSON:
|
|
40518
40790
|
csvPF.WriteRowTitles(row)
|
|
@@ -40525,7 +40797,7 @@ def _printCalendarEvent(user, calId, event, csvPF, FJQC):
|
|
|
40525
40797
|
csvPF.WriteRowNoFilter(row)
|
|
40526
40798
|
|
|
40527
40799
|
def _printShowCalendarEvents(origUser, user, origCal, calIds, count, calendarEventEntity,
|
|
40528
|
-
csvPF, FJQC, fieldsList):
|
|
40800
|
+
csvPF, FJQC, fieldsList, addCSVData):
|
|
40529
40801
|
i = 0
|
|
40530
40802
|
for calId in calIds:
|
|
40531
40803
|
i += 1
|
|
@@ -40551,7 +40823,7 @@ def _printShowCalendarEvents(origUser, user, origCal, calIds, count, calendarEve
|
|
|
40551
40823
|
for event in events:
|
|
40552
40824
|
if calendarEventEntity['showDayOfWeek']:
|
|
40553
40825
|
_getEventDaysOfWeek(event)
|
|
40554
|
-
_printCalendarEvent(user, calId, event, csvPF, FJQC)
|
|
40826
|
+
_printCalendarEvent(user, calId, event, csvPF, FJQC, addCSVData)
|
|
40555
40827
|
elif GC.Values[GC.CSV_OUTPUT_USERS_AUDIT] and user:
|
|
40556
40828
|
csvPF.WriteRowNoFilter({'calendarId': calId, 'primaryEmail': user, 'id': ''})
|
|
40557
40829
|
else:
|
|
@@ -40569,6 +40841,8 @@ def _printShowCalendarEvents(origUser, user, origCal, calIds, count, calendarEve
|
|
|
40569
40841
|
row = {'calendarId': calId}
|
|
40570
40842
|
if user:
|
|
40571
40843
|
row['primaryEmail'] = user
|
|
40844
|
+
if addCSVData:
|
|
40845
|
+
row.update(addCSVData)
|
|
40572
40846
|
row['events'] = jcount
|
|
40573
40847
|
if not calendarEventEntity['eventRowFilter']:
|
|
40574
40848
|
csvPF.WriteRow(row)
|
|
@@ -40799,6 +41073,8 @@ def _getCalendarPrintShowEventOptions(calendarEventEntity, entityType):
|
|
|
40799
41073
|
csvPF = CSVPrintFile(['primaryEmail', 'calendarId', 'id'] if entityType == Ent.USER else ['calendarId', 'id'], 'sortall', indexedTitles=EVENT_INDEXED_TITLES) if Act.csvFormat() else None
|
|
40800
41074
|
FJQC = FormatJSONQuoteChar(csvPF)
|
|
40801
41075
|
fieldsList = []
|
|
41076
|
+
addCSVData = {}
|
|
41077
|
+
addCSVDataLoc = 2 if entityType == Ent.USER else 1
|
|
40802
41078
|
while Cmd.ArgumentsRemaining():
|
|
40803
41079
|
myarg = getArgument()
|
|
40804
41080
|
if csvPF and myarg == 'todrive':
|
|
@@ -40807,6 +41083,9 @@ def _getCalendarPrintShowEventOptions(calendarEventEntity, entityType):
|
|
|
40807
41083
|
pass
|
|
40808
41084
|
elif myarg == 'fields':
|
|
40809
41085
|
_getEventFields(fieldsList)
|
|
41086
|
+
elif csvPF and myarg == 'addcsvdata':
|
|
41087
|
+
k = getString(Cmd.OB_STRING)
|
|
41088
|
+
addCSVData[k] = getString(Cmd.OB_STRING, minLen=0)
|
|
40810
41089
|
elif myarg == 'countsonly':
|
|
40811
41090
|
calendarEventEntity['countsOnly'] = True
|
|
40812
41091
|
elif myarg == 'showdayofweek':
|
|
@@ -40819,27 +41098,35 @@ def _getCalendarPrintShowEventOptions(calendarEventEntity, entityType):
|
|
|
40819
41098
|
fieldsList = ['id']
|
|
40820
41099
|
if csvPF:
|
|
40821
41100
|
if calendarEventEntity['countsOnly']:
|
|
41101
|
+
csvPF.SetFormatJSON(False)
|
|
40822
41102
|
csvPF.RemoveTitles(['id'])
|
|
41103
|
+
if addCSVData:
|
|
41104
|
+
csvPF.InsertTitles(addCSVDataLoc, sorted(addCSVData.keys()))
|
|
40823
41105
|
csvPF.AddTitles(['events'])
|
|
41106
|
+
csvPF.SetSortAllTitles()
|
|
40824
41107
|
calendarEventEntity['countsOnlyTitles'] = csvPF.titlesList[:]
|
|
40825
|
-
|
|
40826
|
-
|
|
41108
|
+
else:
|
|
41109
|
+
if addCSVData:
|
|
41110
|
+
csvPF.InsertTitles(addCSVDataLoc, sorted(addCSVData.keys()))
|
|
41111
|
+
if not FJQC.formatJSON and not fieldsList:
|
|
41112
|
+
csvPF.AddTitles(EVENT_PRINT_ORDER)
|
|
41113
|
+
csvPF.SetSortAllTitles()
|
|
40827
41114
|
_addEventEntitySelectFields(calendarEventEntity, fieldsList)
|
|
40828
|
-
return (csvPF, FJQC, fieldsList)
|
|
41115
|
+
return (csvPF, FJQC, fieldsList, addCSVData)
|
|
40829
41116
|
|
|
40830
41117
|
# gam calendars <CalendarEntity> print events <EventEntity> <EventDisplayProperties>*
|
|
40831
41118
|
# [fields <EventFieldNameList>] [showdayofweek]
|
|
40832
|
-
#
|
|
40833
|
-
# [
|
|
41119
|
+
# (addcsvdata <FieldName> <String>)*
|
|
41120
|
+
# [eventrowfilter]
|
|
41121
|
+
# [countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
|
40834
41122
|
# gam calendars <CalendarEntity> show events <EventEntity> <EventDisplayProperties>*
|
|
40835
41123
|
# [fields <EventFieldNameList>] [showdayofweek]
|
|
40836
|
-
# [countsonly]
|
|
40837
|
-
# [formatjson]
|
|
41124
|
+
# [countsonly|formatjson]
|
|
40838
41125
|
def doCalendarsPrintShowEvents(calIds):
|
|
40839
41126
|
calendarEventEntity = getCalendarEventEntity()
|
|
40840
|
-
csvPF, FJQC, fieldsList = _getCalendarPrintShowEventOptions(calendarEventEntity, Ent.CALENDAR)
|
|
41127
|
+
csvPF, FJQC, fieldsList, addCSVData = _getCalendarPrintShowEventOptions(calendarEventEntity, Ent.CALENDAR)
|
|
40841
41128
|
_printShowCalendarEvents(None, None, None, calIds, len(calIds), calendarEventEntity,
|
|
40842
|
-
csvPF, FJQC, fieldsList)
|
|
41129
|
+
csvPF, FJQC, fieldsList, addCSVData)
|
|
40843
41130
|
if csvPF:
|
|
40844
41131
|
if calendarEventEntity['countsOnly'] and calendarEventEntity['eventRowFilter']:
|
|
40845
41132
|
csvPF.SetTitles(calendarEventEntity['countsOnlyTitles'])
|
|
@@ -41643,7 +41930,7 @@ def convertQueryNameToID(v, nameOrId, matterId, matterNameId):
|
|
|
41643
41930
|
query = callGAPI(v.matters().savedQueries(), 'get',
|
|
41644
41931
|
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
|
|
41645
41932
|
matterId=matterId, savedQueryId=cg.group(1))
|
|
41646
|
-
return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']))
|
|
41933
|
+
return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']), query['query'])
|
|
41647
41934
|
except (GAPI.notFound, GAPI.badRequest):
|
|
41648
41935
|
entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, nameOrId])
|
|
41649
41936
|
except (GAPI.forbidden, GAPI.invalidArgument) as e:
|
|
@@ -41652,12 +41939,12 @@ def convertQueryNameToID(v, nameOrId, matterId, matterNameId):
|
|
|
41652
41939
|
try:
|
|
41653
41940
|
queries = callGAPIpages(v.matters().savedQueries(), 'list', 'savedQueries',
|
|
41654
41941
|
throwReasons=[GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
|
|
41655
|
-
matterId=matterId, fields='savedQueries(savedQueryId,displayName),nextPageToken')
|
|
41942
|
+
matterId=matterId, fields='savedQueries(savedQueryId,displayName,query),nextPageToken')
|
|
41656
41943
|
except (GAPI.forbidden, GAPI.invalidArgument) as e:
|
|
41657
41944
|
ClientAPIAccessDeniedExit(str(e))
|
|
41658
41945
|
for query in queries:
|
|
41659
41946
|
if query['displayName'].lower() == nameOrIdlower:
|
|
41660
|
-
return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']))
|
|
41947
|
+
return (query['savedQueryId'], query['displayName'], formatVaultNameId(query['savedQueryId'], query['displayName']), query['query'])
|
|
41661
41948
|
entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, nameOrId])
|
|
41662
41949
|
|
|
41663
41950
|
def getMatterItem(v, state=None):
|
|
@@ -41705,6 +41992,7 @@ VAULT_SEARCH_METHODS_MAP = {
|
|
|
41705
41992
|
'accounts': 'ACCOUNT',
|
|
41706
41993
|
'chatspace': 'ROOM',
|
|
41707
41994
|
'chatspaces': 'ROOM',
|
|
41995
|
+
'documentids': 'DRIVE_DOCUMENT',
|
|
41708
41996
|
'entireorg': 'ENTIRE_ORG',
|
|
41709
41997
|
'everyone': 'ENTIRE_ORG',
|
|
41710
41998
|
'org': 'ORG_UNIT',
|
|
@@ -41802,11 +42090,13 @@ VAULT_QUERY_ARGS = [
|
|
|
41802
42090
|
# drive
|
|
41803
42091
|
'driveclientsideencryption', 'driveversiondate', 'includeshareddrives', 'includeteamdrives', 'shareddrivesoption',
|
|
41804
42092
|
# hangoutsChat
|
|
41805
|
-
'includerooms',
|
|
42093
|
+
'includerooms',
|
|
41806
42094
|
# mail
|
|
41807
42095
|
'mailclientsideencryption', 'excludedrafts',
|
|
41808
42096
|
# voice
|
|
41809
42097
|
'covereddata',
|
|
42098
|
+
# all
|
|
42099
|
+
'json',
|
|
41810
42100
|
] + list(VAULT_SEARCH_METHODS_MAP.keys())
|
|
41811
42101
|
|
|
41812
42102
|
def _buildVaultQuery(myarg, query, corpusArgumentMap):
|
|
@@ -41820,12 +42110,14 @@ def _buildVaultQuery(myarg, query, corpusArgumentMap):
|
|
|
41820
42110
|
query['dataScope'] = 'ALL_DATA'
|
|
41821
42111
|
if myarg == 'corpus':
|
|
41822
42112
|
query['corpus'] = getChoice(corpusArgumentMap, mapChoice=True)
|
|
42113
|
+
elif myarg == 'scope':
|
|
42114
|
+
query['dataScope'] = getChoice(VAULT_EXPORT_DATASCOPE_MAP, mapChoice=True)
|
|
41823
42115
|
elif myarg in VAULT_SEARCH_METHODS_MAP:
|
|
41824
|
-
if query.get('
|
|
42116
|
+
if query.get('method'):
|
|
41825
42117
|
Cmd.Backup()
|
|
41826
42118
|
usageErrorExit(Msg.MULTIPLE_SEARCH_METHODS_SPECIFIED.format(formatChoiceList(VAULT_SEARCH_METHODS_MAP)))
|
|
41827
42119
|
searchMethod = VAULT_SEARCH_METHODS_MAP[myarg]
|
|
41828
|
-
query['
|
|
42120
|
+
query['method'] = searchMethod
|
|
41829
42121
|
if searchMethod == 'ACCOUNT':
|
|
41830
42122
|
query['accountInfo'] = {'emails': getNormalizedEmailAddressEntity()}
|
|
41831
42123
|
elif searchMethod == 'ORG_UNIT':
|
|
@@ -41840,8 +42132,8 @@ def _buildVaultQuery(myarg, query, corpusArgumentMap):
|
|
|
41840
42132
|
query['hangoutsChatInfo'] = {'roomId': roomIds}
|
|
41841
42133
|
elif searchMethod == 'SITES_URL':
|
|
41842
42134
|
query['sitesUrlInfo'] = {'urls': _getQueryList(Cmd.OB_URL_LIST)}
|
|
41843
|
-
|
|
41844
|
-
|
|
42135
|
+
elif searchMethod == 'DRIVE_DOCUMENT':
|
|
42136
|
+
query['driveDocumentInfo'] = {'documentIds': {'ids': _getQueryList(Cmd.OB_DRIVE_FILE_ID_LIST)}}
|
|
41845
42137
|
elif myarg == 'terms':
|
|
41846
42138
|
query['terms'] = getString(Cmd.OB_STRING)
|
|
41847
42139
|
elif myarg in {'start', 'starttime'}:
|
|
@@ -41880,50 +42172,73 @@ def _buildVaultQuery(myarg, query, corpusArgumentMap):
|
|
|
41880
42172
|
query['hangoutsChatOptions'] = {'includeRooms': getBoolean()}
|
|
41881
42173
|
# mail
|
|
41882
42174
|
elif myarg == 'excludedrafts':
|
|
41883
|
-
query
|
|
42175
|
+
query.setdefault('mailOptions', {})['excludeDrafts'] = getBoolean()
|
|
41884
42176
|
elif myarg == 'mailclientsideencryption':
|
|
41885
42177
|
query.setdefault('mailOptions', {})['clientSideEncryptedOption'] = getChoice(VAULT_CSE_OPTION_MAP, mapChoice=True)
|
|
41886
42178
|
# voice
|
|
41887
42179
|
elif myarg == 'covereddata':
|
|
41888
|
-
query
|
|
42180
|
+
query.setdefault('voiceOptions', {'coveredData': []})
|
|
42181
|
+
query['voiceOptions']['coveredData'].append(getChoice(VAULT_VOICE_COVERED_DATA_MAP, mapChoice=True))
|
|
42182
|
+
# all
|
|
42183
|
+
elif myarg == 'json':
|
|
42184
|
+
jsonData = getJSON([])
|
|
42185
|
+
if 'query' in jsonData:
|
|
42186
|
+
query.update(jsonData['query'])
|
|
42187
|
+
else:
|
|
42188
|
+
query.update(jsonData)
|
|
42189
|
+
|
|
41889
42190
|
|
|
41890
42191
|
def _validateVaultQuery(body, corpusArgumentMap):
|
|
41891
42192
|
if 'corpus' not in body['query']:
|
|
41892
42193
|
missingArgumentExit(f'corpus {formatChoiceList(corpusArgumentMap)}')
|
|
41893
|
-
if '
|
|
42194
|
+
if 'method' not in body['query']:
|
|
41894
42195
|
missingArgumentExit(formatChoiceList(VAULT_SEARCH_METHODS_MAP))
|
|
41895
42196
|
if 'exportOptions' in body:
|
|
41896
42197
|
for corpus, options in VAULT_CORPUS_OPTIONS_MAP.items():
|
|
41897
42198
|
if body['query']['corpus'] != corpus:
|
|
41898
42199
|
body['exportOptions'].pop(options, None)
|
|
41899
42200
|
|
|
41900
|
-
# gam create vaultexport|export matter <MatterItem> [name <String>]
|
|
41901
|
-
#
|
|
42201
|
+
# gam create vaultexport|export matter <MatterItem> [name <String>]
|
|
42202
|
+
# vaultquery <QueryItem>
|
|
42203
|
+
# [driveclientsideencryption any|encrypted|unencrypted]
|
|
42204
|
+
# [includeaccessinfo <Boolean>]
|
|
42205
|
+
# [excludedrafts <Boolean>] [mailclientsideencryption any|encrypted|unencrypted]
|
|
42206
|
+
# [showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
|
|
42207
|
+
# [format ics|mbox|pst|xml]
|
|
42208
|
+
# [region any|europe|us] [showdetails|returnidonly]
|
|
42209
|
+
# gam create vaultexport|export matter <MatterItem> [name <String>]
|
|
42210
|
+
# corpus calendar|drive|gemini|groups|hangouts_chat|mail|voice
|
|
42211
|
+
# [scope all_data|held_data|unprocessed_data]
|
|
42212
|
+
# (accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone|entireorg
|
|
42213
|
+
# (documentids (<DriveFileIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
41902
42214
|
# (shareddrives|teamdrives (<SharedDriveIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
41903
|
-
#
|
|
41904
|
-
#
|
|
41905
|
-
# [
|
|
42215
|
+
# [(includeshareddrives <Boolean>)|(shareddrivesoption included|included_if_account_is_not_a_member|not_included)]
|
|
42216
|
+
# (sitesurl (<URLList>||(select <FileSelector>|<CSVFileSelector>)))
|
|
42217
|
+
# [driveversiondate <Date>|<Time>]
|
|
42218
|
+
# [includerooms <Boolean>]
|
|
42219
|
+
# (rooms (<ChatSpaceList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
41906
42220
|
# [terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
|
41907
42221
|
# [locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
|
|
41908
42222
|
# [responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
|
|
41909
|
-
#
|
|
41910
|
-
# [
|
|
42223
|
+
# (covereddata calllogs|textmessages|voicemails)*
|
|
42224
|
+
# [<JSONData>]
|
|
41911
42225
|
# [driveclientsideencryption any|encrypted|unencrypted]
|
|
41912
|
-
# [
|
|
42226
|
+
# [includeaccessinfo <Boolean>]
|
|
41913
42227
|
# [excludedrafts <Boolean>] [mailclientsideencryption any|encrypted|unencrypted]
|
|
41914
42228
|
# [showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
|
|
41915
|
-
# [covereddata calllogs|textmessages|voicemails]
|
|
41916
42229
|
# [format ics|mbox|pst|xml]
|
|
41917
42230
|
# [region any|europe|us] [showdetails|returnidonly]
|
|
41918
42231
|
def doCreateVaultExport():
|
|
41919
42232
|
v = buildGAPIObject(API.VAULT)
|
|
41920
42233
|
matterId = None
|
|
41921
42234
|
body = {'query': {'dataScope': 'ALL_DATA'}, 'exportOptions': {}}
|
|
42235
|
+
includeAccessInfo = None
|
|
41922
42236
|
exportFormat = None
|
|
42237
|
+
formatLocation = None
|
|
42238
|
+
useNewExport = None
|
|
41923
42239
|
showConfidentialModeContent = None
|
|
41924
42240
|
exportLinkedDriveFiles = None
|
|
41925
42241
|
returnIdOnly = showDetails = False
|
|
41926
|
-
useNewExport = None
|
|
41927
42242
|
while Cmd.ArgumentsRemaining():
|
|
41928
42243
|
myarg = getArgument()
|
|
41929
42244
|
if myarg == 'matter':
|
|
@@ -41931,22 +42246,23 @@ def doCreateVaultExport():
|
|
|
41931
42246
|
body['matterId'] = matterId
|
|
41932
42247
|
elif myarg == 'name':
|
|
41933
42248
|
body['name'] = getString(Cmd.OB_STRING)
|
|
42249
|
+
elif matterId is not None and myarg == 'vaultquery':
|
|
42250
|
+
_, _, _, body['query'] = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
41934
42251
|
elif myarg in VAULT_QUERY_ARGS:
|
|
41935
42252
|
_buildVaultQuery(myarg, body['query'], VAULT_CORPUS_ARGUMENT_MAP)
|
|
41936
|
-
elif myarg == '
|
|
41937
|
-
|
|
42253
|
+
elif myarg == 'includeaccessinfo':
|
|
42254
|
+
includeAccessInfo = getBoolean()
|
|
41938
42255
|
elif myarg == 'format':
|
|
42256
|
+
formatLocation = Cmd.Location()
|
|
41939
42257
|
exportFormat = getChoice(VAULT_EXPORT_FORMAT_MAP, mapChoice=True)
|
|
42258
|
+
elif myarg == 'usenewexport':
|
|
42259
|
+
useNewExport = getBoolean()
|
|
41940
42260
|
elif myarg == 'showconfidentialmodecontent':
|
|
41941
42261
|
showConfidentialModeContent = getBoolean()
|
|
41942
42262
|
elif myarg == 'exportlinkeddrivefiles':
|
|
41943
42263
|
exportLinkedDriveFiles = getBoolean()
|
|
41944
42264
|
elif myarg == 'region':
|
|
41945
42265
|
body['exportOptions']['region'] = getChoice(VAULT_EXPORT_REGION_MAP, mapChoice=True)
|
|
41946
|
-
elif myarg == 'includeaccessinfo':
|
|
41947
|
-
body['exportOptions'].setdefault('driveOptions', {})['includeAccessInfo'] = getBoolean()
|
|
41948
|
-
elif myarg == 'covereddata':
|
|
41949
|
-
body['exportOptions'].setdefault('voiceOptions', {})['coveredData'] = getChoice(VAULT_VOICE_COVERED_DATA_MAP, mapChoice=True)
|
|
41950
42266
|
elif myarg == 'showdetails':
|
|
41951
42267
|
showDetails = True
|
|
41952
42268
|
returnIdOnly = False
|
|
@@ -41958,15 +42274,19 @@ def doCreateVaultExport():
|
|
|
41958
42274
|
if not matterId:
|
|
41959
42275
|
missingArgumentExit('matter')
|
|
41960
42276
|
_validateVaultQuery(body, VAULT_CORPUS_ARGUMENT_MAP)
|
|
41961
|
-
if exportFormat is not None:
|
|
41962
|
-
if not exportFormat in VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']]:
|
|
41963
|
-
invalidChoiceExit(exportFormat, VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']], False)
|
|
41964
|
-
elif body['query']['corpus'] != 'DRIVE':
|
|
41965
|
-
exportFormat = VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']][0]
|
|
41966
42277
|
if 'name' not in body:
|
|
41967
42278
|
body['name'] = f'GAM {body["query"]["corpus"]} Export - {ISOformatTimeStamp(todaysTime())}'
|
|
41968
42279
|
optionsField = VAULT_CORPUS_OPTIONS_MAP[body['query']['corpus']]
|
|
41969
|
-
if body['query']['corpus']
|
|
42280
|
+
if body['query']['corpus'] == 'DRIVE':
|
|
42281
|
+
if includeAccessInfo is not None:
|
|
42282
|
+
body['exportOptions'][optionsField] = {'includeAccessInfo': includeAccessInfo}
|
|
42283
|
+
else:
|
|
42284
|
+
if exportFormat is not None:
|
|
42285
|
+
if not exportFormat in VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']]:
|
|
42286
|
+
Cmd.SetLocation(formatLocation)
|
|
42287
|
+
invalidChoiceExit(exportFormat, VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']], False)
|
|
42288
|
+
else:
|
|
42289
|
+
exportFormat = VAULT_CORPUS_EXPORT_FORMATS[body['query']['corpus']][0]
|
|
41970
42290
|
body['exportOptions'][optionsField] = {'exportFormat': exportFormat}
|
|
41971
42291
|
if body['query']['corpus'] == 'MAIL':
|
|
41972
42292
|
if showConfidentialModeContent is not None:
|
|
@@ -42448,6 +42768,34 @@ def _showVaultHold(matterNameId, hold, cd, FJQC, k=0, kcount=0):
|
|
|
42448
42768
|
showJSON(None, hold, timeObjects=VAULT_HOLD_TIME_OBJECTS)
|
|
42449
42769
|
Ind.Decrement()
|
|
42450
42770
|
|
|
42771
|
+
def _useVaultQueryForHold(v, matterId, matterNameId, body):
|
|
42772
|
+
_, _, _, query = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
42773
|
+
body['corpus'] = query['corpus']
|
|
42774
|
+
method = query.get('method')
|
|
42775
|
+
if method == 'ACCOUNT':
|
|
42776
|
+
body['accounts'] = []
|
|
42777
|
+
for email in query['accountInfo']['emails']:
|
|
42778
|
+
body['accounts'].append({'email': email})
|
|
42779
|
+
elif method == 'ORG_UNIT':
|
|
42780
|
+
body['orgUnit'] = {'orgUnitId': query['orgUnitInfo']['orgUnitId']}
|
|
42781
|
+
queryType = VAULT_CORPUS_QUERY_MAP[query['corpus']]
|
|
42782
|
+
if queryType is None:
|
|
42783
|
+
return
|
|
42784
|
+
body['query'] = {queryType: {}}
|
|
42785
|
+
if query['corpus'] == 'DRIVE':
|
|
42786
|
+
body['query'][queryType]['includeSharedDriveFiles'] = query['driveOptions'].get('includeSharedDrives', False)
|
|
42787
|
+
elif query['corpus'] in {'GROUPS', 'MAIL'}:
|
|
42788
|
+
if query.get('terms'):
|
|
42789
|
+
body['query'][queryType]['terms'] = query['terms']
|
|
42790
|
+
if query.get('startTime'):
|
|
42791
|
+
body['query'][queryType]['startTime'] = query['startTime']
|
|
42792
|
+
if query.get('endTime'):
|
|
42793
|
+
body['query'][queryType]['endTime'] = query['endTime']
|
|
42794
|
+
elif query['corpus'] == 'HANGOUTS_CHAT':
|
|
42795
|
+
body['query'][queryType]['includeRooms'] = query['hangoutsChatOptions'].get('includeRooms', False)
|
|
42796
|
+
elif query['corpus'] == 'VOICE':
|
|
42797
|
+
body['query'][queryType]['coveredData'] = query['voiceOptions']['coveredData']
|
|
42798
|
+
|
|
42451
42799
|
def _getHoldQueryParameters(myarg, queryParameters):
|
|
42452
42800
|
if myarg == 'query':
|
|
42453
42801
|
queryParameters['queryLocation'] = Cmd.Location()
|
|
@@ -42463,7 +42811,8 @@ def _getHoldQueryParameters(myarg, queryParameters):
|
|
|
42463
42811
|
elif myarg in {'includeshareddrives', 'includeteamdrives'}:
|
|
42464
42812
|
queryParameters['includeSharedDriveFiles'] = getBoolean()
|
|
42465
42813
|
elif myarg == 'covereddata':
|
|
42466
|
-
queryParameters
|
|
42814
|
+
queryParameters.setdefault('coveredData', [])
|
|
42815
|
+
queryParameters['coveredData'].append(getChoice(VAULT_VOICE_COVERED_DATA_MAP, mapChoice=True))
|
|
42467
42816
|
else:
|
|
42468
42817
|
return False
|
|
42469
42818
|
return True
|
|
@@ -42498,7 +42847,11 @@ def _setHoldQuery(body, queryParameters):
|
|
|
42498
42847
|
if queryParameters.get('coveredData'):
|
|
42499
42848
|
body['query'][queryType]['coveredData'] = queryParameters['coveredData']
|
|
42500
42849
|
|
|
42501
|
-
# gam create vaulthold|hold matter <MatterItem> [name <String>]
|
|
42850
|
+
# gam create vaulthold|hold matter <MatterItem> [name <String>]
|
|
42851
|
+
# vaultquery <QueryItem>
|
|
42852
|
+
# [showdetails|returnidonly]
|
|
42853
|
+
# gam create vaulthold|hold matter <MatterItem> [name <String>]
|
|
42854
|
+
# corpus calendar|drive|mail|groups|hangouts_chat|voice
|
|
42502
42855
|
# [(accounts|groups|users <EmailItemList>) | (orgunit|org|ou <OrgUnit>)]
|
|
42503
42856
|
# [query <QueryVaultCorpus>]
|
|
42504
42857
|
# [terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
@@ -42512,13 +42865,16 @@ def doCreateVaultHold():
|
|
|
42512
42865
|
matterId = None
|
|
42513
42866
|
accounts = []
|
|
42514
42867
|
queryParameters = {}
|
|
42515
|
-
returnIdOnly = showDetails = False
|
|
42868
|
+
returnIdOnly = showDetails = usedVaultQuery = False
|
|
42516
42869
|
while Cmd.ArgumentsRemaining():
|
|
42517
42870
|
myarg = getArgument()
|
|
42518
42871
|
if myarg == 'matter':
|
|
42519
42872
|
matterId, matterNameId = getMatterItem(v)
|
|
42520
42873
|
elif myarg == 'name':
|
|
42521
42874
|
body['name'] = getString(Cmd.OB_STRING)
|
|
42875
|
+
elif matterId is not None and myarg == 'vaultquery':
|
|
42876
|
+
_useVaultQueryForHold(v, matterId, matterNameId, body)
|
|
42877
|
+
usedVaultQuery = True
|
|
42522
42878
|
elif myarg == 'corpus':
|
|
42523
42879
|
body['corpus'] = getChoice(VAULT_CORPUS_ARGUMENT_MAP, mapChoice=True)
|
|
42524
42880
|
elif myarg in {'accounts', 'users', 'groups'}:
|
|
@@ -42542,7 +42898,8 @@ def doCreateVaultHold():
|
|
|
42542
42898
|
missingArgumentExit(f'corpus {"|".join(VAULT_CORPUS_ARGUMENT_MAP)}')
|
|
42543
42899
|
if 'name' not in body:
|
|
42544
42900
|
body['name'] = f'GAM {body["corpus"]} Hold - {ISOformatTimeStamp(todaysTime())}'
|
|
42545
|
-
|
|
42901
|
+
if not usedVaultQuery:
|
|
42902
|
+
_setHoldQuery(body, queryParameters)
|
|
42546
42903
|
if accounts:
|
|
42547
42904
|
body['accounts'] = []
|
|
42548
42905
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
@@ -42975,6 +43332,116 @@ def _showVaultQuery(matterNameId, query, cd, drive, FJQC, k=0, kcount=0):
|
|
|
42975
43332
|
showJSON(None, query, timeObjects=VAULT_QUERY_TIME_OBJECTS)
|
|
42976
43333
|
Ind.Decrement()
|
|
42977
43334
|
|
|
43335
|
+
def doCreateCopyVaultQuery(copyCmd):
|
|
43336
|
+
v = buildGAPIObject(API.VAULT)
|
|
43337
|
+
body = {'query': {'dataScope': 'ALL_DATA'}, 'displayName': ''}
|
|
43338
|
+
matterId, matterNameId = getMatterItem(v)
|
|
43339
|
+
if copyCmd:
|
|
43340
|
+
_, queryName, _, body['query'] = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
43341
|
+
targetId = None
|
|
43342
|
+
cd = drive = None
|
|
43343
|
+
FJQC = FormatJSONQuoteChar()
|
|
43344
|
+
returnIdOnly = showDetails = False
|
|
43345
|
+
while Cmd.ArgumentsRemaining():
|
|
43346
|
+
myarg = getArgument()
|
|
43347
|
+
if myarg == 'name':
|
|
43348
|
+
body['displayName'] = getString(Cmd.OB_STRING)
|
|
43349
|
+
elif copyCmd and myarg == 'targetmatter':
|
|
43350
|
+
targetId, targetNameId = getMatterItem(v)
|
|
43351
|
+
elif not copyCmd and myarg in VAULT_QUERY_ARGS:
|
|
43352
|
+
_buildVaultQuery(myarg, body['query'], VAULT_CORPUS_ARGUMENT_MAP)
|
|
43353
|
+
elif myarg == 'shownames':
|
|
43354
|
+
cd = buildGAPIObject(API.DIRECTORY)
|
|
43355
|
+
_, drive = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
|
|
43356
|
+
if drive is None:
|
|
43357
|
+
return
|
|
43358
|
+
elif myarg == 'showdetails':
|
|
43359
|
+
showDetails = True
|
|
43360
|
+
returnIdOnly = False
|
|
43361
|
+
elif myarg == 'returnidonly':
|
|
43362
|
+
returnIdOnly = True
|
|
43363
|
+
showDetails = False
|
|
43364
|
+
else:
|
|
43365
|
+
FJQC.GetFormatJSON(myarg)
|
|
43366
|
+
if copyCmd:
|
|
43367
|
+
if targetId is None:
|
|
43368
|
+
targetId = matterId
|
|
43369
|
+
targetNameId = matterNameId
|
|
43370
|
+
if not body['displayName']:
|
|
43371
|
+
body['displayName'] = f'Copy of {queryName}' if matterId == targetId else queryName
|
|
43372
|
+
resultId = targetId
|
|
43373
|
+
resultNameId = targetNameId
|
|
43374
|
+
else:
|
|
43375
|
+
_validateVaultQuery(body, VAULT_CORPUS_ARGUMENT_MAP)
|
|
43376
|
+
if not body['displayName']:
|
|
43377
|
+
body['displayName'] = 'GAM {body["query"]["corpus"]} Query - {ISOformatTimeStamp(todaysTime())}'
|
|
43378
|
+
resultId = matterId
|
|
43379
|
+
resultNameId = matterNameId
|
|
43380
|
+
try:
|
|
43381
|
+
result = callGAPI(v.matters().savedQueries(), 'create',
|
|
43382
|
+
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT, GAPI.ALREADY_EXISTS],
|
|
43383
|
+
matterId=resultId, body=body)
|
|
43384
|
+
if not returnIdOnly:
|
|
43385
|
+
if not FJQC.formatJSON:
|
|
43386
|
+
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, formatVaultNameId(result['displayName'], result['savedQueryId'])])
|
|
43387
|
+
if showDetails or FJQC.formatJSON:
|
|
43388
|
+
_showVaultQuery(resultNameId, result, cd, drive, FJQC)
|
|
43389
|
+
else:
|
|
43390
|
+
writeStdout(f'{result["savedQueryId"]}\n')
|
|
43391
|
+
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.alreadyExists) as e:
|
|
43392
|
+
entityActionFailedWarning([Ent.VAULT_MATTER, resultNameId, Ent.VAULT_QUERY, body['displayName']], str(e))
|
|
43393
|
+
|
|
43394
|
+
# gam create vaultquery <MatterItem> [name <String>]
|
|
43395
|
+
# corpus calendar|drive|gemini|groups|hangouts_chat|mail|voice
|
|
43396
|
+
# [scope all_data|held_data|unprocessed_data]
|
|
43397
|
+
# (accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone|entireorg
|
|
43398
|
+
# (documentids (<DriveFileIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
43399
|
+
# (shareddrives|teamdrives (<SharedDriveIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
43400
|
+
# [(includeshareddrives <Boolean>)|(shareddrivesoption included|included_if_account_is_not_a_member|not_included)]
|
|
43401
|
+
# (sitesurl (<URLList>||(select <FileSelector>|<CSVFileSelector>)))
|
|
43402
|
+
# [driveversiondate <Date>|<Time>]
|
|
43403
|
+
# [includerooms <Boolean>]
|
|
43404
|
+
# (rooms (<ChatSpaceList>|(select <FileSelector>|<CSVFileSelector>))) |
|
|
43405
|
+
# [terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
|
43406
|
+
# [locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
|
|
43407
|
+
# [responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
|
|
43408
|
+
# (covereddata calllogs|textmessages|voicemails)*
|
|
43409
|
+
# [<JSONData>]
|
|
43410
|
+
# [shownames]
|
|
43411
|
+
# [showdetails|returnidonly|formatjson]
|
|
43412
|
+
def doCreateVaultQuery():
|
|
43413
|
+
doCreateCopyVaultQuery(False)
|
|
43414
|
+
|
|
43415
|
+
# gam copy vaultquery <MatterItem> <QueryItem> [targetmatter <MatterItem>] [name <String>]
|
|
43416
|
+
# [shownames]
|
|
43417
|
+
# [showdetails|returnidonly|formatjson]
|
|
43418
|
+
def doCopyVaultQuery():
|
|
43419
|
+
doCreateCopyVaultQuery(True)
|
|
43420
|
+
|
|
43421
|
+
# gam delete vaultquery <QueryItem> matter <MatterItem>
|
|
43422
|
+
# gam delete vaultquery <MatterItem> <QueryItem>
|
|
43423
|
+
def doDeleteVaultQuery():
|
|
43424
|
+
v = buildGAPIObject(API.VAULT)
|
|
43425
|
+
if not Cmd.ArgumentIsAhead('matter'):
|
|
43426
|
+
matterId, matterNameId = getMatterItem(v)
|
|
43427
|
+
queryId, queryName, queryNameId, _ = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
43428
|
+
else:
|
|
43429
|
+
queryName = getString(Cmd.OB_QUERY_ITEM)
|
|
43430
|
+
while Cmd.ArgumentsRemaining():
|
|
43431
|
+
myarg = getArgument()
|
|
43432
|
+
if myarg == 'matter':
|
|
43433
|
+
matterId, matterNameId = getMatterItem(v)
|
|
43434
|
+
queryId, queryName, queryNameId, _ = convertQueryNameToID(v, queryName, matterId, matterNameId)
|
|
43435
|
+
else:
|
|
43436
|
+
unknownArgumentExit()
|
|
43437
|
+
try:
|
|
43438
|
+
callGAPI(v.matters().savedQueries(), 'delete',
|
|
43439
|
+
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
|
|
43440
|
+
matterId=matterId, savedQueryId=queryId)
|
|
43441
|
+
entityActionPerformed([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId])
|
|
43442
|
+
except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e:
|
|
43443
|
+
entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_QUERY, queryNameId], str(e))
|
|
43444
|
+
|
|
42978
43445
|
VAULT_QUERY_FIELDS_CHOICE_MAP = {
|
|
42979
43446
|
'createtime': 'createTime',
|
|
42980
43447
|
'displayname': 'displayName',
|
|
@@ -42995,7 +43462,7 @@ def doInfoVaultQuery():
|
|
|
42995
43462
|
v = buildGAPIObject(API.VAULT)
|
|
42996
43463
|
if not Cmd.ArgumentIsAhead('matter'):
|
|
42997
43464
|
matterId, matterNameId = getMatterItem(v)
|
|
42998
|
-
queryId, queryName, queryNameId = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
43465
|
+
queryId, queryName, queryNameId, _ = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
42999
43466
|
else:
|
|
43000
43467
|
queryName = getString(Cmd.OB_QUERY_ITEM)
|
|
43001
43468
|
cd = drive = None
|
|
@@ -43005,7 +43472,7 @@ def doInfoVaultQuery():
|
|
|
43005
43472
|
myarg = getArgument()
|
|
43006
43473
|
if myarg == 'matter':
|
|
43007
43474
|
matterId, matterNameId = getMatterItem(v)
|
|
43008
|
-
queryId, queryName, queryNameId = convertQueryNameToID(v, queryName, matterId, matterNameId)
|
|
43475
|
+
queryId, queryName, queryNameId, _ = convertQueryNameToID(v, queryName, matterId, matterNameId)
|
|
43009
43476
|
elif myarg == 'shownames':
|
|
43010
43477
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
43011
43478
|
_, drive = buildGAPIServiceObject(API.DRIVE3, _getAdminEmail())
|
|
@@ -43460,14 +43927,16 @@ def doPrintShowVaultMatters():
|
|
|
43460
43927
|
PRINT_VAULT_COUNTS_TITLES = ['account', 'count', 'error']
|
|
43461
43928
|
|
|
43462
43929
|
# gam print vaultcounts [todrive <ToDriveAttributes>*]
|
|
43463
|
-
# matter <MatterItem>
|
|
43464
|
-
#
|
|
43465
|
-
#
|
|
43466
|
-
#
|
|
43467
|
-
#
|
|
43468
|
-
# [scope
|
|
43930
|
+
# matter <MatterItem> <QueryItem>
|
|
43931
|
+
# [wait <Integer>]
|
|
43932
|
+
# gam print vaultcounts [todrive <ToDriveAttributes>*]
|
|
43933
|
+
# matter <MatterItem>
|
|
43934
|
+
# corpus mail|groups
|
|
43935
|
+
# [scope all_data|held_data|unprocessed_data]
|
|
43936
|
+
# (accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone|entireorg
|
|
43469
43937
|
# [terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
|
43470
43938
|
# [excludedrafts <Boolean>]
|
|
43939
|
+
# [<JSONData>]
|
|
43471
43940
|
# [wait <Integer>]
|
|
43472
43941
|
# gam print vaultcounts [todrive <ToDriveAttributes>*]
|
|
43473
43942
|
# matter <MatterItem> operation <String> [wait <Integer>]
|
|
@@ -43476,18 +43945,19 @@ def doPrintVaultCounts():
|
|
|
43476
43945
|
csvPF = CSVPrintFile(PRINT_VAULT_COUNTS_TITLES, 'sortall')
|
|
43477
43946
|
matterId = name = None
|
|
43478
43947
|
operationWait = 15
|
|
43479
|
-
body = {'view': 'ALL'}
|
|
43480
|
-
query = {}
|
|
43948
|
+
body = {'view': 'ALL', 'query': {}}
|
|
43481
43949
|
while Cmd.ArgumentsRemaining():
|
|
43482
43950
|
myarg = getArgument()
|
|
43483
43951
|
if csvPF and myarg == 'todrive':
|
|
43484
43952
|
csvPF.GetTodriveParameters()
|
|
43485
43953
|
elif myarg == 'matter':
|
|
43486
|
-
matterId,
|
|
43954
|
+
matterId, matterNameId = getMatterItem(v)
|
|
43955
|
+
elif matterId is not None and myarg == 'vaultquery':
|
|
43956
|
+
_, _, _, body['query'] = convertQueryNameToID(v, getString(Cmd.OB_QUERY_ITEM), matterId, matterNameId)
|
|
43487
43957
|
elif myarg == 'operation':
|
|
43488
43958
|
name = getString(Cmd.OB_STRING)
|
|
43489
43959
|
elif myarg in VAULT_QUERY_ARGS:
|
|
43490
|
-
_buildVaultQuery(myarg, query, VAULT_COUNTS_CORPUS_ARGUMENT_MAP)
|
|
43960
|
+
_buildVaultQuery(myarg, body['query'], VAULT_COUNTS_CORPUS_ARGUMENT_MAP)
|
|
43491
43961
|
elif myarg == 'wait':
|
|
43492
43962
|
operationWait = getInteger(minVal=1)
|
|
43493
43963
|
else:
|
|
@@ -43498,7 +43968,6 @@ def doPrintVaultCounts():
|
|
|
43498
43968
|
operation = {'name': name}
|
|
43499
43969
|
doWait = False
|
|
43500
43970
|
else:
|
|
43501
|
-
body['query'] = query
|
|
43502
43971
|
_validateVaultQuery(body, VAULT_COUNTS_CORPUS_ARGUMENT_MAP)
|
|
43503
43972
|
try:
|
|
43504
43973
|
operation = callGAPI(v.matters(), 'count',
|
|
@@ -43522,7 +43991,7 @@ def doPrintVaultCounts():
|
|
|
43522
43991
|
doWait = True
|
|
43523
43992
|
response = operation.get('response', {})
|
|
43524
43993
|
query = operation['metadata']['query']
|
|
43525
|
-
search_method = query.get('
|
|
43994
|
+
search_method = query.get('method')
|
|
43526
43995
|
# ARGH count results don't include accounts with zero items.
|
|
43527
43996
|
# so we keep track of which accounts we searched and can report
|
|
43528
43997
|
# zero data for them.
|
|
@@ -43868,6 +44337,28 @@ USER_JSON_SKIP_FIELDS = ['agreedToTerms', 'aliases', 'creationTime', 'customerId
|
|
|
43868
44337
|
|
|
43869
44338
|
ALLOW_EMPTY_CUSTOM_TYPE = 'allowEmptyCustomType'
|
|
43870
44339
|
|
|
44340
|
+
def getNotifyArguments(myarg, notify, userNotification):
|
|
44341
|
+
if myarg == 'notify':
|
|
44342
|
+
if userNotification:
|
|
44343
|
+
notify['recipients'].extend(getNormalizedEmailAddressEntity(shlexSplit=True, noLower=True))
|
|
44344
|
+
else: #delegateNotificatiomn
|
|
44345
|
+
notify['notify'] = getBoolean()
|
|
44346
|
+
elif myarg == 'subject':
|
|
44347
|
+
notify['subject'] = getString(Cmd.OB_STRING)
|
|
44348
|
+
elif myarg in SORF_MSG_FILE_ARGUMENTS:
|
|
44349
|
+
notify['message'], notify['charset'], notify['html'] = getStringOrFile(myarg)
|
|
44350
|
+
elif myarg == 'html':
|
|
44351
|
+
notify['html'] = getBoolean()
|
|
44352
|
+
elif myarg == 'from':
|
|
44353
|
+
notify['from'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
44354
|
+
elif myarg == 'mailbox':
|
|
44355
|
+
notify['mailbox'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
44356
|
+
elif myarg == 'replyto':
|
|
44357
|
+
notify['replyto'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
44358
|
+
else:
|
|
44359
|
+
return False
|
|
44360
|
+
return True
|
|
44361
|
+
|
|
43871
44362
|
def getUserAttributes(cd, updateCmd, noUid=False):
|
|
43872
44363
|
def getKeywordAttribute(keywords, attrdict, **opts):
|
|
43873
44364
|
if Cmd.ArgumentsRemaining():
|
|
@@ -43955,6 +44446,7 @@ def getUserAttributes(cd, updateCmd, noUid=False):
|
|
|
43955
44446
|
invalidArgumentExit(Cmd.OB_SCHEMA_NAME_FIELD_NAME)
|
|
43956
44447
|
|
|
43957
44448
|
parameters = {
|
|
44449
|
+
'notifyRecoveryEmail': False,
|
|
43958
44450
|
'verifyNotInvitable': False,
|
|
43959
44451
|
'createIfNotFound': False,
|
|
43960
44452
|
'noActionIfAlias': False,
|
|
@@ -43971,7 +44463,7 @@ def getUserAttributes(cd, updateCmd, noUid=False):
|
|
|
43971
44463
|
body = {'name': {'givenName': UNKNOWN, 'familyName': UNKNOWN}}
|
|
43972
44464
|
body['primaryEmail'] = getEmailAddress(noUid=noUid)
|
|
43973
44465
|
notFoundBody = {}
|
|
43974
|
-
notify = {'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''}
|
|
44466
|
+
notify = {'recipients': [], 'subject': '', 'message': '', 'html': False, 'charset': UTF8, 'password': ''}
|
|
43975
44467
|
primary = {}
|
|
43976
44468
|
updatePrimaryEmail = {}
|
|
43977
44469
|
groupOrgUnitMap = None
|
|
@@ -43982,20 +44474,10 @@ def getUserAttributes(cd, updateCmd, noUid=False):
|
|
|
43982
44474
|
resolveConflictAccount = True
|
|
43983
44475
|
while Cmd.ArgumentsRemaining():
|
|
43984
44476
|
myarg = getArgument()
|
|
43985
|
-
if myarg
|
|
43986
|
-
|
|
43987
|
-
elif myarg == '
|
|
43988
|
-
|
|
43989
|
-
elif myarg in SORF_MSG_FILE_ARGUMENTS:
|
|
43990
|
-
notify['message'], notify['charset'], notify['html'] = getStringOrFile(myarg)
|
|
43991
|
-
elif myarg == 'html':
|
|
43992
|
-
notify['html'] = getBoolean()
|
|
43993
|
-
elif myarg == 'from':
|
|
43994
|
-
notify['from'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
43995
|
-
elif myarg == 'replyto':
|
|
43996
|
-
notify['replyto'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
43997
|
-
elif myarg == 'mailbox':
|
|
43998
|
-
notify['mailbox'] = getString(Cmd.OB_EMAIL_ADDRESS)
|
|
44477
|
+
if getNotifyArguments(myarg, notify, True):
|
|
44478
|
+
pass
|
|
44479
|
+
elif myarg == 'notifyrecoveryemail':
|
|
44480
|
+
parameters['notifyRecoveryEmail'] = True
|
|
43999
44481
|
elif PwdOpts.ProcessArgument(myarg, notify, notFoundBody):
|
|
44000
44482
|
pass
|
|
44001
44483
|
elif _getTagReplacement(myarg, tagReplacements, True):
|
|
@@ -44399,14 +44881,14 @@ def createUserAddAliases(cd, user, aliasList, i, count):
|
|
|
44399
44881
|
# (groups [<GroupRole>] [[delivery] <DeliverySetting>] <GroupEntity>)*
|
|
44400
44882
|
# [alias|aliases <EmailAddressList>]
|
|
44401
44883
|
# [license <SKUID> [product|productid <ProductID>]]
|
|
44402
|
-
# [notify <EmailAddressList>
|
|
44884
|
+
# [[notify <EmailAddressList>] [notifyrecoveryemail]
|
|
44403
44885
|
# [subject <String>]
|
|
44404
|
-
# [
|
|
44405
|
-
# [from <EmailAaddress>]
|
|
44886
|
+
# [from <EmailAaddress>] [mailbox <EmailAddress>]
|
|
44406
44887
|
# [replyto <EmailAaddress>]
|
|
44407
|
-
# [<
|
|
44408
|
-
#
|
|
44409
|
-
#
|
|
44888
|
+
# [notifypassword <String>]
|
|
44889
|
+
# [<NotifyMessageContent>] [html [<Boolean>]]
|
|
44890
|
+
# (replace <Tag> <UserReplacement>)*
|
|
44891
|
+
# (replaceregex <REMatchPattern> <RESubstitution> <Tag> <UserReplacement>)*]
|
|
44410
44892
|
# [logpassword <FileName>] [ignorenullpassword]
|
|
44411
44893
|
# [addnumericsuffixonduplicate <Number>]
|
|
44412
44894
|
def doCreateUser():
|
|
@@ -44417,7 +44899,7 @@ def doCreateUser():
|
|
|
44417
44899
|
suffix = 0
|
|
44418
44900
|
originalEmail = body['primaryEmail']
|
|
44419
44901
|
atLoc = originalEmail.find('@')
|
|
44420
|
-
fields = '*' if tagReplacements['subs'] else 'primaryEmail,name'
|
|
44902
|
+
fields = '*' if tagReplacements['subs'] else 'primaryEmail,name,recoveryEmail'
|
|
44421
44903
|
while True:
|
|
44422
44904
|
user = body['primaryEmail']
|
|
44423
44905
|
if parameters['verifyNotInvitable']:
|
|
@@ -44458,7 +44940,9 @@ def doCreateUser():
|
|
|
44458
44940
|
createUserAddToGroups(cd, result['primaryEmail'], addGroups, 0, 0)
|
|
44459
44941
|
if addAliases:
|
|
44460
44942
|
createUserAddAliases(cd, result['primaryEmail'], addAliases, 0, 0)
|
|
44461
|
-
if notify.get('recipients'):
|
|
44943
|
+
if (notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'))):
|
|
44944
|
+
if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
|
|
44945
|
+
notify['recipients'].append(result['recoveryEmail'])
|
|
44462
44946
|
sendCreateUpdateUserNotification(result, notify, tagReplacements)
|
|
44463
44947
|
for productSku in parameters[LICENSE_PRODUCT_SKUIDS]:
|
|
44464
44948
|
productId = productSku[0]
|
|
@@ -44501,14 +44985,14 @@ def verifyUserPrimaryEmail(cd, user, createIfNotFound, i, count):
|
|
|
44501
44985
|
# [createifnotfound] [notfoundpassword (random [<Integer>])|blocklogin|<Password>]
|
|
44502
44986
|
# (groups [<GroupRole>] [[delivery] <DeliverySetting>] <GroupEntity>)*
|
|
44503
44987
|
# [alias|aliases <EmailAddressList>]
|
|
44504
|
-
# [notify <EmailAddressList>
|
|
44988
|
+
# [[notify <EmailAddressList>] [notifyrecoveryemail]
|
|
44505
44989
|
# [subject <String>]
|
|
44506
|
-
# [
|
|
44507
|
-
# [from <EmailAaddress>]
|
|
44990
|
+
# [from <EmailAaddress>] [mailbox <EmailAddress>]
|
|
44508
44991
|
# [replyto <EmailAaddress>]
|
|
44509
|
-
# [<NotifyMessageContent>
|
|
44992
|
+
# [<NotifyMessageContent> [html [<Boolean>]]
|
|
44510
44993
|
# (replace <Tag> <UserReplacement>)*
|
|
44511
44994
|
# (replaceregex <REMatchPattern> <RESubstitution> <Tag> <UserReplacement>)*]
|
|
44995
|
+
# [notifypassword <String>]]
|
|
44512
44996
|
# [notifyonupdate [<Boolean>]]
|
|
44513
44997
|
# [logpassword <FileName>] [ignorenullpassword]
|
|
44514
44998
|
def updateUsers(entityList):
|
|
@@ -44530,7 +45014,7 @@ def updateUsers(entityList):
|
|
|
44530
45014
|
else:
|
|
44531
45015
|
checkImmutableOUs = False
|
|
44532
45016
|
i, count, entityList = getEntityArgument(entityList)
|
|
44533
|
-
fields = '*' if tagReplacements['subs'] else 'primaryEmail,name'
|
|
45017
|
+
fields = '*' if tagReplacements['subs'] else 'primaryEmail,name,recoveryEmail'
|
|
44534
45018
|
for user in entityList:
|
|
44535
45019
|
i += 1
|
|
44536
45020
|
user = userKey = normalizeEmailAddressOrUID(user)
|
|
@@ -44606,7 +45090,10 @@ def updateUsers(entityList):
|
|
|
44606
45090
|
entityActionPerformed([Ent.USER, user], i, count)
|
|
44607
45091
|
if PwdOpts.filename and PwdOpts.password:
|
|
44608
45092
|
writeFile(PwdOpts.filename, f'{userKey},{PwdOpts.password}\n', mode='a', continueOnError=True)
|
|
44609
|
-
if parameters['notifyOnUpdate'] and notify
|
|
45093
|
+
if (parameters['notifyOnUpdate'] and notify['password'] and
|
|
45094
|
+
(notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail')))):
|
|
45095
|
+
if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
|
|
45096
|
+
notify['recipients'].append(result['recoveryEmail'])
|
|
44610
45097
|
sendCreateUpdateUserNotification(result, notify, tagReplacements, i, count, createMessage=False)
|
|
44611
45098
|
break
|
|
44612
45099
|
except GAPI.conditionNotMet as e:
|
|
@@ -44640,8 +45127,10 @@ def updateUsers(entityList):
|
|
|
44640
45127
|
createUserAddToGroups(cd, result['primaryEmail'], addGroups, i, count)
|
|
44641
45128
|
if addAliases:
|
|
44642
45129
|
createUserAddAliases(cd, result['primaryEmail'], addAliases, i, count)
|
|
44643
|
-
if notify.get('recipients'):
|
|
45130
|
+
if (notify.get('recipients') or (parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'))):
|
|
44644
45131
|
notify['password'] = notify['notFoundPassword']
|
|
45132
|
+
if parameters['notifyRecoveryEmail'] and result.get('recoveryEmail'):
|
|
45133
|
+
notify['recipients'].append(result['recoveryEmail'])
|
|
44645
45134
|
sendCreateUpdateUserNotification(result, notify, tagReplacements, i, count)
|
|
44646
45135
|
except GAPI.duplicate:
|
|
44647
45136
|
duplicateAliasGroupUserWarning(cd, [Ent.USER, body['primaryEmail']], i, count)
|
|
@@ -45688,7 +46177,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
|
|
|
45688
46177
|
# [userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
|
|
45689
46178
|
# [delimiter <Character>] [sortheaders] [formatjson [quotechar <Character>]] [quoteplusphonenumbers]
|
|
45690
46179
|
# [convertcrnl]
|
|
45691
|
-
# [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
46180
|
+
# [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
45692
46181
|
# [showitemcountonly]
|
|
45693
46182
|
# [showvalidcolumn] (addcsvdata <FieldName> <String>)*
|
|
45694
46183
|
#
|
|
@@ -45701,7 +46190,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
|
|
|
45701
46190
|
# [userview] [basic|full|allfields | <UserFieldName>* | fields <UserFieldNameList>]
|
|
45702
46191
|
# [delimiter <Character>] [sortheaders] [formatjson [quotechar <Character>]] [quoteplusphonenumbers]
|
|
45703
46192
|
# [convertcrnl]
|
|
45704
|
-
# [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
46193
|
+
# [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
45705
46194
|
# [showitemcountonly]
|
|
45706
46195
|
# [showvalidcolumn] (addcsvdata <FieldName> <String>)*
|
|
45707
46196
|
#
|
|
@@ -45709,13 +46198,13 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
|
|
|
45709
46198
|
# ([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
|
45710
46199
|
# [limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>]
|
|
45711
46200
|
# [formatjson [quotechar <Character>]] [countonly]
|
|
45712
|
-
# [issuspended <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
46201
|
+
# [issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
|
45713
46202
|
# [showitemcountonly]
|
|
45714
46203
|
# [showvalidcolumn] (addcsvdata <FieldName> <String>)*
|
|
45715
46204
|
#
|
|
45716
46205
|
# gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
|
|
45717
46206
|
# [formatjson [quotechar <Character>]] [countonly]
|
|
45718
|
-
# [issuspended <Boolean>]
|
|
46207
|
+
# [issuspended <Boolean>] [isarchived <Boolean>]
|
|
45719
46208
|
# [showitemcountonly]
|
|
45720
46209
|
def doPrintUsers(entityList=None):
|
|
45721
46210
|
def _writeUserEntity(userEntity):
|
|
@@ -45733,71 +46222,81 @@ def doPrintUsers(entityList=None):
|
|
|
45733
46222
|
csvPF.WriteRowNoFilter(row)
|
|
45734
46223
|
|
|
45735
46224
|
def _printUser(userEntity, i, count):
|
|
45736
|
-
if isSuspended is None
|
|
45737
|
-
|
|
45738
|
-
|
|
45739
|
-
|
|
45740
|
-
|
|
45741
|
-
|
|
45742
|
-
|
|
45743
|
-
|
|
45744
|
-
|
|
45745
|
-
|
|
45746
|
-
|
|
45747
|
-
|
|
45748
|
-
|
|
45749
|
-
|
|
45750
|
-
|
|
45751
|
-
|
|
45752
|
-
|
|
45753
|
-
|
|
45754
|
-
|
|
45755
|
-
|
|
45756
|
-
|
|
45757
|
-
|
|
45758
|
-
|
|
45759
|
-
|
|
45760
|
-
|
|
45761
|
-
|
|
45762
|
-
|
|
45763
|
-
|
|
45764
|
-
|
|
45765
|
-
|
|
45766
|
-
|
|
45767
|
-
|
|
45768
|
-
|
|
45769
|
-
|
|
45770
|
-
|
|
45771
|
-
|
|
45772
|
-
|
|
45773
|
-
|
|
45774
|
-
|
|
45775
|
-
|
|
45776
|
-
if aliasMatchPattern and 'aliases' in userEntity:
|
|
45777
|
-
userEntity['aliases'] = [alias for alias in userEntity['aliases'] if aliasMatchPattern.match(alias)]
|
|
45778
|
-
if printOptions['getLicenseFeed'] or printOptions['getLicenseFeedByUser']:
|
|
45779
|
-
if printOptions['getLicenseFeed']:
|
|
45780
|
-
u_licenses = licenses.get(userEmail.lower(), [])
|
|
46225
|
+
if (isSuspended is None and isArchived is None):
|
|
46226
|
+
showUser = True
|
|
46227
|
+
elif (isSuspended is not None and isArchived is None):
|
|
46228
|
+
showUser = isSuspended == userEntity.get('suspended', isSuspended)
|
|
46229
|
+
elif (isSuspended is None and isArchived is not None):
|
|
46230
|
+
showUser = isArchived == userEntity.get('archived', isArchived)
|
|
46231
|
+
else:
|
|
46232
|
+
showUser = ((isSuspended == userEntity.get('suspended', isSuspended)) or
|
|
46233
|
+
(isArchived == userEntity.get('archived', isArchived)))
|
|
46234
|
+
if not showUser:
|
|
46235
|
+
return
|
|
46236
|
+
if showValidColumn:
|
|
46237
|
+
userEntity[showValidColumn] = True
|
|
46238
|
+
userEmail = userEntity['primaryEmail']
|
|
46239
|
+
if printOptions['emailParts']:
|
|
46240
|
+
if userEmail.find('@') != -1:
|
|
46241
|
+
userEntity['primaryEmailLocal'], userEntity['primaryEmailDomain'] = splitEmailAddress(userEmail)
|
|
46242
|
+
if 'languages' in userEntity and not FJQC.formatJSON:
|
|
46243
|
+
userEntity['languages'] = _formatLanguagesList(userEntity.pop('languages'), delimiter)
|
|
46244
|
+
for location in userEntity.get('locations', []):
|
|
46245
|
+
location['buildingName'] = _getBuildingNameById(cd, location.get('buildingId', ''))
|
|
46246
|
+
if quotePlusPhoneNumbers:
|
|
46247
|
+
for phone in userEntity.get('phones', []):
|
|
46248
|
+
phoneNumber = phone.get('value', '')
|
|
46249
|
+
if phoneNumber.startswith('+'):
|
|
46250
|
+
phone['value'] = "'"+phoneNumber
|
|
46251
|
+
if schemaParms['selectedSchemaFields']:
|
|
46252
|
+
_filterSchemaFields(userEntity, schemaParms)
|
|
46253
|
+
if printOptions['getGroupFeed']:
|
|
46254
|
+
printGettingAllEntityItemsForWhom(Ent.GROUP_MEMBERSHIP, userEmail, i, count)
|
|
46255
|
+
try:
|
|
46256
|
+
groups = callGAPIpages(cd.groups(), 'list', 'groups',
|
|
46257
|
+
pageMessage=getPageMessageForWhom(),
|
|
46258
|
+
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
|
|
46259
|
+
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
46260
|
+
userKey=userEmail, orderBy='email', fields='nextPageToken,groups(email)')
|
|
46261
|
+
numGroups = len(groups)
|
|
46262
|
+
if not printOptions['groupsInColumns']:
|
|
46263
|
+
userEntity['GroupsCount'] = numGroups
|
|
46264
|
+
userEntity['Groups'] = delimiter.join([groupname['email'] for groupname in groups])
|
|
45781
46265
|
else:
|
|
45782
|
-
|
|
45783
|
-
|
|
45784
|
-
userEntity['
|
|
45785
|
-
|
|
45786
|
-
userEntity['
|
|
45787
|
-
|
|
46266
|
+
if numGroups > printOptions['maxGroups']:
|
|
46267
|
+
printOptions['maxGroups'] = numGroups
|
|
46268
|
+
userEntity['Groups'] = numGroups
|
|
46269
|
+
for j, group in enumerate(groups):
|
|
46270
|
+
userEntity[f'Groups{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{j}'] = group['email']
|
|
46271
|
+
except (GAPI.invalidMember, GAPI.invalidInput):
|
|
46272
|
+
badRequestWarning(Ent.GROUP, Ent.MEMBER, userEmail)
|
|
46273
|
+
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
|
|
46274
|
+
accessErrorExit(cd)
|
|
46275
|
+
if aliasMatchPattern and 'aliases' in userEntity:
|
|
46276
|
+
userEntity['aliases'] = [alias for alias in userEntity['aliases'] if aliasMatchPattern.match(alias)]
|
|
46277
|
+
if printOptions['getLicenseFeed'] or printOptions['getLicenseFeedByUser']:
|
|
46278
|
+
if printOptions['getLicenseFeed']:
|
|
46279
|
+
u_licenses = licenses.get(userEmail.lower(), [])
|
|
45788
46280
|
else:
|
|
45789
|
-
u_licenses =
|
|
46281
|
+
u_licenses = getUserLicenses(lic, userEntity, skus)
|
|
45790
46282
|
if not oneLicensePerRow:
|
|
45791
|
-
|
|
45792
|
-
else:
|
|
46283
|
+
userEntity['LicensesCount'] = len(u_licenses)
|
|
45793
46284
|
if u_licenses:
|
|
45794
|
-
|
|
45795
|
-
|
|
45796
|
-
|
|
45797
|
-
|
|
45798
|
-
|
|
45799
|
-
|
|
46285
|
+
userEntity['Licenses'] = delimiter.join(u_licenses)
|
|
46286
|
+
userEntity['LicensesDisplay'] = delimiter.join([SKU.skuIdToDisplayName(skuId) for skuId in u_licenses])
|
|
46287
|
+
else:
|
|
46288
|
+
u_licenses = []
|
|
46289
|
+
if not oneLicensePerRow:
|
|
46290
|
+
_writeUserEntity(userEntity)
|
|
46291
|
+
else:
|
|
46292
|
+
if u_licenses:
|
|
46293
|
+
for skuId in u_licenses:
|
|
46294
|
+
userEntity['License'] = skuId
|
|
46295
|
+
userEntity['LicenseDisplay'] = SKU.skuIdToDisplayName(skuId)
|
|
45800
46296
|
_writeUserEntity(userEntity)
|
|
46297
|
+
else:
|
|
46298
|
+
userEntity['License'] = userEntity['LicenseDisplay'] = ''
|
|
46299
|
+
_writeUserEntity(userEntity)
|
|
45801
46300
|
|
|
45802
46301
|
def _updateDomainCounts(emailAddress):
|
|
45803
46302
|
nonlocal domainCounts
|
|
@@ -45871,7 +46370,7 @@ def doPrintUsers(entityList=None):
|
|
|
45871
46370
|
schemaParms = _initSchemaParms('basic')
|
|
45872
46371
|
projectionSet = False
|
|
45873
46372
|
oneLicensePerRow = quotePlusPhoneNumbers = showDeleted = False
|
|
45874
|
-
aliasMatchPattern = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None
|
|
46373
|
+
aliasMatchPattern = isArchived = isSuspended = orgUnitPath = orgUnitPathLower = orderBy = sortOrder = None
|
|
45875
46374
|
viewType = 'admin_view'
|
|
45876
46375
|
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
|
45877
46376
|
showValidColumn = ''
|
|
@@ -45892,6 +46391,8 @@ def doPrintUsers(entityList=None):
|
|
|
45892
46391
|
_, entityList = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
45893
46392
|
elif myarg == 'issuspended':
|
|
45894
46393
|
isSuspended = getBoolean()
|
|
46394
|
+
elif myarg == 'isarchived':
|
|
46395
|
+
isArchived = getBoolean()
|
|
45895
46396
|
elif myarg == 'orderby':
|
|
45896
46397
|
orderBy, sortOrder = getOrderBySortOrder(USERS_ORDERBY_CHOICE_MAP)
|
|
45897
46398
|
elif myarg == 'userview':
|
|
@@ -46004,7 +46505,7 @@ def doPrintUsers(entityList=None):
|
|
|
46004
46505
|
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
|
46005
46506
|
kwargs = kwargsQuery[0]
|
|
46006
46507
|
query = kwargsQuery[1]
|
|
46007
|
-
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
|
|
46508
|
+
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended, isArchived)
|
|
46008
46509
|
printGettingAllAccountEntities(Ent.USER, pquery)
|
|
46009
46510
|
pageMessage = getPageMessage(showFirstLastItems=True)
|
|
46010
46511
|
try:
|
|
@@ -46014,9 +46515,9 @@ def doPrintUsers(entityList=None):
|
|
|
46014
46515
|
GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN,
|
|
46015
46516
|
GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION],
|
|
46016
46517
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS+[GAPI.UNKNOWN_ERROR, GAPI.FAILED_PRECONDITION],
|
|
46017
|
-
query=query, fields=fields,
|
|
46018
46518
|
showDeleted=showDeleted, orderBy=orderBy, sortOrder=sortOrder, viewType=viewType,
|
|
46019
46519
|
projection=schemaParms['projection'], customFieldMask=schemaParms['customFieldMask'],
|
|
46520
|
+
query=query, fields=fields,
|
|
46020
46521
|
maxResults=maxResults, **kwargs)
|
|
46021
46522
|
for users in feed:
|
|
46022
46523
|
if orgUnitPath is None:
|
|
@@ -46069,6 +46570,8 @@ def doPrintUsers(entityList=None):
|
|
|
46069
46570
|
# If no individual fields were specified (allfields, basic, full) or individual fields other than primaryEmail were specified, look up each user
|
|
46070
46571
|
if isSuspended is not None and fieldsList:
|
|
46071
46572
|
fieldsList.append('suspended')
|
|
46573
|
+
if isArchived is not None and fieldsList:
|
|
46574
|
+
fieldsList.append('archived')
|
|
46072
46575
|
if projectionSet or len(set(fieldsList)) > 1 or showValidColumn:
|
|
46073
46576
|
jcount = len(entityList)
|
|
46074
46577
|
fields = getFieldsFromFieldsList(fieldsList)
|
|
@@ -46864,7 +47367,7 @@ def doCreateInboundSSOCredential():
|
|
|
46864
47367
|
count, Ent.Choose(Ent.INBOUND_SSO_CREDENTIALS, count)))
|
|
46865
47368
|
if generateKey:
|
|
46866
47369
|
privKey, pemData = _generatePrivateKeyAndPublicCert('', '', 'GAM', keySize, b64enc_pub=False)
|
|
46867
|
-
timestamp =
|
|
47370
|
+
timestamp = arrow.now(GC.Values[GC.TIMEZONE]).strftime('%Y%m%d-%I%M%S')
|
|
46868
47371
|
priv_file = f'privatekey-{timestamp}.pem'
|
|
46869
47372
|
writeFile(priv_file, privKey)
|
|
46870
47373
|
writeStdout(Msg.WROTE_PRIVATE_KEY_DATA.format(priv_file))
|
|
@@ -47936,8 +48439,8 @@ class CourseAttributes():
|
|
|
47936
48439
|
def checkDueDate(self, body):
|
|
47937
48440
|
if 'dueDate' in body and 'dueTime' in body:
|
|
47938
48441
|
try:
|
|
47939
|
-
return self.currDateTime <
|
|
47940
|
-
|
|
48442
|
+
return self.currDateTime < arrow.Arrow(body['dueDate']['year'], body['dueDate']['month'], body['dueDate']['day'],
|
|
48443
|
+
body['dueTime'].get('hours', 0), body['dueTime'].get('minutes', 0), tzinfo='UTC')
|
|
47941
48444
|
except ValueError:
|
|
47942
48445
|
pass
|
|
47943
48446
|
return False
|
|
@@ -48119,7 +48622,7 @@ class CourseAttributes():
|
|
|
48119
48622
|
entityPerformActionModifierItemValueList([Ent.COURSE, newCourse['id']], Act.MODIFIER_FROM, [Ent.COURSE, self.courseId], i, count)
|
|
48120
48623
|
Ind.Increment()
|
|
48121
48624
|
if not self.removeDueDate:
|
|
48122
|
-
self.currDateTime =
|
|
48625
|
+
self.currDateTime = arrow.utcnow()
|
|
48123
48626
|
self.CopyAttributes(newCourse, i, count)
|
|
48124
48627
|
if self.csvPF:
|
|
48125
48628
|
self.csvPF.writeCSVfile('Course Drive File IDs')
|
|
@@ -48640,13 +49143,15 @@ def _doInfoCourses(courseIdList):
|
|
|
48640
49143
|
|
|
48641
49144
|
# gam info courses <CourseEntity> [owneraccess]
|
|
48642
49145
|
# [owneremail] [alias|aliases] [show none|all|students|teachers] [countsonly]
|
|
48643
|
-
# [fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
|
|
49146
|
+
# [fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
|
|
49147
|
+
# [formatjson]
|
|
48644
49148
|
def doInfoCourses():
|
|
48645
49149
|
_doInfoCourses(getEntityList(Cmd.OB_COURSE_ENTITY, shlexSplit=True))
|
|
48646
49150
|
|
|
48647
49151
|
# gam info course <CourseID> [owneraccess]
|
|
48648
49152
|
# [owneremail] [alias|aliases] [show none|all|students|teachers] [countsonly]
|
|
48649
|
-
# [fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
|
|
49153
|
+
# [fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
|
|
49154
|
+
# [formatjson]
|
|
48650
49155
|
def doInfoCourse():
|
|
48651
49156
|
_doInfoCourses(getStringReturnInList(Cmd.OB_COURSE_ID))
|
|
48652
49157
|
|
|
@@ -48699,7 +49204,7 @@ def _courseItemPassesFilter(item, courseItemFilter):
|
|
|
48699
49204
|
return False
|
|
48700
49205
|
startTime = courseItemFilter['startTime']
|
|
48701
49206
|
endTime = courseItemFilter['endTime']
|
|
48702
|
-
timeValue
|
|
49207
|
+
timeValue = arrow.get(timeStr)
|
|
48703
49208
|
return ((startTime is None) or (timeValue >= startTime)) and ((endTime is None) or (timeValue <= endTime))
|
|
48704
49209
|
|
|
48705
49210
|
def _gettingCoursesQuery(courseSelectionParameters):
|
|
@@ -48762,6 +49267,7 @@ def _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties, getO
|
|
|
48762
49267
|
# [show none|all|students|teachers] [countsonly]
|
|
48763
49268
|
# [fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
|
|
48764
49269
|
# [timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
49270
|
+
# (addcsvdata <FieldName> <String>)*
|
|
48765
49271
|
# [showitemcountonly] [formatjson [quotechar <Character>]]
|
|
48766
49272
|
def doPrintCourses():
|
|
48767
49273
|
def _saveParticipants(course, participants, role, rtitles):
|
|
@@ -48805,6 +49311,7 @@ def doPrintCourses():
|
|
|
48805
49311
|
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
|
48806
49312
|
showItemCountOnly = False
|
|
48807
49313
|
useOwnerAccess = GC.Values[GC.USE_COURSE_OWNER_ACCESS]
|
|
49314
|
+
addCSVData = {}
|
|
48808
49315
|
while Cmd.ArgumentsRemaining():
|
|
48809
49316
|
myarg = getArgument()
|
|
48810
49317
|
if myarg == 'todrive':
|
|
@@ -48819,6 +49326,9 @@ def doPrintCourses():
|
|
|
48819
49326
|
pass
|
|
48820
49327
|
elif myarg == 'showitemcountonly':
|
|
48821
49328
|
showItemCountOnly = True
|
|
49329
|
+
elif myarg == 'addcsvdata':
|
|
49330
|
+
k = getString(Cmd.OB_STRING)
|
|
49331
|
+
addCSVData[k] = getString(Cmd.OB_STRING, minLen=0)
|
|
48822
49332
|
else:
|
|
48823
49333
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
48824
49334
|
applyCourseItemFilter = _setApplyCourseItemFilter(courseItemFilter, None)
|
|
@@ -48830,6 +49340,11 @@ def doPrintCourses():
|
|
|
48830
49340
|
if showItemCountOnly:
|
|
48831
49341
|
writeStdout('0\n')
|
|
48832
49342
|
return
|
|
49343
|
+
if addCSVData:
|
|
49344
|
+
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
49345
|
+
if FJQC.formatJSON:
|
|
49346
|
+
csvPF.AddJSONTitles(sorted(addCSVData.keys()))
|
|
49347
|
+
csvPF.MoveJSONTitlesToEnd(['JSON'])
|
|
48833
49348
|
if courseShowProperties['aliases']:
|
|
48834
49349
|
if FJQC.formatJSON:
|
|
48835
49350
|
csvPF.AddJSONTitles('JSON-aliases')
|
|
@@ -48887,11 +49402,15 @@ def doPrintCourses():
|
|
|
48887
49402
|
if courseShowProperties['members'] != 'teachers':
|
|
48888
49403
|
_saveParticipants(course, students, 'students', stitles)
|
|
48889
49404
|
row = flattenJSON(course, timeObjects=COURSE_TIME_OBJECTS, noLenObjects=COURSE_NOLEN_OBJECTS)
|
|
49405
|
+
if addCSVData:
|
|
49406
|
+
row.update(addCSVData)
|
|
48890
49407
|
if not FJQC.formatJSON:
|
|
48891
49408
|
csvPF.WriteRowTitles(row)
|
|
48892
49409
|
elif csvPF.CheckRowTitles(row):
|
|
48893
49410
|
row = {'id': courseId, 'JSON': json.dumps(cleanJSON(course, timeObjects=COURSE_TIME_OBJECTS),
|
|
48894
49411
|
ensure_ascii=False, sort_keys=True)}
|
|
49412
|
+
if addCSVData:
|
|
49413
|
+
row.update(addCSVData)
|
|
48895
49414
|
if courseShowProperties['aliases']:
|
|
48896
49415
|
row['JSON-aliases'] = json.dumps(list(aliases))
|
|
48897
49416
|
if courseShowProperties['members'] != 'none':
|
|
@@ -48964,7 +49483,7 @@ COURSE_ANNOUNCEMENTS_INDEXED_TITLES = ['materials']
|
|
|
48964
49483
|
# (orderby <CourseAnnouncementOrderByFieldName> [ascending|descending])*)
|
|
48965
49484
|
# [showcreatoremails|creatoremail] [fields <CourseAnnouncementFieldNameList>]
|
|
48966
49485
|
# [timefilter creationtime|updatetime|scheduledtime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
48967
|
-
# [countsonly
|
|
49486
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
48968
49487
|
def doPrintCourseAnnouncements():
|
|
48969
49488
|
def _printCourseAnnouncement(course, courseAnnouncement, i, count):
|
|
48970
49489
|
if applyCourseItemFilter and not _courseItemPassesFilter(courseAnnouncement, courseItemFilter):
|
|
@@ -49020,6 +49539,8 @@ def doPrintCourseAnnouncements():
|
|
|
49020
49539
|
coursesInfo = _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties)
|
|
49021
49540
|
if coursesInfo is None:
|
|
49022
49541
|
return
|
|
49542
|
+
if countsOnly:
|
|
49543
|
+
csvPF.SetFormatJSON(False)
|
|
49023
49544
|
applyCourseItemFilter = _setApplyCourseItemFilter(courseItemFilter, fieldsList)
|
|
49024
49545
|
if showCreatorEmail and fieldsList:
|
|
49025
49546
|
fieldsList.append('creatorUserId')
|
|
@@ -49076,7 +49597,7 @@ COURSE_TOPICS_SORT_TITLES = ['courseId', 'courseName', 'topicId', 'name', 'updat
|
|
|
49076
49597
|
# (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] states <CourseStateList>])
|
|
49077
49598
|
# [topicids <CourseTopicIDEntity>]
|
|
49078
49599
|
# [timefilter updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
49079
|
-
# [countsonly
|
|
49600
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
49080
49601
|
def doPrintCourseTopics():
|
|
49081
49602
|
def _printCourseTopic(course, courseTopic):
|
|
49082
49603
|
if applyCourseItemFilter and not _courseItemPassesFilter(courseTopic, courseItemFilter):
|
|
@@ -49117,6 +49638,8 @@ def doPrintCourseTopics():
|
|
|
49117
49638
|
coursesInfo = _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties)
|
|
49118
49639
|
if coursesInfo is None:
|
|
49119
49640
|
return
|
|
49641
|
+
if countsOnly:
|
|
49642
|
+
csvPF.SetFormatJSON(False)
|
|
49120
49643
|
applyCourseItemFilter = _setApplyCourseItemFilter(courseItemFilter, fieldsList)
|
|
49121
49644
|
courseTopicIdsLists = courseTopicIds if isinstance(courseTopicIds, dict) else None
|
|
49122
49645
|
i = 0
|
|
@@ -49250,6 +49773,16 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49250
49773
|
pass
|
|
49251
49774
|
return topicNames
|
|
49252
49775
|
|
|
49776
|
+
def _printCourseWMrow(course, courseWM):
|
|
49777
|
+
row = flattenJSON(courseWM, flattened={'courseId': course['id'], 'courseName': course['name']}, timeObjects=TimeObjects,
|
|
49778
|
+
simpleLists=['studentIds'] if showStudentsAsList else None, delimiter=delimiter)
|
|
49779
|
+
if not FJQC.formatJSON:
|
|
49780
|
+
csvPF.WriteRowTitles(row)
|
|
49781
|
+
elif csvPF.CheckRowTitles(row):
|
|
49782
|
+
csvPF.WriteRowNoFilter({'courseId': course['id'], 'courseName': course['name'],
|
|
49783
|
+
'JSON': json.dumps(cleanJSON(courseWM, timeObjects=TimeObjects),
|
|
49784
|
+
ensure_ascii=False, sort_keys=True)})
|
|
49785
|
+
|
|
49253
49786
|
def _printCourseWM(course, courseWM, i, count):
|
|
49254
49787
|
if applyCourseItemFilter and not _courseItemPassesFilter(courseWM, courseItemFilter):
|
|
49255
49788
|
return
|
|
@@ -49261,14 +49794,13 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49261
49794
|
topicId = courseWM.get('topicId')
|
|
49262
49795
|
if topicId:
|
|
49263
49796
|
courseWM['topicName'] = topicNames.get(topicId, topicId)
|
|
49264
|
-
|
|
49265
|
-
|
|
49266
|
-
|
|
49267
|
-
|
|
49268
|
-
|
|
49269
|
-
|
|
49270
|
-
|
|
49271
|
-
ensure_ascii=False, sort_keys=True)})
|
|
49797
|
+
if not oneItemPerRow or not courseWM.get('materials', []):
|
|
49798
|
+
_printCourseWMrow(course, courseWM)
|
|
49799
|
+
else:
|
|
49800
|
+
courseMaterials = courseWM.pop('materials')
|
|
49801
|
+
for courseMaterial in courseMaterials:
|
|
49802
|
+
courseWM['materials'] = courseMaterial
|
|
49803
|
+
_printCourseWMrow(course, courseWM)
|
|
49272
49804
|
|
|
49273
49805
|
croom = buildGAPIObject(API.CLASSROOM)
|
|
49274
49806
|
if entityIDType == Ent.COURSE_WORK_ID:
|
|
@@ -49306,7 +49838,7 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49306
49838
|
courseShowProperties = _initCourseShowProperties(['name'])
|
|
49307
49839
|
OBY = OrderBy(OrderbyChoiceMap)
|
|
49308
49840
|
creatorEmails = {}
|
|
49309
|
-
showCreatorEmail = showTopicNames = False
|
|
49841
|
+
oneItemPerRow = showCreatorEmail = showTopicNames = False
|
|
49310
49842
|
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
|
49311
49843
|
countsOnly = showStudentsAsList = False
|
|
49312
49844
|
while Cmd.ArgumentsRemaining():
|
|
@@ -49323,6 +49855,9 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49323
49855
|
pass
|
|
49324
49856
|
elif myarg == 'orderby':
|
|
49325
49857
|
OBY.GetChoice()
|
|
49858
|
+
elif myarg == 'oneitemperrow':
|
|
49859
|
+
oneItemPerRow = True
|
|
49860
|
+
csvPF.RemoveIndexedTitles('materials')
|
|
49326
49861
|
elif myarg in {'showcreatoremails', 'creatoremail'}:
|
|
49327
49862
|
showCreatorEmail = True
|
|
49328
49863
|
elif myarg == 'showtopicnames':
|
|
@@ -49349,6 +49884,8 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49349
49884
|
coursesInfo = _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties)
|
|
49350
49885
|
if coursesInfo is None:
|
|
49351
49886
|
return
|
|
49887
|
+
if countsOnly:
|
|
49888
|
+
csvPF.SetFormatJSON(False)
|
|
49352
49889
|
applyCourseItemFilter = _setApplyCourseItemFilter(courseItemFilter, fieldsList)
|
|
49353
49890
|
courseWMIds = courseWMSelectionParameters['courseWMIds']
|
|
49354
49891
|
courseWMIdsLists = courseWMIds if isinstance(courseWMIds, dict) else {}
|
|
@@ -49403,7 +49940,8 @@ def doPrintCourseWM(entityIDType, entityStateType):
|
|
|
49403
49940
|
# (orderby <CourseMaterialsOrderByFieldName> [ascending|descending])*)
|
|
49404
49941
|
# [showcreatoremails|creatoremail] [showtopicnames] [fields <CourseMaterialFieldNameList>]
|
|
49405
49942
|
# [timefilter creationtime|updatetime|scheduledtime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
49406
|
-
# [
|
|
49943
|
+
# [oneitemperrow]
|
|
49944
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
49407
49945
|
def doPrintCourseMaterials():
|
|
49408
49946
|
doPrintCourseWM(Ent.COURSE_MATERIAL_ID, Ent.COURSE_MATERIAL_STATE)
|
|
49409
49947
|
|
|
@@ -49414,7 +49952,8 @@ def doPrintCourseMaterials():
|
|
|
49414
49952
|
# [showcreatoremails|creatoremail] [showtopicnames] [fields <CourseWorkFieldNameList>]
|
|
49415
49953
|
# [showstudentsaslist [<Boolean>]] [delimiter <Character>]
|
|
49416
49954
|
# [timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
49417
|
-
# [
|
|
49955
|
+
# [oneitemperrow]
|
|
49956
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
49418
49957
|
def doPrintCourseWork():
|
|
49419
49958
|
doPrintCourseWM(Ent.COURSE_WORK_ID, Ent.COURSE_WORK_STATE)
|
|
49420
49959
|
|
|
@@ -49458,7 +49997,7 @@ def _gettingCourseSubmissionQuery(courseSubmissionStates, late, userId):
|
|
|
49458
49997
|
# (submissionids <CourseSubmissionIDEntity>)|((submissionstates <CourseSubmissionStateList>)*) [late|notlate]
|
|
49459
49998
|
# [fields <CourseSubmissionFieldNameList>] [showuserprofile]
|
|
49460
49999
|
# [timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
|
49461
|
-
# [countsonly
|
|
50000
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
49462
50001
|
def doPrintCourseSubmissions():
|
|
49463
50002
|
def _printCourseSubmission(course, courseSubmission):
|
|
49464
50003
|
if applyCourseItemFilter and not _courseItemPassesFilter(courseSubmission, courseItemFilter):
|
|
@@ -49540,6 +50079,8 @@ def doPrintCourseSubmissions():
|
|
|
49540
50079
|
coursesInfo = _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties, getOwnerId=True)
|
|
49541
50080
|
if coursesInfo is None:
|
|
49542
50081
|
return
|
|
50082
|
+
if countsOnly:
|
|
50083
|
+
csvPF.SetFormatJSON(False)
|
|
49543
50084
|
applyCourseItemFilter = _setApplyCourseItemFilter(courseItemFilter, fieldsList)
|
|
49544
50085
|
courseWorkIds = courseWMSelectionParameters['courseWMIds']
|
|
49545
50086
|
courseWorkIdsLists = courseWorkIds if isinstance(courseWorkIds, dict) else {}
|
|
@@ -53286,16 +53827,16 @@ def infoCalendarEvents(users):
|
|
|
53286
53827
|
|
|
53287
53828
|
# gam <UserTypeEntity> print events <UserCalendarEntity> <EventEntity> <EventDisplayProperties>*
|
|
53288
53829
|
# [fields <EventFieldNameList>] [showdayofweek]
|
|
53289
|
-
#
|
|
53290
|
-
# [
|
|
53830
|
+
# (addcsvdata <FieldName> <String>)*
|
|
53831
|
+
# [eventrowfilter]
|
|
53832
|
+
# [countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
|
53291
53833
|
# gam <UserTypeEntity> show events <UserCalendarEntity> <EventEntity> <EventDisplayProperties>*
|
|
53292
53834
|
# [fields <EventFieldNameList>] [showdayofweek]
|
|
53293
|
-
# [countsonly]
|
|
53294
|
-
# [formatjson]
|
|
53835
|
+
# ~[countsonly|formatjson]
|
|
53295
53836
|
def printShowCalendarEvents(users):
|
|
53296
53837
|
calendarEntity = getUserCalendarEntity()
|
|
53297
53838
|
calendarEventEntity = getCalendarEventEntity()
|
|
53298
|
-
csvPF, FJQC, fieldsList = _getCalendarPrintShowEventOptions(calendarEventEntity, Ent.USER)
|
|
53839
|
+
csvPF, FJQC, fieldsList, addCSVData = _getCalendarPrintShowEventOptions(calendarEventEntity, Ent.USER)
|
|
53299
53840
|
i, count, users = getEntityArgument(users)
|
|
53300
53841
|
for user in users:
|
|
53301
53842
|
i += 1
|
|
@@ -53306,7 +53847,7 @@ def printShowCalendarEvents(users):
|
|
|
53306
53847
|
continue
|
|
53307
53848
|
Ind.Increment()
|
|
53308
53849
|
_printShowCalendarEvents(origUser, user, cal, calIds, jcount, calendarEventEntity,
|
|
53309
|
-
csvPF, FJQC, fieldsList)
|
|
53850
|
+
csvPF, FJQC, fieldsList, addCSVData)
|
|
53310
53851
|
Ind.Decrement()
|
|
53311
53852
|
if csvPF:
|
|
53312
53853
|
if calendarEventEntity['countsOnly'] and calendarEventEntity['eventRowFilter']:
|
|
@@ -53330,22 +53871,21 @@ def getStatusEventDateTime(dateType, dateList):
|
|
|
53330
53871
|
firstDate = getYYYYMMDD(minLen=1, returnDateTime=True).replace(tzinfo=GC.Values[GC.TIMEZONE])
|
|
53331
53872
|
if dateType == 'range':
|
|
53332
53873
|
lastDate = getYYYYMMDD(minLen=1, returnDateTime=True).replace(tzinfo=GC.Values[GC.TIMEZONE])
|
|
53333
|
-
deltaDay = datetime.timedelta(days=1)
|
|
53334
|
-
deltaWeek = datetime.timedelta(weeks=1)
|
|
53335
53874
|
if dateType in {'date', 'allday'}:
|
|
53336
|
-
dateList.append({'type': 'date', 'first': firstDate, 'last': firstDate
|
|
53337
|
-
'ulast': firstDate
|
|
53875
|
+
dateList.append({'type': 'date', 'first': firstDate, 'last': firstDate.shift(days=1),
|
|
53876
|
+
'ulast': firstDate.shift(days=1), 'udelta': {'days': 1}})
|
|
53338
53877
|
elif dateType == 'range':
|
|
53339
|
-
dateList.append({'type': dateType, 'first': firstDate, 'last': lastDate
|
|
53340
|
-
'ulast': lastDate, 'udelta':
|
|
53878
|
+
dateList.append({'type': dateType, 'first': firstDate, 'last': lastDate.shift(days=1),
|
|
53879
|
+
'ulast': lastDate, 'udelta': {'days': 1}})
|
|
53341
53880
|
elif dateType == 'daily':
|
|
53342
53881
|
argRepeat = getInteger(minVal=1, maxVal=366)
|
|
53343
|
-
dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate
|
|
53344
|
-
'ulast': firstDate
|
|
53882
|
+
dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate.shift(days=argRepeat),
|
|
53883
|
+
'ulast': firstDate.shift(days=argRepeat), 'udelta': {'days': 1}})
|
|
53345
53884
|
else: #weekly
|
|
53346
53885
|
argRepeat = getInteger(minVal=1, maxVal=52)
|
|
53347
|
-
dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate
|
|
53348
|
-
'
|
|
53886
|
+
dateList.append({'type': dateType, 'first': firstDate, 'last': firstDate.shift(days=1),
|
|
53887
|
+
'pdelta': {'weeks': 1}, 'repeats': argRepeat,
|
|
53888
|
+
'ulast': firstDate.shift(weeks=argRepeat), 'udelta': {'weeks': 1}})
|
|
53349
53889
|
|
|
53350
53890
|
def _showCalendarStatusEvent(primaryEmail, calId, eventEntityType, event, k, kcount, FJQC):
|
|
53351
53891
|
if FJQC.formatJSON:
|
|
@@ -53533,7 +54073,7 @@ def createStatusEvent(users, eventType):
|
|
|
53533
54073
|
if wlDate['type'] != 'timerange':
|
|
53534
54074
|
body['start']['date'] = first.strftime(YYYYMMDD_FORMAT)
|
|
53535
54075
|
kvList[5] = body['start']['date']
|
|
53536
|
-
body['end']['date'] = (first
|
|
54076
|
+
body['end']['date'] = (first.shift(days=1)).strftime(YYYYMMDD_FORMAT)
|
|
53537
54077
|
else:
|
|
53538
54078
|
body['start']['dateTime'] = ISOformatTimeStamp(first)
|
|
53539
54079
|
kvList[5] = body['start']['dateTime']
|
|
@@ -53552,7 +54092,7 @@ def createStatusEvent(users, eventType):
|
|
|
53552
54092
|
entityActionPerformed(kvList, j, jcount)
|
|
53553
54093
|
if wlDate['type'] == 'timerange':
|
|
53554
54094
|
break
|
|
53555
|
-
first
|
|
54095
|
+
first = first.shift(**wlDate['udelta'])
|
|
53556
54096
|
except (GAPI.forbidden, GAPI.invalid) as e:
|
|
53557
54097
|
entityActionFailedWarning([Ent.CALENDAR, user], str(e), i, count)
|
|
53558
54098
|
break
|
|
@@ -53751,10 +54291,10 @@ def printShowStatusEvent(users, eventType):
|
|
|
53751
54291
|
for event in events:
|
|
53752
54292
|
if showDayOfWeek:
|
|
53753
54293
|
_getEventDaysOfWeek(event)
|
|
53754
|
-
_printCalendarEvent(user, calId, event, csvPF, FJQC)
|
|
54294
|
+
_printCalendarEvent(user, calId, event, csvPF, FJQC, {})
|
|
53755
54295
|
if 'pdelta' in wlDate:
|
|
53756
|
-
first
|
|
53757
|
-
last
|
|
54296
|
+
first = first.shift(**wlDate['pdelta'])
|
|
54297
|
+
last = last.shift(**wlDate['pdelta'])
|
|
53758
54298
|
if csvPF:
|
|
53759
54299
|
csvPF.writeCSVfile(f'Calendar {Ent.Plural(entityType)}')
|
|
53760
54300
|
|
|
@@ -54969,7 +55509,7 @@ def getDriveFileAttribute(myarg, body, parameters, updateCmd):
|
|
|
54969
55509
|
elif myarg == 'timestamp':
|
|
54970
55510
|
parameters[DFA_TIMESTAMP] = getBoolean()
|
|
54971
55511
|
elif myarg == 'timeformat':
|
|
54972
|
-
parameters[DFA_TIMEFORMAT] = getString(Cmd.
|
|
55512
|
+
parameters[DFA_TIMEFORMAT] = getString(Cmd.OB_DATETIME_FORMAT, minLen=0)
|
|
54973
55513
|
elif getDriveFileCopyAttribute(myarg, body, parameters):
|
|
54974
55514
|
pass
|
|
54975
55515
|
else:
|
|
@@ -56258,7 +56798,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
|
|
|
56258
56798
|
count = 0
|
|
56259
56799
|
if revisionsEntity['time'][0] == 'before':
|
|
56260
56800
|
for revision in results:
|
|
56261
|
-
modifiedDateTime
|
|
56801
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56262
56802
|
if modifiedDateTime >= dateTime:
|
|
56263
56803
|
break
|
|
56264
56804
|
revisionIds.append(revision['id'])
|
|
@@ -56268,7 +56808,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
|
|
|
56268
56808
|
return revisionIds
|
|
56269
56809
|
# time: after
|
|
56270
56810
|
for revision in results:
|
|
56271
|
-
modifiedDateTime
|
|
56811
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56272
56812
|
if modifiedDateTime >= dateTime:
|
|
56273
56813
|
revisionIds.append(revision['id'])
|
|
56274
56814
|
count += 1
|
|
@@ -56280,7 +56820,7 @@ def _selectRevisionIds(drive, fileId, origUser, user, i, count, j, jcount, revis
|
|
|
56280
56820
|
endDateTime = revisionsEntity['range'][2]
|
|
56281
56821
|
count = 0
|
|
56282
56822
|
for revision in results:
|
|
56283
|
-
modifiedDateTime
|
|
56823
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56284
56824
|
if modifiedDateTime >= startDateTime:
|
|
56285
56825
|
if modifiedDateTime >= endDateTime:
|
|
56286
56826
|
break
|
|
@@ -56490,7 +57030,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
|
|
|
56490
57030
|
count = 0
|
|
56491
57031
|
if revisionsEntity['time'][0] == 'before':
|
|
56492
57032
|
for revision in results:
|
|
56493
|
-
modifiedDateTime
|
|
57033
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56494
57034
|
if modifiedDateTime >= dateTime:
|
|
56495
57035
|
break
|
|
56496
57036
|
count += 1
|
|
@@ -56501,7 +57041,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
|
|
|
56501
57041
|
return results[:count]
|
|
56502
57042
|
# time: after
|
|
56503
57043
|
for revision in results:
|
|
56504
|
-
modifiedDateTime
|
|
57044
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56505
57045
|
if modifiedDateTime >= dateTime:
|
|
56506
57046
|
break
|
|
56507
57047
|
count += 1
|
|
@@ -56518,7 +57058,7 @@ def _selectRevisionResults(results, fileId, origUser, revisionsEntity, previewDe
|
|
|
56518
57058
|
count = 0
|
|
56519
57059
|
selectedResults = []
|
|
56520
57060
|
for revision in results:
|
|
56521
|
-
modifiedDateTime
|
|
57061
|
+
modifiedDateTime = arrow.get(revision['modifiedTime'])
|
|
56522
57062
|
if modifiedDateTime >= startDateTime:
|
|
56523
57063
|
if modifiedDateTime >= endDateTime:
|
|
56524
57064
|
break
|
|
@@ -57056,7 +57596,7 @@ class PermissionMatch():
|
|
|
57056
57596
|
break
|
|
57057
57597
|
elif field in {'expirationstart', 'expirationend'}:
|
|
57058
57598
|
if 'expirationTime' in permission:
|
|
57059
|
-
expirationDateTime
|
|
57599
|
+
expirationDateTime = arrow.get(permission['expirationTime'])
|
|
57060
57600
|
if field == 'expirationstart':
|
|
57061
57601
|
if expirationDateTime < value:
|
|
57062
57602
|
break
|
|
@@ -59539,7 +60079,7 @@ def processFilenameReplacements(name, replacements):
|
|
|
59539
60079
|
return name
|
|
59540
60080
|
|
|
59541
60081
|
def addTimestampToFilename(parameters, body):
|
|
59542
|
-
tdtime =
|
|
60082
|
+
tdtime = arrow.now(GC.Values[GC.TIMEZONE])
|
|
59543
60083
|
body['name'] += ' - '
|
|
59544
60084
|
if not parameters[DFA_TIMEFORMAT]:
|
|
59545
60085
|
body['name'] += ISOformatTimeStamp(tdtime)
|
|
@@ -59556,7 +60096,7 @@ createReturnItemMap = {
|
|
|
59556
60096
|
# [(localfile <FileName>|-)|(url <URL>)]
|
|
59557
60097
|
# [(drivefilename|newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
|
|
59558
60098
|
# [stripnameprefix <String>]
|
|
59559
|
-
# [timestamp <Boolean>]] [timeformat <
|
|
60099
|
+
# [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
|
|
59560
60100
|
# <DriveFileCreateAttribute>* [noduplicate]
|
|
59561
60101
|
# [(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*)) |
|
|
59562
60102
|
# (returnidonly|returnlinkonly|returneditlinkonly|showdetails)]
|
|
@@ -60083,7 +60623,7 @@ def checkDriveFileShortcut(users):
|
|
|
60083
60623
|
# [(localfile <FileName>|-)|(url <URL>)]
|
|
60084
60624
|
# [retainname | (newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
|
|
60085
60625
|
# [stripnameprefix <String>]
|
|
60086
|
-
# [timestamp <Boolean>]] [timeformat <
|
|
60626
|
+
# [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
|
|
60087
60627
|
# <DriveFileUpdateAttribute>*
|
|
60088
60628
|
# [(gsheet|csvsheet <SheetEntity> [clearfilter])|(addsheet <String>)]
|
|
60089
60629
|
# [charset <String>] [columndelimiter <Character>]
|
|
@@ -64531,11 +65071,11 @@ def claimOwnership(users):
|
|
|
64531
65071
|
elif myarg == 'onlyusers':
|
|
64532
65072
|
_, userList = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
64533
65073
|
checkOnly = True
|
|
64534
|
-
onlyOwners =
|
|
65074
|
+
onlyOwners = {normalizeEmailAddressOrUID(user, noUid=True) for user in userList}
|
|
64535
65075
|
elif myarg == 'skipusers':
|
|
64536
65076
|
_, userList = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
|
64537
65077
|
checkSkip = len(userList) > 0
|
|
64538
|
-
skipOwners =
|
|
65078
|
+
skipOwners = {normalizeEmailAddressOrUID(user, noUid=True) for user in userList}
|
|
64539
65079
|
elif myarg == 'subdomains':
|
|
64540
65080
|
subdomains = getEntityList(Cmd.OB_DOMAIN_NAME_ENTITY)
|
|
64541
65081
|
elif myarg == 'includetrashed':
|
|
@@ -71667,8 +72207,8 @@ def _mapMessageQueryDates(parameters):
|
|
|
71667
72207
|
if not mg:
|
|
71668
72208
|
break
|
|
71669
72209
|
try:
|
|
71670
|
-
dt =
|
|
71671
|
-
query = query[:mg.start(2)]+str(
|
|
72210
|
+
dt = arrow.Arrow(int(mg.groups()[1]), int(mg.groups()[2]), int(mg.groups()[3]), tzinfo=GC.Values[GC.TIMEZONE])
|
|
72211
|
+
query = query[:mg.start(2)]+str(dt.int_timestamp)+query[mg.end(4):]
|
|
71672
72212
|
except ValueError:
|
|
71673
72213
|
pass
|
|
71674
72214
|
pos = mg.end()
|
|
@@ -72849,9 +73389,9 @@ def printShowMessagesThreads(users, entityType):
|
|
|
72849
73389
|
if pLoc > 0:
|
|
72850
73390
|
dateTimeValue = dateTimeValue[:pLoc]
|
|
72851
73391
|
try:
|
|
72852
|
-
dateTimeValue =
|
|
73392
|
+
dateTimeValue = arrow.Arrow.strptime(dateTimeValue, RFC2822_TIME_FORMAT)
|
|
72853
73393
|
if dateHeaderConvertTimezone:
|
|
72854
|
-
dateTimeValue = dateTimeValue.
|
|
73394
|
+
dateTimeValue = dateTimeValue.to(GC.Values[GC.TIMEZONE])
|
|
72855
73395
|
return dateTimeValue.strftime(dateHeaderFormat)
|
|
72856
73396
|
except ValueError:
|
|
72857
73397
|
return headerValue
|
|
@@ -73508,14 +74048,58 @@ def printShowMessages(users):
|
|
|
73508
74048
|
def printShowThreads(users):
|
|
73509
74049
|
printShowMessagesThreads(users, Ent.THREAD)
|
|
73510
74050
|
|
|
74051
|
+
def sendCreateDelegateNotification(user, delegate, basenotify, i=0, count=0, msgFrom=None):
|
|
74052
|
+
# Substitute for #user#, #delegate#
|
|
74053
|
+
def _substituteForPattern(field, pattern, value):
|
|
74054
|
+
if field.find('#') == -1:
|
|
74055
|
+
return field
|
|
74056
|
+
return field.replace(pattern, value)
|
|
74057
|
+
|
|
74058
|
+
def _makeSubstitutions(field):
|
|
74059
|
+
notify[field] = _substituteForPattern(notify[field], '#user#', user)
|
|
74060
|
+
notify[field] = _substituteForPattern(notify[field], '#delegate#', delegate)
|
|
74061
|
+
|
|
74062
|
+
notify = basenotify.copy()
|
|
74063
|
+
if not notify['subject']:
|
|
74064
|
+
notify['subject'] = Msg.CREATE_DELEGATE_NOTIFY_SUBJECT
|
|
74065
|
+
_makeSubstitutions('subject')
|
|
74066
|
+
if not notify['message']:
|
|
74067
|
+
notify['message'] = Msg.CREATE_DELEGATE_NOTIFY_MESSAGE
|
|
74068
|
+
elif notify['html']:
|
|
74069
|
+
notify['message'] = notify['message'].replace('\r', '').replace('\\n', '<br/>')
|
|
74070
|
+
else:
|
|
74071
|
+
notify['message'] = notify['message'].replace('\r', '').replace('\\n', '\n')
|
|
74072
|
+
_makeSubstitutions('message')
|
|
74073
|
+
if 'from' in notify:
|
|
74074
|
+
msgFrom = notify['from']
|
|
74075
|
+
msgReplyTo = notify.get('replyto', None)
|
|
74076
|
+
mailBox = notify.get('mailbox', None)
|
|
74077
|
+
send_email(notify['subject'], notify['message'], delegate, i, count,
|
|
74078
|
+
msgFrom=msgFrom, msgReplyTo=msgReplyTo, html=notify['html'], charset=notify['charset'], mailBox=mailBox)
|
|
74079
|
+
|
|
73511
74080
|
# gam <UserTypeEntity> create delegate|delegates [convertalias] <UserEntity>
|
|
74081
|
+
# [notify [<Boolean>]
|
|
74082
|
+
# [subject <String>]
|
|
74083
|
+
# [from <EmailAaddress>] [mailbox <EmailAddress>]
|
|
74084
|
+
# [replyto <EmailAaddress>]
|
|
74085
|
+
# [<NotifyMessageContent>] [html [<Boolean>]]
|
|
74086
|
+
# ]
|
|
73512
74087
|
# gam <UserTypeEntity> delete delegate|delegates [convertalias] <UserEntity>
|
|
73513
74088
|
def processDelegates(users):
|
|
73514
74089
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
73515
|
-
|
|
74090
|
+
createCmd = Act.Get() != Act.DELETE
|
|
73516
74091
|
aliasAllowed = not checkArgumentPresent(['convertalias'])
|
|
73517
74092
|
delegateEntity = getUserObjectEntity(Cmd.OB_USER_ENTITY, Ent.DELEGATE)
|
|
73518
|
-
|
|
74093
|
+
notify = {'notify': False, 'subject': '', 'message': '', 'html': False, 'charset': UTF8}
|
|
74094
|
+
if createCmd:
|
|
74095
|
+
while Cmd.ArgumentsRemaining():
|
|
74096
|
+
myarg = getArgument()
|
|
74097
|
+
if getNotifyArguments(myarg, notify, False):
|
|
74098
|
+
pass
|
|
74099
|
+
else:
|
|
74100
|
+
unknownArgumentExit()
|
|
74101
|
+
else:
|
|
74102
|
+
checkForExtraneousArguments()
|
|
73519
74103
|
i, count, users = getEntityArgument(users)
|
|
73520
74104
|
for user in users:
|
|
73521
74105
|
i += 1
|
|
@@ -73527,25 +74111,37 @@ def processDelegates(users):
|
|
|
73527
74111
|
for delegate in delegates:
|
|
73528
74112
|
j += 1
|
|
73529
74113
|
delegateEmail = convertUIDtoEmailAddress(delegate, cd=cd, emailTypes=['user', 'group'], aliasAllowed=aliasAllowed)
|
|
74114
|
+
kvList = [Ent.USER, user, Ent.DELEGATE, delegateEmail]
|
|
73530
74115
|
try:
|
|
73531
|
-
if
|
|
73532
|
-
callGAPI(gmail.users().settings().delegates(),
|
|
74116
|
+
if createCmd:
|
|
74117
|
+
callGAPI(gmail.users().settings().delegates(), 'create',
|
|
73533
74118
|
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.ALREADY_EXISTS, GAPI.FAILED_PRECONDITION, GAPI.INVALID,
|
|
73534
74119
|
GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
|
73535
74120
|
userId='me', body={'delegateEmail': delegateEmail})
|
|
74121
|
+
entityActionPerformed(kvList, j, jcount)
|
|
74122
|
+
if notify['notify']:
|
|
74123
|
+
Ind.Increment()
|
|
74124
|
+
sendCreateDelegateNotification(user, delegateEmail, notify, j, jcount)
|
|
74125
|
+
Ind.Decrement()
|
|
73536
74126
|
else:
|
|
73537
|
-
callGAPI(gmail.users().settings().delegates(),
|
|
74127
|
+
callGAPI(gmail.users().settings().delegates(), 'delete',
|
|
73538
74128
|
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INVALID_INPUT, GAPI.PERMISSION_DENIED],
|
|
73539
74129
|
userId='me', delegateEmail=delegateEmail)
|
|
73540
|
-
|
|
74130
|
+
entityActionPerformed(kvList, j, jcount)
|
|
73541
74131
|
except (GAPI.alreadyExists, GAPI.failedPrecondition, GAPI.invalid,
|
|
73542
74132
|
GAPI.notFound, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
|
73543
|
-
entityActionFailedWarning(
|
|
74133
|
+
entityActionFailedWarning(kvList, str(e), j, jcount)
|
|
73544
74134
|
except GAPI.serviceNotAvailable:
|
|
73545
74135
|
userGmailServiceNotEnabledWarning(user, i, count)
|
|
73546
74136
|
Ind.Decrement()
|
|
73547
74137
|
|
|
73548
74138
|
# gam <UserTypeEntity> delegate to [convertalias] <UserEntity>
|
|
74139
|
+
# [notify [<Boolean>]
|
|
74140
|
+
# [subject <String>]
|
|
74141
|
+
# [from <EmailAaddress>] [mailbox <EmailAddress>]
|
|
74142
|
+
# [replyto <EmailAaddress>]
|
|
74143
|
+
# [<NotifyMessageContent>] [html [<Boolean>]]
|
|
74144
|
+
# ]
|
|
73549
74145
|
def delegateTo(users):
|
|
73550
74146
|
checkArgumentPresent('to', required=True)
|
|
73551
74147
|
processDelegates(users)
|
|
@@ -77002,7 +77598,7 @@ TASK_QUERY_STATE_MAP = {
|
|
|
77002
77598
|
# [updatedmin <Time>]
|
|
77003
77599
|
# [showcompleted [<Boolean>]] [showdeleted [<Boolean>]] [showhidden [<Boolean>]] [showall]
|
|
77004
77600
|
# [orderby completed|due|updated]
|
|
77005
|
-
# [countsonly
|
|
77601
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
77006
77602
|
def printShowTasks(users):
|
|
77007
77603
|
def _showTaskAndChildren(tasklist, taskId, k, compact):
|
|
77008
77604
|
if taskId in taskParentsProcessed:
|
|
@@ -77069,8 +77665,11 @@ def printShowTasks(users):
|
|
|
77069
77665
|
csvPF.SetTitles(['User', CSVTitle])
|
|
77070
77666
|
else:
|
|
77071
77667
|
FJQC.GetFormatJSONQuoteChar(myarg, False)
|
|
77072
|
-
if csvPF
|
|
77073
|
-
|
|
77668
|
+
if csvPF:
|
|
77669
|
+
if countsOnly:
|
|
77670
|
+
csvPF.SetFormatJSON(False)
|
|
77671
|
+
elif FJQC.formatJSON:
|
|
77672
|
+
csvPF.SetJSONTitles(['User', 'tasklistId', 'id', 'taskId', 'title', 'JSON'])
|
|
77074
77673
|
i, count, users = getEntityArgument(users)
|
|
77075
77674
|
for user in users:
|
|
77076
77675
|
i += 1
|
|
@@ -77269,7 +77868,7 @@ def processTasklists(users):
|
|
|
77269
77868
|
# gam <UserTypeEntity> show tasklists
|
|
77270
77869
|
# [countsonly|formatjson]
|
|
77271
77870
|
# gam <UserTypeEntity> print tasklists [todrive <ToDriveAttribute>*]
|
|
77272
|
-
# [countsonly
|
|
77871
|
+
# [countsonly|(formatjson [quotechar <Character>])]
|
|
77273
77872
|
def printShowTasklists(users):
|
|
77274
77873
|
csvPF = CSVPrintFile(['User', 'id', 'title']) if Act.csvFormat() else None
|
|
77275
77874
|
if csvPF:
|
|
@@ -77287,6 +77886,8 @@ def printShowTasklists(users):
|
|
|
77287
77886
|
csvPF.SetTitles(['User', CSVTitle])
|
|
77288
77887
|
else:
|
|
77289
77888
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
77889
|
+
if countsOnly and csvPF:
|
|
77890
|
+
csvPF.SetFormatJSON(False)
|
|
77290
77891
|
i, count, users = getEntityArgument(users)
|
|
77291
77892
|
for user in users:
|
|
77292
77893
|
i += 1
|
|
@@ -77890,6 +78491,7 @@ MAIN_ADD_CREATE_FUNCTIONS = {
|
|
|
77890
78491
|
Cmd.ARG_VAULTEXPORT: doCreateVaultExport,
|
|
77891
78492
|
Cmd.ARG_VAULTHOLD: doCreateVaultHold,
|
|
77892
78493
|
Cmd.ARG_VAULTMATTER: doCreateVaultMatter,
|
|
78494
|
+
Cmd.ARG_VAULTQUERY: doCreateVaultQuery,
|
|
77893
78495
|
Cmd.ARG_VERIFY: doCreateSiteVerification,
|
|
77894
78496
|
}
|
|
77895
78497
|
|
|
@@ -77947,6 +78549,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|
|
77947
78549
|
{Cmd.ARG_SHAREDDRIVEACLS: doCopySyncSharedDriveACLs,
|
|
77948
78550
|
Cmd.ARG_STORAGEBUCKET: doCopyCloudStorageBucket,
|
|
77949
78551
|
Cmd.ARG_VAULTEXPORT: doCopyVaultExport,
|
|
78552
|
+
Cmd.ARG_VAULTQUERY: doCopyVaultQuery,
|
|
77950
78553
|
}
|
|
77951
78554
|
),
|
|
77952
78555
|
'create':
|
|
@@ -78011,6 +78614,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|
|
78011
78614
|
Cmd.ARG_VAULTEXPORT: doDeleteVaultExport,
|
|
78012
78615
|
Cmd.ARG_VAULTHOLD: doDeleteVaultHold,
|
|
78013
78616
|
Cmd.ARG_VAULTMATTER: doDeleteVaultMatter,
|
|
78617
|
+
Cmd.ARG_VAULTQUERY: doDeleteVaultQuery,
|
|
78014
78618
|
}
|
|
78015
78619
|
),
|
|
78016
78620
|
'download':
|
|
@@ -78138,6 +78742,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|
|
78138
78742
|
Cmd.ARG_CHROMEAPP: doPrintShowChromeApps,
|
|
78139
78743
|
Cmd.ARG_CHROMEAPPDEVICES: doPrintShowChromeAppDevices,
|
|
78140
78744
|
Cmd.ARG_CHROMEAUES: doPrintShowChromeAues,
|
|
78745
|
+
Cmd.ARG_CHROMEDEVICECOUNTS: doPrintShowChromeDeviceCounts,
|
|
78141
78746
|
Cmd.ARG_CHROMEHISTORY: doPrintShowChromeHistory,
|
|
78142
78747
|
Cmd.ARG_CHROMENEEDSATTN: doPrintShowChromeNeedsAttn,
|
|
78143
78748
|
Cmd.ARG_CHROMEPOLICY: doPrintShowChromePolicies,
|
|
@@ -78275,6 +78880,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|
|
78275
78880
|
Cmd.ARG_CHROMEAPP: doPrintShowChromeApps,
|
|
78276
78881
|
Cmd.ARG_CHROMEAPPDEVICES: doPrintShowChromeAppDevices,
|
|
78277
78882
|
Cmd.ARG_CHROMEAUES: doPrintShowChromeAues,
|
|
78883
|
+
Cmd.ARG_CHROMEDEVICECOUNTS: doPrintShowChromeDeviceCounts,
|
|
78278
78884
|
Cmd.ARG_CHROMEHISTORY: doPrintShowChromeHistory,
|
|
78279
78885
|
Cmd.ARG_CHROMENEEDSATTN: doPrintShowChromeNeedsAttn,
|
|
78280
78886
|
Cmd.ARG_CHROMEPOLICY: doPrintShowChromePolicies,
|