Multiple updates

This commit is contained in:
Ross Scroggs
2024-01-31 10:41:36 -08:00
parent a404af0582
commit 25cdf2e544
4 changed files with 169 additions and 57 deletions

View File

@@ -2,6 +2,43 @@
Merged GAM-Team version
6.67.31
Updated `gam <UserTypeEntity> claim|transfer ownership <DriveFileEntity>` to properly
handle the case where `<DriveFileEntity>` referencess a Drive shortcut.
6.67.30
Fixed bug where the `fullpath` option in various commands was not converting the generic shared drive name `Drive` to the drive's actual name.
6.67.29
Added optional argument `owneraccess` to `gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity` and
`gam course <CourseID> remove teacher|student [owneraccess] <EmailAddress>` in order to test a possible API change.
Updated code to avoid a trap when `gam config auto_batch_min 1 csv file.csv gam ...` was entered.
The `config auto_batch_min 1` is not appropriate in this context and will be ignored.
6.67.28
Improved handling of `Bad Request` error in `gam <UserTypeEntity> collect orphans`.
6.67.27
Updated `gam <UserTypeEntity> collect orphans` to handle the following error:
```
ERROR: 400: badRequest - Bad Request
```
6.67.26
Fixed bug in `gam print vaultexports ... formatjson` that caused a trap.
6.67.25
Added option `owneraccess` to `gam info courses <CourseEntity>` and `gam info course <CourseID>` in order
to test a possible API change.
6.67.24
Fixed bug that caused HTML password notification email messages to be displayed in raw form.

View File

