|
|
|
|
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
|
|
|
|
__version__ = '7.10.09'
|
|
|
|
|
__version__ = '7.11.01'
|
|
|
|
|
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
|
|
|
|
|
|
|
|
|
#pylint: disable=wrong-import-position
|
|
|
|
|
@@ -634,7 +634,8 @@ def accessErrorMessage(cd, errMsg=None):
|
|
|
|
|
cd = buildGAPIObject(API.DIRECTORY)
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.customers(), 'get',
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customerKey=GC.Values[GC.CUSTOMER_ID], fields='id')
|
|
|
|
|
except (GAPI.badRequest, GAPI.invalidInput):
|
|
|
|
|
return formatKeyValueList('',
|
|
|
|
|
@@ -671,14 +672,16 @@ def accessErrorExitNonDirectory(api, errMsg):
|
|
|
|
|
''))
|
|
|
|
|
|
|
|
|
|
def ClientAPIAccessDeniedExit(errMsg=None):
|
|
|
|
|
stderrErrorMsg(Msg.API_ACCESS_DENIED)
|
|
|
|
|
if errMsg:
|
|
|
|
|
if errMsg is None:
|
|
|
|
|
stderrErrorMsg(Msg.API_ACCESS_DENIED)
|
|
|
|
|
missingScopes = API.getClientScopesSet(GM.Globals[GM.CURRENT_CLIENT_API])-GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]
|
|
|
|
|
if missingScopes:
|
|
|
|
|
writeStderr(Msg.API_CHECK_CLIENT_AUTHORIZATION.format(GM.Globals[GM.OAUTH2_CLIENT_ID],
|
|
|
|
|
','.join(sorted(missingScopes))))
|
|
|
|
|
systemErrorExit(API_ACCESS_DENIED_RC, None)
|
|
|
|
|
else:
|
|
|
|
|
stderrErrorMsg(errMsg)
|
|
|
|
|
missingScopes = API.getClientScopesSet(GM.Globals[GM.CURRENT_CLIENT_API])-GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]
|
|
|
|
|
if missingScopes:
|
|
|
|
|
writeStderr(Msg.API_CHECK_CLIENT_AUTHORIZATION.format(GM.Globals[GM.OAUTH2_CLIENT_ID],
|
|
|
|
|
','.join(sorted(missingScopes))))
|
|
|
|
|
systemErrorExit(API_ACCESS_DENIED_RC, None)
|
|
|
|
|
systemErrorExit(API_ACCESS_DENIED_RC, Msg.REAUTHENTICATION_IS_NEEDED)
|
|
|
|
|
|
|
|
|
|
def SvcAcctAPIAccessDenied():
|
|
|
|
|
_getSvcAcctData()
|
|
|
|
|
@@ -4458,6 +4461,8 @@ def handleOAuthTokenError(e, softErrors, displayError=False, i=0, count=0):
|
|
|
|
|
errMsg.startswith('invalid_request: Invalid impersonation "sub" field')):
|
|
|
|
|
if not GM.Globals[GM.CURRENT_SVCACCT_USER]:
|
|
|
|
|
ClientAPIAccessDeniedExit()
|
|
|
|
|
# 403 Forbidden, API disabled, user not enabled
|
|
|
|
|
# 400 Bad Request, user not defined
|
|
|
|
|
if softErrors:
|
|
|
|
|
entityActionFailedWarning([Ent.USER, GM.Globals[GM.CURRENT_SVCACCT_USER], Ent.USER, None], errMsg, i, count)
|
|
|
|
|
return None
|
|
|
|
|
@@ -4465,6 +4470,7 @@ def handleOAuthTokenError(e, softErrors, displayError=False, i=0, count=0):
|
|
|
|
|
if errMsg in API.OAUTH2_UNAUTHORIZED_ERRORS:
|
|
|
|
|
if not GM.Globals[GM.CURRENT_SVCACCT_USER]:
|
|
|
|
|
ClientAPIAccessDeniedExit()
|
|
|
|
|
# 401 Unauthorized, API disabled, user enabled
|
|
|
|
|
if softErrors:
|
|
|
|
|
if displayError:
|
|
|
|
|
apiOrScopes = API.getAPIName(GM.Globals[GM.CURRENT_SVCACCT_API]) if GM.Globals[GM.CURRENT_SVCACCT_API] else ','.join(sorted(GM.Globals[GM.CURRENT_SVCACCT_API_SCOPES]))
|
|
|
|
|
@@ -6515,7 +6521,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
|
|
printGettingAllEntityItemsForWhom(Ent.TEACHER, removeCourseIdScope(courseId), entityType=Ent.COURSE)
|
|
|
|
|
result = callGAPIpages(courseInfo['croom'].courses().teachers(), 'list', 'teachers',
|
|
|
|
|
pageMessage=getPageMessageForWhom(),
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
|
|
|
courseId=courseId, fields='nextPageToken,teachers/profile/emailAddress',
|
|
|
|
|
pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
|
|
|
|
|
@@ -6528,7 +6535,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
|
|
printGettingAllEntityItemsForWhom(Ent.STUDENT, removeCourseIdScope(courseId), entityType=Ent.COURSE)
|
|
|
|
|
result = callGAPIpages(courseInfo['croom'].courses().students(), 'list', 'students',
|
|
|
|
|
pageMessage=getPageMessageForWhom(),
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
|
|
|
courseId=courseId, fields='nextPageToken,students/profile/emailAddress',
|
|
|
|
|
pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
|
|
|
|
|
@@ -6544,8 +6552,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
|
|
|
|
entityActionNotPerformedWarning([Ent.COURSE, removeCourseIdScope(courseId)], str(e))
|
|
|
|
|
GM.Globals[GM.CLASSROOM_SERVICE_NOT_AVAILABLE] = True
|
|
|
|
|
break
|
|
|
|
|
except (GAPI.forbidden, GAPI.badRequest):
|
|
|
|
|
ClientAPIAccessDeniedExit()
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied, GAPI.badRequest) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
elif entityType == Cmd.ENTITY_CROS:
|
|
|
|
|
buildGAPIObject(API.DIRECTORY)
|
|
|
|
|
result = convertEntityToList(entity)
|
|
|
|
|
@@ -9023,7 +9031,7 @@ class CSVPrintFile():
|
|
|
|
|
normalizeSortHeaders()
|
|
|
|
|
if self.outputTranspose:
|
|
|
|
|
newRows = []
|
|
|
|
|
newTitlesList = [i for i in range(len(self.rows)+1)]
|
|
|
|
|
newTitlesList = list(range(len(self.rows) + 1))
|
|
|
|
|
for title in titlesList:
|
|
|
|
|
i = 0
|
|
|
|
|
newRow = {i: title}
|
|
|
|
|
@@ -16010,8 +16018,9 @@ def doCreateDomainAlias():
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.domainAliases(), 'insert',
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.DUPLICATE, GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.CONFLICT],
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.DUPLICATE, GAPI.INVALID, GAPI.CONFLICT,
|
|
|
|
|
GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
|
|
|
|
|
entityActionPerformed([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']])
|
|
|
|
|
except GAPI.domainNotFound:
|
|
|
|
|
@@ -16020,8 +16029,10 @@ def doCreateDomainAlias():
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']], Msg.DUPLICATE)
|
|
|
|
|
except (GAPI.invalid, GAPI.conflict) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']], str(e))
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
# gam delete domainalias|aliasdomain <DomainAlias>
|
|
|
|
|
def doDeleteDomainAlias():
|
|
|
|
|
@@ -16030,13 +16041,16 @@ def doDeleteDomainAlias():
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.domainAliases(), 'delete',
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], domainAliasName=domainAliasName)
|
|
|
|
|
entityActionPerformed([Ent.DOMAIN_ALIAS, domainAliasName])
|
|
|
|
|
except GAPI.domainAliasNotFound:
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN_ALIAS, domainAliasName], Msg.DOES_NOT_EXIST)
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
DOMAIN_TIME_OBJECTS = {'creationTime'}
|
|
|
|
|
DOMAIN_ALIAS_PRINT_ORDER = ['parentDomainName', 'creationTime', 'verified']
|
|
|
|
|
@@ -16064,14 +16078,17 @@ def doInfoDomainAlias():
|
|
|
|
|
FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
|
|
|
|
|
try:
|
|
|
|
|
result = callGAPI(cd.domainAliases(), 'get',
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], domainAliasName=domainAliasName)
|
|
|
|
|
aliasSkipObjects = DOMAIN_ALIAS_SKIP_OBJECTS
|
|
|
|
|
_showDomainAlias(result, FJQC, aliasSkipObjects)
|
|
|
|
|
except GAPI.domainAliasNotFound:
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN_ALIAS, domainAliasName], Msg.DOES_NOT_EXIST)
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
def _printDomain(domain, csvPF):
|
|
|
|
|
row = {}
|
|
|
|
|
@@ -16107,7 +16124,8 @@ def doPrintShowDomainAliases():
|
|
|
|
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
|
|
|
|
try:
|
|
|
|
|
domainAliases = callGAPIitems(cd.domainAliases(), 'list', 'domainAliases',
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID])
|
|
|
|
|
count = len(domainAliases)
|
|
|
|
|
if showItemCountOnly:
|
|
|
|
|
@@ -16125,8 +16143,10 @@ def doPrintShowDomainAliases():
|
|
|
|
|
csvPF.WriteRowNoFilter({'domainAliasName': domainAlias['domainAliasName'],
|
|
|
|
|
'JSON': json.dumps(cleanJSON(domainAlias, timeObjects=DOMAIN_TIME_OBJECTS),
|
|
|
|
|
ensure_ascii=False, sort_keys=True)})
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
if csvPF:
|
|
|
|
|
csvPF.writeCSVfile('Domain Aliases')
|
|
|
|
|
|
|
|
|
|
@@ -16137,15 +16157,19 @@ def doCreateDomain():
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.domains(), 'insert',
|
|
|
|
|
throwReasons=[GAPI.DUPLICATE, GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.CONFLICT],
|
|
|
|
|
throwReasons=[GAPI.DUPLICATE, GAPI.CONFLICT,
|
|
|
|
|
GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
|
|
|
|
|
entityActionPerformed([Ent.DOMAIN, body['domainName']])
|
|
|
|
|
except GAPI.duplicate:
|
|
|
|
|
entityDuplicateWarning([Ent.DOMAIN, body['domainName']])
|
|
|
|
|
except GAPI.conflict as e:
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN, body['domainName']], str(e))
|
|
|
|
|
except (GAPI.domainNotFound, GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.domainNotFound, GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
# gam update domain <DomainName> primary
|
|
|
|
|
def doUpdateDomain():
|
|
|
|
|
@@ -16162,13 +16186,17 @@ def doUpdateDomain():
|
|
|
|
|
missingArgumentExit('primary')
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.customers(), 'update',
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_INPUT],
|
|
|
|
|
throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.BAD_REQUEST,
|
|
|
|
|
GAPI.RESOURCE_NOT_FOUND, GAPI.INVALID_INPUT,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customerKey=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
|
|
|
|
|
entityActionPerformedMessage([Ent.DOMAIN, domainName], Msg.NOW_THE_PRIMARY_DOMAIN)
|
|
|
|
|
except GAPI.domainNotVerifiedSecondary:
|
|
|
|
|
entityActionFailedWarning([Ent.DOMAIN, domainName], Msg.DOMAIN_NOT_VERIFIED_SECONDARY)
|
|
|
|
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.invalidInput) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.invalidInput) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
# gam delete domain <DomainName>
|
|
|
|
|
def doDeleteDomain():
|
|
|
|
|
@@ -16177,11 +16205,14 @@ def doDeleteDomain():
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cd.domains(), 'delete',
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], domainName=domainName)
|
|
|
|
|
entityActionPerformed([Ent.DOMAIN, domainName])
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
|
|
|
|
|
except (GAPI.badRequest, GAPI.notFound) as e:
|
|
|
|
|
accessErrorExit(cd, str(e))
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
|
|
|
|
|
CUSTOMER_LICENSE_MAP = {
|
|
|
|
|
'accounts:num_users': 'Total Users',
|
|
|
|
|
@@ -16210,7 +16241,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|
|
|
|
while True:
|
|
|
|
|
try:
|
|
|
|
|
result = callGAPI(rep.customerUsageReports(), 'get',
|
|
|
|
|
throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN],
|
|
|
|
|
throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
date=tryDate, customerId=customerInfo['id'],
|
|
|
|
|
fields='warnings,usageReports', parameters=parameters)
|
|
|
|
|
usageReports = numUsersAvailable(result)
|
|
|
|
|
@@ -16228,8 +16259,8 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|
|
|
|
if not tryDate:
|
|
|
|
|
return
|
|
|
|
|
continue
|
|
|
|
|
except GAPI.forbidden:
|
|
|
|
|
return
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit(str(e))
|
|
|
|
|
if not FJQC.formatJSON:
|
|
|
|
|
printKeyValueList([f'User counts as of {tryDate}:'])
|
|
|
|
|
Ind.Increment()
|
|
|
|
|
@@ -46776,6 +46807,13 @@ COURSE_STATE_MAPS = {
|
|
|
|
|
'published': 'PUBLISHED',
|
|
|
|
|
'deleted': 'DELETED',
|
|
|
|
|
},
|
|
|
|
|
Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST: {
|
|
|
|
|
'draft': 'DRAFT',
|
|
|
|
|
'published': 'PUBLISHED',
|
|
|
|
|
},
|
|
|
|
|
Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST: {
|
|
|
|
|
'published': 'PUBLISHED',
|
|
|
|
|
},
|
|
|
|
|
Cmd.OB_COURSE_WORK_STATE_LIST: {
|
|
|
|
|
'draft': 'DRAFT',
|
|
|
|
|
'published': 'PUBLISHED',
|
|
|
|
|
@@ -47681,7 +47719,8 @@ def _getCoursesOwnerInfo(croom, courseIds, useOwnerAccess, addCIIdScope=True):
|
|
|
|
|
if courseId not in coursesInfo:
|
|
|
|
|
try:
|
|
|
|
|
course = callGAPI(croom.courses(), 'get',
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
|
|
|
|
|
GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
|
|
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
|
|
|
id=courseId, fields='name,ownerId')
|
|
|
|
|
if useOwnerAccess:
|
|
|
|
|
@@ -47692,9 +47731,9 @@ def _getCoursesOwnerInfo(croom, courseIds, useOwnerAccess, addCIIdScope=True):
|
|
|
|
|
coursesInfo[ciCourseId] = {'name': course['name'], 'croom': ocroom}
|
|
|
|
|
except GAPI.notFound:
|
|
|
|
|
entityDoesNotExistWarning(Ent.COURSE, courseId)
|
|
|
|
|
except (GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
|
|
|
|
|
except GAPI.serviceNotAvailable as e:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, courseId], str(e))
|
|
|
|
|
except GAPI.forbidden:
|
|
|
|
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
|
|
|
|
ClientAPIAccessDeniedExit()
|
|
|
|
|
return 0, len(coursesInfo), coursesInfo
|
|
|
|
|
|
|
|
|
|
@@ -48923,77 +48962,114 @@ def doPrintCourseParticipants():
|
|
|
|
|
csvPF.SetSortTitles(COURSE_PARTICIPANTS_SORT_TITLES)
|
|
|
|
|
csvPF.writeCSVfile('Course Participants')
|
|
|
|
|
|
|
|
|
|
def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
|
|
|
|
|
def _batchAddItemsToCourse(croom, courseId, i, count, addItems, addType):
|
|
|
|
|
def _addIdToResponse(response, riItem):
|
|
|
|
|
if addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
respId = response.get('id', '')
|
|
|
|
|
elif addType == Ent.COURSE_TOPIC:
|
|
|
|
|
respId = response.get('topicId', '')
|
|
|
|
|
else:
|
|
|
|
|
respId = ''
|
|
|
|
|
if respId:
|
|
|
|
|
return riItem + f'({respId})'
|
|
|
|
|
return riItem
|
|
|
|
|
|
|
|
|
|
_ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
|
|
|
|
|
GAPI.ALREADY_EXISTS: Msg.DUPLICATE,
|
|
|
|
|
GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED}
|
|
|
|
|
def _callbackAddItemsToCourse(request_id, _, exception):
|
|
|
|
|
def _callbackAddItemsToCourse(request_id, response, exception):
|
|
|
|
|
ri = request_id.splitlines()
|
|
|
|
|
if addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
mg = re.match(r"^{'text': '(.+)'}$", ri[RI_ITEM])
|
|
|
|
|
if mg:
|
|
|
|
|
riText = mg.group(1)
|
|
|
|
|
else:
|
|
|
|
|
riText = ''
|
|
|
|
|
if len(riText) > 100:
|
|
|
|
|
riItem = riText[0:100]+'...'
|
|
|
|
|
else:
|
|
|
|
|
riItem = riText
|
|
|
|
|
else:
|
|
|
|
|
riItem = ri[RI_ITEM]
|
|
|
|
|
if exception is None:
|
|
|
|
|
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
riItem = _addIdToResponse(response, riItem)
|
|
|
|
|
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], addType, riItem], int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
else:
|
|
|
|
|
http_status, reason, message = checkGAPIError(exception)
|
|
|
|
|
if (reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}) and ((reason != GAPI.NOT_FOUND) or (ri[RI_ROLE] == Ent.COURSE_ALIAS)):
|
|
|
|
|
if (reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}) and ((reason != GAPI.NOT_FOUND) or (addType == Ent.COURSE_ALIAS)):
|
|
|
|
|
if reason in [GAPI.FORBIDDEN, GAPI.BACKEND_ERROR]:
|
|
|
|
|
errMsg = getPhraseDNEorSNA(ri[RI_ITEM])
|
|
|
|
|
errMsg = getPhraseDNEorSNA(riItem)
|
|
|
|
|
else:
|
|
|
|
|
errMsg = getHTTPError(_ADD_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
|
|
|
|
|
if (reason == GAPI.PERMISSION_DENIED) and (ri[RI_ROLE] in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
|
|
|
|
|
errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} role {Ent.Singular(ri[RI_ROLE])}'
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
if (reason == GAPI.PERMISSION_DENIED) and (addType in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
|
|
|
|
|
errMsg += f' Add external user with: gam user {riItem} create classroominvitation courses {ri[RI_ENTITY]} addType {Ent.Singular(addType)}'
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
return
|
|
|
|
|
waitOnFailure(1, 10, reason, message)
|
|
|
|
|
if addType in {Ent.STUDENT, Ent.TEACHER, Ent.COURSE_TOPIC}:
|
|
|
|
|
rbody = {attribute: riItem}
|
|
|
|
|
elif addType == Ent.COURSE_ALIAS:
|
|
|
|
|
rbody = {attribute: addCourseAliasScope(riItem)}
|
|
|
|
|
else: # addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
rbody = ri[RI_ITEM]
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(service, 'create',
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BACKEND_ERROR,
|
|
|
|
|
GAPI.ALREADY_EXISTS, GAPI.FAILED_PRECONDITION,
|
|
|
|
|
GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
|
|
|
|
|
courseId=addCourseIdScope(ri[RI_ENTITY]),
|
|
|
|
|
body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
|
|
|
|
|
fields='')
|
|
|
|
|
result = callGAPI(service, 'create',
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BACKEND_ERROR,
|
|
|
|
|
GAPI.ALREADY_EXISTS, GAPI.FAILED_PRECONDITION,
|
|
|
|
|
GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
|
|
|
|
|
courseId=addCourseIdScope(ri[RI_ENTITY]), body=rbody, fields=returnFields)
|
|
|
|
|
riItem = _addIdToResponse(result, riItem)
|
|
|
|
|
except (GAPI.notFound, GAPI.backendError, GAPI.forbidden):
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], getPhraseDNEorSNA(ri[RI_ITEM]), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], getPhraseDNEorSNA(riItem), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except GAPI.alreadyExists:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.DUPLICATE, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], Msg.DUPLICATE, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except GAPI.failedPrecondition:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.NOT_ALLOWED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], Msg.NOT_ALLOWED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
|
|
|
|
|
if role == Ent.STUDENT:
|
|
|
|
|
returnFields = ''
|
|
|
|
|
if addType == Ent.STUDENT:
|
|
|
|
|
service = croom.courses().students()
|
|
|
|
|
attribute = 'userId'
|
|
|
|
|
elif role == Ent.TEACHER:
|
|
|
|
|
elif addType == Ent.TEACHER:
|
|
|
|
|
service = croom.courses().teachers()
|
|
|
|
|
attribute = 'userId'
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
elif addType == Ent.COURSE_ALIAS:
|
|
|
|
|
service = croom.courses().aliases()
|
|
|
|
|
attribute = 'alias'
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif addType == Ent.COURSE_TOPIC:
|
|
|
|
|
service = croom.courses().topics()
|
|
|
|
|
attribute = 'name'
|
|
|
|
|
returnFields = 'topicId'
|
|
|
|
|
else: # addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
service = croom.courses().announcements()
|
|
|
|
|
attribute = 'text'
|
|
|
|
|
returnFields = 'id'
|
|
|
|
|
method = getattr(service, 'create')
|
|
|
|
|
Act.Set(Act.ADD)
|
|
|
|
|
jcount = len(addParticipants)
|
|
|
|
|
jcount = len(addItems)
|
|
|
|
|
noScopeCourseId = removeCourseIdScope(courseId)
|
|
|
|
|
entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
|
|
|
|
|
entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, addType, i, count)
|
|
|
|
|
Ind.Increment()
|
|
|
|
|
svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', '')]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
|
|
|
|
svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', returnFields)]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
|
|
|
|
dbatch = croom.new_batch_http_request(callback=_callbackAddItemsToCourse)
|
|
|
|
|
bcount = 0
|
|
|
|
|
j = 0
|
|
|
|
|
for participant in addParticipants:
|
|
|
|
|
for addItem in addItems:
|
|
|
|
|
j += 1
|
|
|
|
|
svcparms = svcargs.copy()
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
svcparms['body'][attribute] = addCourseAliasScope(participant)
|
|
|
|
|
if addType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(addItem)
|
|
|
|
|
elif addType == Ent.COURSE_ALIAS:
|
|
|
|
|
svcparms['body'][attribute] = addCourseAliasScope(addItem)
|
|
|
|
|
cleanItem = removeCourseAliasScope(svcparms['body'][attribute])
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
svcparms['body'][attribute] = cleanItem = participant
|
|
|
|
|
dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
|
|
|
|
|
elif addType == Ent.COURSE_TOPIC:
|
|
|
|
|
svcparms['body'][attribute] = cleanItem = addItem
|
|
|
|
|
else: # addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
svcparms['body'] = cleanItem = addItem
|
|
|
|
|
dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, addType))
|
|
|
|
|
bcount += 1
|
|
|
|
|
if bcount >= GC.Values[GC.BATCH_SIZE]:
|
|
|
|
|
executeBatch(dbatch)
|
|
|
|
|
@@ -49003,74 +49079,82 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
|
|
|
|
|
dbatch.execute()
|
|
|
|
|
Ind.Decrement()
|
|
|
|
|
|
|
|
|
|
def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, role):
|
|
|
|
|
def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeItems, removeType):
|
|
|
|
|
_REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
|
|
|
|
|
GAPI.FORBIDDEN: Msg.FORBIDDEN,
|
|
|
|
|
GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED}
|
|
|
|
|
def _callbackRemoveItemsFromCourse(request_id, _, exception):
|
|
|
|
|
ri = request_id.splitlines()
|
|
|
|
|
riItem = ri[RI_ITEM]
|
|
|
|
|
if exception is None:
|
|
|
|
|
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
else:
|
|
|
|
|
http_status, reason, message = checkGAPIError(exception)
|
|
|
|
|
if reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}:
|
|
|
|
|
if reason == GAPI.NOT_FOUND and ri[RI_ROLE] != Ent.COURSE_ALIAS:
|
|
|
|
|
errMsg = f'{Msg.NOT_A} {Ent.Singular(ri[RI_ROLE])}'
|
|
|
|
|
if reason == GAPI.NOT_FOUND and removeType != Ent.COURSE_ALIAS:
|
|
|
|
|
errMsg = f'{Msg.NOT_A} {Ent.Singular(removeType)}'
|
|
|
|
|
else:
|
|
|
|
|
errMsg = getHTTPError(_REMOVE_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
return
|
|
|
|
|
waitOnFailure(1, 10, reason, message)
|
|
|
|
|
if removeType in {Ent.STUDENT, Ent.TEACHER, Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT}:
|
|
|
|
|
rbody = {attribute: riItem}
|
|
|
|
|
else: # removeType == Ent.COURSE_ALIAS:
|
|
|
|
|
rbody = {attribute: addCourseAliasScope(riItem)}
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(service, 'delete',
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
|
|
|
|
|
GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FAILED_PRECONDITION],
|
|
|
|
|
retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
|
|
|
|
|
courseId=addCourseIdScope(ri[RI_ENTITY]),
|
|
|
|
|
body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
|
|
|
|
|
fields='')
|
|
|
|
|
courseId=addCourseIdScope(ri[RI_ENTITY]), body=rbody, fields='')
|
|
|
|
|
except GAPI.notFound:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.DOES_NOT_EXIST, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.DOES_NOT_EXIST, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except GAPI.forbidden:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except GAPI.permissionDenied:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
except (GAPI.quotaExceeded, GAPI.serviceNotAvailable, GAPI.failedPrecondition) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
|
|
|
|
|
|
|
|
|
if role == Ent.STUDENT:
|
|
|
|
|
if removeType == Ent.STUDENT:
|
|
|
|
|
service = croom.courses().students()
|
|
|
|
|
attribute = 'userId'
|
|
|
|
|
elif role == Ent.TEACHER:
|
|
|
|
|
elif removeType == Ent.TEACHER:
|
|
|
|
|
service = croom.courses().teachers()
|
|
|
|
|
attribute = 'userId'
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
elif removeType == Ent.COURSE_ALIAS:
|
|
|
|
|
service = croom.courses().aliases()
|
|
|
|
|
attribute = 'alias'
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif removeType == Ent.COURSE_TOPIC:
|
|
|
|
|
service = croom.courses().topics()
|
|
|
|
|
attribute = 'id'
|
|
|
|
|
else: # removeType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
service = croom.courses().announcements()
|
|
|
|
|
attribute = 'id'
|
|
|
|
|
method = getattr(service, 'delete')
|
|
|
|
|
Act.Set(Act.REMOVE)
|
|
|
|
|
jcount = len(removeParticipants)
|
|
|
|
|
jcount = len(removeItems)
|
|
|
|
|
noScopeCourseId = removeCourseIdScope(courseId)
|
|
|
|
|
entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
|
|
|
|
|
entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, removeType, i, count)
|
|
|
|
|
Ind.Increment()
|
|
|
|
|
svcargs = dict([('courseId', courseId), ('fields', ''), (attribute, None)]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
|
|
|
|
dbatch = croom.new_batch_http_request(callback=_callbackRemoveItemsFromCourse)
|
|
|
|
|
bcount = 0
|
|
|
|
|
j = 0
|
|
|
|
|
for participant in removeParticipants:
|
|
|
|
|
for removeItem in removeItems:
|
|
|
|
|
j += 1
|
|
|
|
|
svcparms = svcargs.copy()
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
svcparms[attribute] = addCourseAliasScope(participant)
|
|
|
|
|
if removeType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(removeItem)
|
|
|
|
|
elif removeType == Ent.COURSE_ALIAS:
|
|
|
|
|
svcparms[attribute] = addCourseAliasScope(removeItem)
|
|
|
|
|
cleanItem = removeCourseAliasScope(svcparms[attribute])
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
svcparms[attribute] = cleanItem = participant
|
|
|
|
|
dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
|
|
|
|
|
elif removeType == Ent.COURSE_TOPIC:
|
|
|
|
|
svcparms[attribute] = cleanItem = removeItem
|
|
|
|
|
else: # removeType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
svcparms[attribute] = cleanItem = removeItem
|
|
|
|
|
dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, removeType))
|
|
|
|
|
bcount += 1
|
|
|
|
|
if bcount >= GC.Values[GC.BATCH_SIZE]:
|
|
|
|
|
executeBatch(dbatch)
|
|
|
|
|
@@ -49100,9 +49184,28 @@ def _updateCourseOwner(croom, courseId, owner, i, count):
|
|
|
|
|
entityActionPerformedMessage([Ent.COURSE, removeCourseIdScope(courseId), Ent.TEACHER, owner], Msg.ALREADY_WAS_OWNER, i, count)
|
|
|
|
|
Act.Set(action)
|
|
|
|
|
|
|
|
|
|
ADD_REMOVE_PARTICIPANT_TYPES_MAP = {
|
|
|
|
|
def getCourseAnnouncement(createCmd):
|
|
|
|
|
body = {}
|
|
|
|
|
while Cmd.ArgumentsRemaining():
|
|
|
|
|
myarg = getArgument()
|
|
|
|
|
if myarg in SORF_TEXT_ARGUMENTS:
|
|
|
|
|
body['text'] = getStringOrFile(myarg, minLen=1, unescapeCRLF=True)[0]
|
|
|
|
|
elif myarg == 'scheduledtime':
|
|
|
|
|
body['scheduledTime'] = getTimeOrDeltaFromNow()
|
|
|
|
|
elif myarg == 'state':
|
|
|
|
|
body['state'] = getChoice(COURSE_STATE_MAPS[Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST if createCmd else Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST],
|
|
|
|
|
mapChoice=True)
|
|
|
|
|
else:
|
|
|
|
|
unknownArgumentExit()
|
|
|
|
|
if createCmd and 'text' not in body:
|
|
|
|
|
missingArgumentExit('text <String>')
|
|
|
|
|
return body
|
|
|
|
|
|
|
|
|
|
ADD_REMOVE_UPDATE_ITEM_TYPES_MAP = {
|
|
|
|
|
'alias': Ent.COURSE_ALIAS,
|
|
|
|
|
'aliases': Ent.COURSE_ALIAS,
|
|
|
|
|
'announcement': Ent.COURSE_ANNOUNCEMENT,
|
|
|
|
|
'announcements': Ent.COURSE_ANNOUNCEMENT,
|
|
|
|
|
'student': Ent.STUDENT,
|
|
|
|
|
'students': Ent.STUDENT,
|
|
|
|
|
'teacher': Ent.TEACHER,
|
|
|
|
|
@@ -49123,6 +49226,10 @@ PARTICIPANT_EN_MAP = {
|
|
|
|
|
|
|
|
|
|
# gam courses <CourseEntity> create alias <CourseAliasEntity>
|
|
|
|
|
# gam course <CourseID> create alias <CourseAlias>
|
|
|
|
|
# gam courses <CourseEntity> create announcement
|
|
|
|
|
# <CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
|
|
|
|
|
# gam course <CourseID> create announcement
|
|
|
|
|
# <CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
|
|
|
|
|
# gam courses <CourseEntity> create topic <CourseTopicEntity>
|
|
|
|
|
# gam course <CourseID> create topic <CourseTopic>
|
|
|
|
|
# gam courses <CourseEntity> create students <UserTypeEntity>
|
|
|
|
|
@@ -49131,35 +49238,39 @@ PARTICIPANT_EN_MAP = {
|
|
|
|
|
# gam course <CourseID> create teacher [makefirstteacherowner] <EmailAddress>
|
|
|
|
|
def doCourseAddItems(courseIdList, getEntityListArg):
|
|
|
|
|
croom = buildGAPIObject(API.CLASSROOM)
|
|
|
|
|
role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
|
|
|
|
|
if role == Ent.TEACHER:
|
|
|
|
|
addType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
|
|
|
|
|
if addType == Ent.TEACHER:
|
|
|
|
|
makeFirstTeacherOwner = checkArgumentPresent(['makefirstteacherowner'])
|
|
|
|
|
else:
|
|
|
|
|
makeFirstTeacherOwner = False
|
|
|
|
|
if not getEntityListArg:
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
if addType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
addItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
elif addType == Ent.COURSE_ALIAS:
|
|
|
|
|
addItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif addType == Ent.COURSE_TOPIC:
|
|
|
|
|
addItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC)
|
|
|
|
|
else: # addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
addItems = [getCourseAnnouncement(True)]
|
|
|
|
|
courseParticipantLists = None
|
|
|
|
|
else:
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
if addType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
_, addItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
|
|
|
|
|
typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]},
|
|
|
|
|
typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[addType]},
|
|
|
|
|
isSuspended=False, isArchived=False)
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
elif addType == Ent.COURSE_ALIAS:
|
|
|
|
|
addItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif addType == Ent.COURSE_TOPIC:
|
|
|
|
|
addItems = getEntityList(Cmd.OB_COURSE_TOPIC_ENTITY, shlexSplit=True)
|
|
|
|
|
else: # addType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
addItems = getCourseAnnouncement(True)
|
|
|
|
|
courseParticipantLists = addItems if isinstance(addItems, dict) else None
|
|
|
|
|
if courseParticipantLists is None:
|
|
|
|
|
firstTeacher = None
|
|
|
|
|
if makeFirstTeacherOwner and addItems:
|
|
|
|
|
firstTeacher = normalizeEmailAddressOrUID(addItems[0])
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, role == Ent.COURSE_TOPIC,
|
|
|
|
|
i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, addType in {Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT},
|
|
|
|
|
addCIIdScope=courseParticipantLists is None)
|
|
|
|
|
for courseId, courseInfo in coursesInfo.items():
|
|
|
|
|
i += 1
|
|
|
|
|
@@ -49169,43 +49280,51 @@ def doCourseAddItems(courseIdList, getEntityListArg):
|
|
|
|
|
if makeFirstTeacherOwner and addItems:
|
|
|
|
|
firstTeacher = normalizeEmailAddressOrUID(addItems[0])
|
|
|
|
|
courseId = addCourseIdScope(courseId)
|
|
|
|
|
_batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, role)
|
|
|
|
|
_batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, addType)
|
|
|
|
|
if makeFirstTeacherOwner and firstTeacher:
|
|
|
|
|
_updateCourseOwner(courseInfo['croom'], courseId, firstTeacher, i, count)
|
|
|
|
|
|
|
|
|
|
# gam courses <CourseEntity> remove alias <CourseAliasEntity>
|
|
|
|
|
# gam course <CourseID> remove alias <CourseAlias>
|
|
|
|
|
# gam courses <CourseEntity> remove announcement <CourseAnnouncementIDEntity>
|
|
|
|
|
# gam course <CourseID> remove announcement <CourseAnnouncementID>
|
|
|
|
|
# gam courses <CourseEntity> remove topic <CourseTopicIDEntity>
|
|
|
|
|
# gam course <CourseID> remove topic <CourseTopicID>
|
|
|
|
|
# gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity>
|
|
|
|
|
# gam course <CourseID> remove teacher|student [owneracccess] <EmailAddress>
|
|
|
|
|
def doCourseRemoveItems(courseIdList, getEntityListArg):
|
|
|
|
|
croom = buildGAPIObject(API.CLASSROOM)
|
|
|
|
|
role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
|
|
|
|
|
removeType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
|
|
|
|
|
if not getEntityListArg:
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
if removeType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
useOwnerAccess = GC.Values[GC.USE_COURSE_OWNER_ACCESS]
|
|
|
|
|
if checkArgumentPresent(OWNER_ACCESS_OPTIONS):
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
removeItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
elif removeType == Ent.COURSE_ALIAS:
|
|
|
|
|
useOwnerAccess = False
|
|
|
|
|
removeItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif removeType == Ent.COURSE_TOPIC:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
removeItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
|
|
|
|
|
else: # removeType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
removeItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
|
|
|
|
|
courseParticipantLists = None
|
|
|
|
|
else:
|
|
|
|
|
if role in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
if removeType in {Ent.STUDENT, Ent.TEACHER}:
|
|
|
|
|
useOwnerAccess = checkArgumentPresent(OWNER_ACCESS_OPTIONS)
|
|
|
|
|
_, removeItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
|
|
|
|
|
typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]})
|
|
|
|
|
elif role == Ent.COURSE_ALIAS:
|
|
|
|
|
typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[removeType]})
|
|
|
|
|
elif removeType == Ent.COURSE_ALIAS:
|
|
|
|
|
useOwnerAccess = False
|
|
|
|
|
removeItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
|
|
|
|
|
else: # role == Ent.COURSE_TOPIC:
|
|
|
|
|
elif removeType == Ent.COURSE_TOPIC:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
removeItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
|
|
|
|
|
else: # removeType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
removeItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
|
|
|
|
|
courseParticipantLists = removeItems if isinstance(removeItems, dict) else None
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
|
|
|
|
|
@@ -49215,7 +49334,72 @@ def doCourseRemoveItems(courseIdList, getEntityListArg):
|
|
|
|
|
if courseParticipantLists:
|
|
|
|
|
removeItems = courseParticipantLists[courseId]
|
|
|
|
|
courseId = addCourseIdScope(courseId)
|
|
|
|
|
_batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, role)
|
|
|
|
|
_batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, removeType)
|
|
|
|
|
|
|
|
|
|
# gam courses <CourseEntity> update announcement <CourseAnnouncemntIDEntity>
|
|
|
|
|
# [<CourseAnnouncementContent>] [scheduledtime <Time>] [state published]
|
|
|
|
|
# gam course <CourseID> update announcement <CourseAnnouncementID>
|
|
|
|
|
# [<CourseAnnouncementContent>] [scheduledtime <Time>] [state published]
|
|
|
|
|
# gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
|
|
|
|
|
# gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
|
|
|
|
|
def doCourseUpdateItems(courseIdList, getEntityListArg):
|
|
|
|
|
croom = buildGAPIObject(API.CLASSROOM)
|
|
|
|
|
updateType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
|
|
|
|
|
if not getEntityListArg:
|
|
|
|
|
if updateType == Ent.COURSE_TOPIC:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
updateItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
|
|
|
|
|
body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
|
|
|
|
|
else: # updateType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
updateItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
|
|
|
|
|
body = getCourseAnnouncement(False)
|
|
|
|
|
courseItemLists = None
|
|
|
|
|
else:
|
|
|
|
|
if updateType == Ent.COURSE_TOPIC:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
updateItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
|
|
|
|
|
body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
|
|
|
|
|
else: # updateType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
useOwnerAccess = True
|
|
|
|
|
updateItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
|
|
|
|
|
body = getCourseAnnouncement(False)
|
|
|
|
|
courseItemLists = updateItems if isinstance(updateItems, dict) else None
|
|
|
|
|
checkForExtraneousArguments()
|
|
|
|
|
i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
|
|
|
|
|
addCIIdScope=courseItemLists is None)
|
|
|
|
|
for courseId, courseInfo in coursesInfo.items():
|
|
|
|
|
i += 1
|
|
|
|
|
if courseItemLists:
|
|
|
|
|
updateItems = courseItemLists[courseId]
|
|
|
|
|
courseId = addCourseIdScope(courseId)
|
|
|
|
|
jcount = len(updateItems)
|
|
|
|
|
noScopeCourseId = removeCourseIdScope(courseId)
|
|
|
|
|
if updateType == Ent.COURSE_TOPIC:
|
|
|
|
|
service = courseInfo['croom'].courses().topics()
|
|
|
|
|
else: # updateType == Ent.COURSE_ANNOUNCEMENT:
|
|
|
|
|
service = courseInfo['croom'].courses().announcements()
|
|
|
|
|
entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, updateType, i, count)
|
|
|
|
|
Ind.Increment()
|
|
|
|
|
j = 0
|
|
|
|
|
for updateItem in updateItems:
|
|
|
|
|
j += 1
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(service, 'patch',
|
|
|
|
|
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
|
|
|
|
|
GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
courseId=addCourseIdScope(courseId), id=updateItem, updateMask=','.join(body.keys()), body=body, fields='')
|
|
|
|
|
entityActionPerformed([Ent.COURSE, courseId, updateType, updateItem], j, jcount)
|
|
|
|
|
except GAPI.notFound:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.DOES_NOT_EXIST, j, jcount)
|
|
|
|
|
except GAPI.forbidden:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.FORBIDDEN, j, jcount)
|
|
|
|
|
except GAPI.permissionDenied:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.PERMISSION_DENIED, j, jcount)
|
|
|
|
|
except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], str(e), j, jcount)
|
|
|
|
|
Ind.Decrement()
|
|
|
|
|
|
|
|
|
|
# gam courses <CourseEntity> clear teachers|students
|
|
|
|
|
# gam course <CourseID> clear teacher|student
|
|
|
|
|
@@ -65870,12 +66054,14 @@ SHAREDDRIVE_ACL_ROLES_MAP = {
|
|
|
|
|
'writer': 'writer',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SHOWWEBVIEWLINK_CHOICES = {'text', 'hyperlink'}
|
|
|
|
|
|
|
|
|
|
# gam <UserTypeEntity> print shareddrives [todrive <ToDriveAttribute>*]
|
|
|
|
|
# [asadmin [shareddriveadminquery|query <QuerySharedDrive>]]
|
|
|
|
|
# [matchname <REMatchPattern>] [orgunit|org|ou <OrgUnitPath>]
|
|
|
|
|
# (role|roles <SharedDriveACLRoleList>)*
|
|
|
|
|
# [fields <SharedDriveFieldNameList>] [noorgunits [<Boolean>]]
|
|
|
|
|
# [showwebviewlink]
|
|
|
|
|
# [showwebviewlink [text|hyperlink]]
|
|
|
|
|
# [guiroles [<Boolean>]] [formatjson [quotechar <Character>]]
|
|
|
|
|
# [showitemcountonly]
|
|
|
|
|
# gam <UserTypeEntity> show shareddrives
|
|
|
|
|
@@ -65883,7 +66069,7 @@ SHAREDDRIVE_ACL_ROLES_MAP = {
|
|
|
|
|
# [matchname <REMatchPattrn>] [orgunit|org|ou <OrgUnitPath>]
|
|
|
|
|
# (role|roles <SharedDriveACLRoleLIst>)*
|
|
|
|
|
# [fields <SharedDriveFieldNameList>] [noorgunits [<Boolean>]]
|
|
|
|
|
# [showwebviewlink]
|
|
|
|
|
# [showwebviewlink [text|hyperlink]]
|
|
|
|
|
# [guiroles [<Boolean>]] [formatjson]
|
|
|
|
|
# [showitemcountonly]
|
|
|
|
|
def printShowSharedDrives(users, useDomainAdminAccess=False):
|
|
|
|
|
@@ -65893,7 +66079,10 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
|
|
|
|
|
if td_ouid:
|
|
|
|
|
shareddrive['orgUnit'] = orgUnitIdToPathMap.get(f'id:{td_ouid}', UNKNOWN)
|
|
|
|
|
if showWebViewLink:
|
|
|
|
|
shareddrive['webViewLink'] = 'https://drive.google.com/drive/folders/'+shareddrive['id']
|
|
|
|
|
if showWebViewLink == 'text':
|
|
|
|
|
shareddrive['webViewLink'] = 'https://drive.google.com/drive/folders/'+shareddrive['id']
|
|
|
|
|
else:
|
|
|
|
|
shareddrive['webViewLink'] = '=HYPERLINK("https://drive.google.com/drive/folders/'+shareddrive['id']+'", "'+shareddrive['name']+'")'
|
|
|
|
|
if not showFields:
|
|
|
|
|
return shareddrive
|
|
|
|
|
sshareddrive = {}
|
|
|
|
|
@@ -65912,7 +66101,7 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
|
|
|
|
|
showOrgUnitPaths = True
|
|
|
|
|
orgUnitIdToPathMap = None
|
|
|
|
|
guiRoles = showItemCountOnly = False
|
|
|
|
|
showWebViewLink = False
|
|
|
|
|
showWebViewLink = ''
|
|
|
|
|
while Cmd.ArgumentsRemaining():
|
|
|
|
|
myarg = getArgument()
|
|
|
|
|
if csvPF and myarg == 'todrive':
|
|
|
|
|
@@ -65943,7 +66132,7 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
|
|
|
|
|
elif myarg == 'guiroles':
|
|
|
|
|
guiRoles = getBoolean()
|
|
|
|
|
elif myarg == 'showwebviewlink':
|
|
|
|
|
showWebViewLink = True
|
|
|
|
|
showWebViewLink = getChoice(SHOWWEBVIEWLINK_CHOICES)
|
|
|
|
|
elif myarg == 'showitemcountonly':
|
|
|
|
|
showItemCountOnly = True
|
|
|
|
|
showOrgUnitPaths = False
|
|
|
|
|
@@ -77160,6 +77349,7 @@ COURSE_SUBCOMMANDS = {
|
|
|
|
|
'add': (Act.ADD, doCourseAddItems),
|
|
|
|
|
'clear': (Act.REMOVE, doCourseClearParticipants),
|
|
|
|
|
'remove': (Act.REMOVE, doCourseRemoveItems),
|
|
|
|
|
'update': (Act.UPDATE, doCourseUpdateItems),
|
|
|
|
|
'sync': (Act.SYNC, doCourseSyncParticipants),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|