@@ -246,6 +246,7 @@ FILENAME_SAFE_CHARS = ALPHANUMERIC_CHARS+'-_.() '
CHAT_MESSAGEID_CHARS = string.ascii_lowercase+string.digits+'-'
ADMIN_ACCESS_OPTIONS = {'adminaccess', 'asadmin'}
OWNER_ACCESS_OPTIONS = {'owneraccess', 'asowner'}
# Python 3 values
DEFAULT_CSV_READ_MODE = 'r'
@@ -9503,6 +9504,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
GM.Globals[GM.PRINT_CROS_OUS_AND_CHILDREN] = printCrosOUsAndChildren
GM.Globals[GM.SAVED_STDOUT] = None
GM.Globals[GM.SYSEXITRC] = 0
GM.Globals[GM.PARSER] = None
if mpQueueCSVFile:
GM.Globals[GM.CSVFILE][GM.REDIRECT_QUEUE] = mpQueueCSVFile
if mpQueueStdout:
@@ -29869,7 +29871,7 @@ def doCreateGroup(ciGroupsAPI=False):
except GAPI.notFound:
entityActionFailedWarning([entityType, groupEmail], Msg.DOES_NOT_EXIST)
except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
GAPI.backendError, GAPI.invalid, GAPI.invalidAttributeValue, GAPI.invalidInput, GAPI.invalidArgument,
GAPI.backendError, GAPI.invalid, GAPI.invalidAttributeValue, GAPI.invalidInput, GAPI.invalidArgument, GAPI.failedPrecondition,
GAPI.badRequest, GAPI.permissionDenied, GAPI.systemError, GAPI.serviceLimit, GAPI.serviceNotAvailable, GAPI.authError) as e:
entityActionFailedWarning([entityType, groupEmail], str(e))
except GAPI.required:
@@ -39731,7 +39733,7 @@ def doPrintShowVaultMatters():
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
csvPF.WriteRowNoFilter({'matterId': matter['id'], 'name': matter['name'],
csvPF.WriteRowNoFilter({'matterId': matter['matterId'], 'name': matter['name'],
'JSON': json.dumps(cleanJSON(matter), ensure_ascii=False, sort_keys=True)})
if csvPF:
csvPF.writeCSVfile('Vault Matters')
@@ -44299,7 +44301,7 @@ class CourseAttributes():
else:
return True
if self.members != 'none':
_, self.teachers, self.students = _getCourseAliasesMembers(self.croom, self.courseId, {'members': self.members},
_, self.teachers, self.students = _getCourseAliasesMembers(self.croom, self.croom, self.courseId, {'members': self.members},
'nextPageToken,teachers(profile(emailAddress,id))',
'nextPageToken,students(profile(emailAddress))')
if self.announcementStates:
@@ -44930,7 +44932,7 @@ def _convertCourseUserIdToEmail(croom, userId, emails, entityValueList, i, count
emails[userId] = userEmail
return userEmail
def _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFields, studentsFields, showGettings=False, i=0, count=0):
def _getCourseAliasesMembers(croom, ocroom, courseId, courseShowProperties, teachersFields, studentsFields, showGettings=False, i=0, count=0):
aliases = []
teachers = []
students = []
@@ -44956,7 +44958,7 @@ def _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFiel
printGettingEntityItemForWhom(Ent.TEACHER, formatKeyValueList('', [Ent.Singular(Ent.COURSE), courseId], currentCount(i, count)))
pageMessage = getPageMessage()
try:
teachers = callGAPIpages(croom.courses().teachers(), 'list', 'teachers',
teachers = callGAPIpages(ocroom.courses().teachers(), 'list', 'teachers',
pageMessage=pageMessage,
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
@@ -44970,7 +44972,7 @@ def _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFiel
printGettingEntityItemForWhom(Ent.STUDENT, formatKeyValueList('', [Ent.Singular(Ent.COURSE), courseId], currentCount(i, count)))
pageMessage = getPageMessage()
try:
students = callGAPIpages(croom.courses().students(), 'list', 'students',
students = callGAPIpages(ocroom.courses().students(), 'list', 'students',
pageMessage=pageMessage,
throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
@@ -44981,18 +44983,23 @@ def _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFiel
ClientAPIAccessDeniedExit()
return (aliases, teachers, students)
def _doInfoCourses(entityList):
def _doInfoCourses(courseIdList):
croom = buildGAPIObject(API.CLASSROOM)
courseShowProperties = _initCourseShowProperties()
courseShowProperties['ownerEmail'] = True
ownerEmails = {}
useOwnerAccess = False
FJQC = FormatJSONQuoteChar()
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if _getCourseShowProperties(myarg, courseShowProperties):
pass
elif myarg in OWNER_ACCESS_OPTIONS:
useOwnerAccess = True
else:
FJQC.GetFormatJSON(myarg)
coursesInfo = {}
_getCoursesOwnerInfo(croom, courseIdList, coursesInfo, not useOwnerAccess)
fields = _setCourseFields(courseShowProperties, False)
if courseShowProperties['members'] != 'none':
if courseShowProperties['countsOnly']:
@@ -45004,10 +45011,13 @@ def _doInfoCourses(entityList):
else:
teachersFields = studentsFields = None
i = 0
count = len(entityList)
for course in entityList:
count = len(courseIdList)
for courseId in courseIdList:
i += 1
courseId = addCourseIdScope(course)
courseId = addCourseIdScope(courseId)
courseInfo = coursesInfo[courseId]
if not courseInfo:
continue
try:
course = callGAPI(croom.courses(), 'get',
throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
@@ -45016,7 +45026,7 @@ def _doInfoCourses(entityList):
if courseShowProperties['ownerEmail']:
course['ownerEmail'] = _convertCourseUserIdToEmail(croom, course['ownerId'], ownerEmails,
[Ent.COURSE, course['id'], Ent.OWNER_ID, course['ownerId']], i, count)
aliases, teachers, students = _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFields, studentsFields)
aliases, teachers, students = _getCourseAliasesMembers(croom, courseInfo['croom'], courseId, courseShowProperties, teachersFields, studentsFields)
if FJQC.formatJSON:
if courseShowProperties['aliases']:
course.update({'aliases': list(aliases)})
@@ -45308,7 +45318,7 @@ def doPrintCourses():
if showItemCountOnly:
itemCount += 1
continue
aliases, teachers, students = _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFields, studentsFields, True, i, count)
aliases, teachers, students = _getCourseAliasesMembers(croom, croom, courseId, courseShowProperties, teachersFields, studentsFields, True, i, count)
if courseShowProperties['aliases']:
if not courseShowProperties['aliasesInColumns']:
course['Aliases'] = delimiter.join([removeCourseAliasScope(alias['alias']) for alias in aliases])
@@ -46051,7 +46061,7 @@ def doPrintCourseParticipants():
for course in coursesInfo:
i += 1
courseId = course['id']
_, teachers, students = _getCourseAliasesMembers(croom, courseId, courseShowProperties, teachersFields, studentsFields, True, i, count)
_, teachers, students = _getCourseAliasesMembers(croom, croom, courseId, courseShowProperties, teachersFields, studentsFields, True, i, count)
if showItemCountOnly:
if courseShowProperties['members'] != 'students':
itemCount += len(teachers)
@@ -46362,31 +46372,37 @@ def doCourseAddItems(courseIdList, getEntityListArg):
# gam course <CourseID> remove alias <CourseAlias>
# gam courses <CourseEntity> remove topic <CourseTopicIDEntity>
# gam course <CourseID> remove topic <CourseTopicID>
# gam courses <CourseEntity> remove teachers|students <UserTypeEntity>
# gam course <CourseID> remove teacher|student <EmailAddress>
# 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)
coursesInfo = {}
if not getEntityListArg:
if role in {Ent.STUDENT, Ent.TEACHER}:
useOwnerAccess = checkArgumentPresent(OWNER_ACCESS_OPTIONS)
removeItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
elif role == Ent.COURSE_ALIAS:
useOwnerAccess = False
removeItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
else: # role == Ent.COURSE_TOPIC:
useOwnerAccess = True
removeItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
courseParticipantLists = None
else:
if role 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:
useOwnerAccess = False
removeItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
else: # role == Ent.COURSE_TOPIC:
useOwnerAccess = True
removeItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
courseParticipantLists = removeItems if isinstance(removeItems, dict) else None
checkForExtraneousArguments()
_getCoursesOwnerInfo(croom, courseIdList, coursesInfo, role != Ent.COURSE_TOPIC)
_getCoursesOwnerInfo(croom, courseIdList, coursesInfo, not useOwnerAccess)
i = 0
count = len(courseIdList)
for courseId in courseIdList:
@@ -52378,6 +52394,8 @@ def extendFileTreeParents(drive, fileTree, fields):
if not result.get('driveId'):
result['parents'] = [ORPHANS] if result.get('ownedByMe', False) else [SHARED_WITHME]
else:
if result['name'] == TEAM_DRIVE:
result['name'] = _getSharedDriveNameFromId(drive, result['driveId'])
result['parents'] = [SHARED_DRIVES] if 'sharedWithMeTime' not in f_file else [SHARED_WITHME]
fileTree[fileId]['info'] = result
fileTree[fileId]['info']['noDisplay'] = True
@@ -58133,12 +58151,13 @@ def collectOrphans(users):
try:
callGAPI(drive.files(), 'update',
bailOnInternalError=True,
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INTERNAL_ERROR, GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND,
GAPI.INTERNAL_ERROR, GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
retryReasons=[GAPI.FILE_NOT_FOUND],
fileId=fileId, body={}, addParents=newParentId, fields='')
entityModifierNewValueItemValueListActionPerformed([Ent.USER, user, fileType, fileName],
Act.MODIFIER_INTO, None, [Ent.DRIVE_FOLDER, trgtUserFolderName], j, jcount)
except (GAPI.fileNotFound, GAPI.internalError, GAPI.insufficientParentPermissions,) as e:
except (GAPI.badRequest, GAPI.fileNotFound, GAPI.internalError, GAPI.insufficientParentPermissions,) as e:
entityActionFailedWarning([Ent.USER, user, fileType, fileName], str(e), j, jcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
@@ -59184,7 +59203,7 @@ def transferOwnership(users):
try:
fileEntryInfo = callGAPI(drive.files(), 'get',
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
fileId=fileId, fields='id,name,parents,mimeType,ownedByMe,trashed')
fileId=fileId, fields='id,name,parents,mimeType,ownedByMe,trashed,shortcutDetails')
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.NOT_FOUND, j, jcount)
continue
@@ -59205,6 +59224,8 @@ def transferOwnership(users):
if changeParents:
filesToTransfer[fileId]['addParents'] = addParents
filesToTransfer[fileId]['removeParents'] = ','.join(fileEntryInfo.get('parents', []))
if fileEntryInfo['mimeType'] == MIMETYPE_GA_SHORTCUT and entityType != Ent.DRIVE_SHORTCUT:
filesToTransfer[fileId]['shortcutDetails'] = fileEntryInfo['shortcutDetails']
if fileEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER and not noRecursion:
if buildTree:
_identifyFilesToTransfer(fileEntry)
@@ -59228,7 +59249,7 @@ def transferOwnership(users):
fileDesc = f'{fileInfo["name"]} ({xferFileId})'
kvList = [Ent.USER, user, entityType, fileDesc]
try:
if entityType != Ent.DRIVE_SHORTCUT:
if entityType not in {Ent.DRIVE_SHORTCUT, Ent.DRIVE_FILE_SHORTCUT, Ent.DRIVE_FOLDER_SHORTCUT}:
if changeParents:
removeParents = fileInfo.get('removeParents', '')
if removeParents:
@@ -59244,7 +59265,16 @@ def transferOwnership(users):
fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='')
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount)
else:
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount)
if changeParents and entityType != Ent.DRIVE_SHORTCUT:
callGAPI(drive.files(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS,
fileId=xferFileId, supportsAllDrives=True)
action = Act.Get()
Act.Set(Act.DELETE_SHORTCUT)
entityActionPerformed(kvList, k, kcount)
Act.Set(action)
else:
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount)
except GAPI.permissionNotFound:
# this might happen if target user isn't explicitly in ACL (i.e. shared with anyone)
try:
@@ -59279,11 +59309,11 @@ def transferOwnership(users):
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
break
if entityType != Ent.DRIVE_SHORTCUT:
if changeParents and 'addParents' in fileInfo:
kvList = [Ent.USER, newOwner, entityType, fileDesc]
try:
if entityType != Ent.DRIVE_SHORTCUT:
kvList = [Ent.USER, newOwner, entityType, fileDesc]
try:
if entityType not in {Ent.DRIVE_SHORTCUT, Ent.DRIVE_FILE_SHORTCUT, Ent.DRIVE_FOLDER_SHORTCUT}:
if changeParents and 'addParents' in fileInfo:
if entityType != Ent.DRIVE_FILE_SHORTCUT:
callGAPI(targetDrive.files(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.CANNOT_ADD_PARENT, GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
fileId=xferFileId, addParents=fileInfo['addParents'], fields='', supportsAllDrives=True)
@@ -59291,13 +59321,27 @@ def transferOwnership(users):
Act.Set(Act.ADD)
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.DRIVE_FOLDER, fileInfo['addParents']], k, kcount)
Act.Set(action)
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.DOES_NOT_EXIST, k, kcount)
except (GAPI.forbidden, GAPI.cannotAddParent, GAPI.insufficientPermissions, GAPI.insufficientParentPermissions,
GAPI.invalid, GAPI.badRequest, GAPI.unknownError) as e:
entityActionFailedWarning(kvList, str(e), k, kcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(newOwner, str(e), 0, 0)
else:
if changeParents and 'addParents' in fileInfo and entityType != Ent.DRIVE_SHORTCUT:
body = {'name': fileInfo['name'], 'mimeType': MIMETYPE_GA_SHORTCUT,
'parents': [fileInfo['addParents']], 'shortcutDetails': {'targetId': fileInfo['shortcutDetails']['targetId']}}
callGAPI(targetDrive.files(), 'create',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR,
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP, GAPI.SHORTCUT_TARGET_INVALID,
GAPI.TARGET_USER_ROLE_LIMITED_BY_LICENSE_RESTRICTION],
body=body, fields='id', supportsAllDrives=True)
Act.Set(Act.CREATE_SHORTCUT)
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_IN, None, [Ent.DRIVE_FOLDER, fileInfo['addParents']], k, kcount)
Act.Set(action)
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.DOES_NOT_EXIST, k, kcount)
except (GAPI.forbidden, GAPI.cannotAddParent, GAPI.insufficientPermissions, GAPI.insufficientParentPermissions,
GAPI.invalid, GAPI.badRequest, GAPI.unknownError) as e:
entityActionFailedWarning(kvList, str(e), k, kcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(newOwner, str(e), 0, 0)
Ind.Decrement()
Ind.Decrement()
Ind.Decrement()
@@ -59501,7 +59545,7 @@ def claimOwnership(users):
fileEntryInfo = callGAPI(drive.files(), 'get',
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
fileId=fileId,
fields='id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId)')
fields='id,name,parents,mimeType,ownedByMe,trashed,shortcutDetails,owners(emailAddress,permissionId)')
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.NOT_FOUND, j, jcount)
continue
@@ -59529,6 +59573,8 @@ def claimOwnership(users):
if changeParents:
filesToClaim[owner][fileId]['addParents'] = addParents
filesToClaim[owner][fileId]['removeParents'] = ','.join(fileEntryInfo.get('parents', []))
if fileEntryInfo['mimeType'] == MIMETYPE_GA_SHORTCUT and entityType != Ent.DRIVE_SHORTCUT:
filesToClaim[owner][fileId]['shortcutDetails'] = fileEntryInfo['shortcutDetails']
if fileEntryInfo['mimeType'] == MIMETYPE_GA_FOLDER:
if buildTree:
_identifyFilesToClaim(fileEntry)
@@ -59565,7 +59611,7 @@ def claimOwnership(users):
fileDesc = f'{fileInfo["name"]} ({xferFileId})'
kvList = [Ent.USER, oldOwner, entityType, fileDesc]
try:
if entityType != Ent.DRIVE_SHORTCUT:
if entityType not in {Ent.DRIVE_SHORTCUT, Ent.DRIVE_FILE_SHORTCUT, Ent.DRIVE_FOLDER_SHORTCUT}:
if bodyShare:
callGAPI(sourceDrive.files(), 'update',
fileId=xferFileId, body=bodyShare, fields='')
@@ -59587,8 +59633,17 @@ def claimOwnership(users):
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount)
_processRetainedRole(user, i, count, oldOwner, entityType, xferFileId, fileDesc, l, lcount)
else:
kvList = [Ent.USER, user, entityType, fileDesc]
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount)
if changeParents and entityType != Ent.DRIVE_SHORTCUT:
callGAPI(sourceDrive.files(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS,
fileId=xferFileId, supportsAllDrives=True)
action = Act.Get()
Act.Set(Act.DELETE_SHORTCUT)
entityActionPerformed(kvList, l, lcount)
Act.Set(action)
else:
kvList = [Ent.USER, user, entityType, fileDesc]
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount)
except GAPI.permissionNotFound:
# if claimer not in ACL (file might be visible for all with link)
try:
@@ -59624,23 +59679,37 @@ def claimOwnership(users):
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
break
if entityType != Ent.DRIVE_SHORTCUT:
if changeParents and 'addParents' in fileInfo:
kvList = [Ent.USER, user, entityType, fileDesc]
try:
callGAPI(drive.files(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.CANNOT_ADD_PARENT, GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
fileId=xferFileId, addParents=fileInfo['addParents'], fields='', supportsAllDrives=True)
action = Act.Get()
Act.Set(Act.ADD)
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.DRIVE_FOLDER, fileInfo['addParents']], l, lcount)
kvList = [Ent.USER, user, entityType, fileDesc]
try:
if entityType not in {Ent.DRIVE_SHORTCUT, Ent.DRIVE_FILE_SHORTCUT, Ent.DRIVE_FOLDER_SHORTCUT}:
if changeParents and 'addParents' in fileInfo:
callGAPI(drive.files(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.CANNOT_ADD_PARENT, GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
fileId=xferFileId, addParents=fileInfo['addParents'], fields='', supportsAllDrives=True)
action = Act.Get()
Act.Set(Act.ADD)
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.DRIVE_FOLDER, fileInfo['addParents']], l, lcount)
Act.Set(action)
else:
if changeParents and 'addParents' in fileInfo and entityType != Ent.DRIVE_SHORTCUT:
body = {'name': fileInfo['name'], 'mimeType': MIMETYPE_GA_SHORTCUT,
'parents': [fileInfo['addParents']], 'shortcutDetails': {'targetId': fileInfo['shortcutDetails']['targetId']}}
callGAPI(drive.files(), 'create',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR,
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP, GAPI.SHORTCUT_TARGET_INVALID,
GAPI.TARGET_USER_ROLE_LIMITED_BY_LICENSE_RESTRICTION],
body=body, fields='id', supportsAllDrives=True)
Act.Set(Act.CREATE_SHORTCUT)
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_IN, None, [Ent.DRIVE_FOLDER, fileInfo['addParents']], l, lcount)
Act.Set(action)
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.DOES_NOT_EXIST, l, lcount)
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, GAPI.cannotAddParent) as e:
entityActionFailedWarning(kvList, str(e), l, lcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
except GAPI.fileNotFound:
entityActionFailedWarning(kvList, Msg.DOES_NOT_EXIST, l, lcount)
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, GAPI.cannotAddParent) as e:
entityActionFailedWarning(kvList, str(e), l, lcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
Ind.Decrement()
else:
entityPerformActionModifierNumItemsModifier([Ent.USER, user], 'Not Performed', kcount, Ent.DRIVE_FILE_OR_FOLDER,
@@ -72883,7 +72952,7 @@ def ProcessGAMCommand(args, processGamCfg=True, inLoop=False, closeSTD=True):
CROS_COMMANDS_WITH_OBJECTS[CL_command][CMD_FUNCTION][CL_objectName](entityList)
sys.exit(GM.Globals[GM.SYSEXITRC])
except KeyboardInterrupt:
batchWriteStderr('Control-C\n')
batchWriteStderr('\nControl-C\n')
setSysExitRC(KEYBOARD_INTERRUPT_RC)
showAPICallsRetryData()
adjustRedirectedSTDFilesIfNotMultiprocessing()

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -539,6 +539,10 @@ _SVCACCT_SCOPES = [
'api': CLASSROOM,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/classroom.profile.emails'},
{'name': 'Classroom API - Profile Photos',
'api': CLASSROOM,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/classroom.profile.photos'},
{'name': 'Classroom API - Rosters',
'api': CLASSROOM,
'subscopes': READONLY,

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -115,11 +115,13 @@ LIMIT_EXCEEDED = 'limitExceeded'
LOGIN_REQUIRED = 'loginRequired'
MALFORMED_WORKING_LOCATION_EVENT = 'malformedWorkingLocationEvent'
MEMBER_NOT_FOUND = 'memberNotFound'
MYDRIVE_HIERARCHY_DEPTH_LIMIT_EXCEEDED = 'myDriveHierarchyDepthLimitExceeded'
NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE = 'noListTeamDrivesAdministratorPrivilege'
NO_MANAGE_TEAMDRIVE_ADMINISTRATOR_PRIVILEGE = 'noManageTeamDriveAdministratorPrivilege'
NOT_A_CALENDAR_USER = 'notACalendarUser'
NOT_FOUND = 'notFound'
NOT_IMPLEMENTED = 'notImplemented'
NUM_CHILDREN_IN_NON_ROOT_LIMIT_EXCEEDED = 'numChildrenInNonRootLimitExceeded'
OPERATION_NOT_SUPPORTED = 'operationNotSupported'
ORGANIZER_ON_NON_TEAMDRIVE_NOT_SUPPORTED = 'organizerOnNonTeamDriveNotSupported'
ORGANIZER_ON_NON_TEAMDRIVE_ITEM_NOT_SUPPORTED = 'organizerOnNonTeamDriveItemNotSupported'
@@ -181,7 +183,7 @@ SERVICE_NOT_AVAILABLE_RETRY_REASONS = [SERVICE_NOT_AVAILABLE]
ACTIVITY_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST]
ALERT_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR]
CALENDAR_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, NOT_A_CALENDAR_USER]
CIGROUP_CREATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, ALREADY_EXISTS, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_ARGUMENT, PERMISSION_DENIED]
CIGROUP_CREATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, ALREADY_EXISTS, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_ARGUMENT, PERMISSION_DENIED, FAILED_PRECONDITION]
CIGROUP_GET_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR, PERMISSION_DENIED]
CIGROUP_LIST_THROW_REASONS = [SERVICE_NOT_AVAILABLE, RESOURCE_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, INVALID_ARGUMENT, SYSTEM_ERROR, PERMISSION_DENIED]
CIGROUP_LIST_USERKEY_THROW_REASONS = CIGROUP_LIST_THROW_REASONS+[INVALID_ARGUMENT]