mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-04 12:51:36 +00:00
916 lines
48 KiB
Python
916 lines
48 KiB
Python
"""File creation, update, shortcuts.
|
|
|
|
Part of the drive sub-package, extracted from drive.py."""
|
|
|
|
"""GAM Google Drive file, permission, shared drive, and label management."""
|
|
|
|
import re
|
|
import sys
|
|
|
|
from gamlib import glaction
|
|
from gamlib import glapi as API
|
|
from gamlib import glcfg as GC
|
|
from gamlib import glclargs
|
|
from gamlib import glentity
|
|
from gamlib import glgapi as GAPI
|
|
from gamlib import glglobals as GM
|
|
from gamlib import glindent
|
|
from gamlib import glmsgs as Msg
|
|
|
|
Act = glaction.GamAction()
|
|
Ent = glentity.GamEntity()
|
|
Ind = glindent.GamIndent()
|
|
Cmd = glclargs.GamCLArgs()
|
|
|
|
APPLICATION_VND_GOOGLE_APPS = 'application/vnd.google-apps.'
|
|
MIMETYPE_GA_DOCUMENT = f'{APPLICATION_VND_GOOGLE_APPS}document'
|
|
MIMETYPE_GA_DRAWING = f'{APPLICATION_VND_GOOGLE_APPS}drawing'
|
|
MIMETYPE_GA_FILE = f'{APPLICATION_VND_GOOGLE_APPS}file'
|
|
MIMETYPE_GA_FOLDER = f'{APPLICATION_VND_GOOGLE_APPS}folder'
|
|
MIMETYPE_GA_FORM = f'{APPLICATION_VND_GOOGLE_APPS}form'
|
|
MIMETYPE_GA_FUSIONTABLE = f'{APPLICATION_VND_GOOGLE_APPS}fusiontable'
|
|
MIMETYPE_GA_JAM = f'{APPLICATION_VND_GOOGLE_APPS}jam'
|
|
MIMETYPE_GA_MAP = f'{APPLICATION_VND_GOOGLE_APPS}map'
|
|
MIMETYPE_GA_PRESENTATION = f'{APPLICATION_VND_GOOGLE_APPS}presentation'
|
|
MIMETYPE_GA_SCRIPT = f'{APPLICATION_VND_GOOGLE_APPS}script'
|
|
MIMETYPE_GA_SCRIPT_JSON = f'{APPLICATION_VND_GOOGLE_APPS}script+json'
|
|
MIMETYPE_GA_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}shortcut'
|
|
MIMETYPE_GA_3P_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}drive-sdk'
|
|
MIMETYPE_GA_SITE = f'{APPLICATION_VND_GOOGLE_APPS}site'
|
|
MIMETYPE_GA_SPREADSHEET = f'{APPLICATION_VND_GOOGLE_APPS}spreadsheet'
|
|
ME_IN_OWNERS = "'me' in owners"
|
|
ME_IN_OWNERS_AND = ME_IN_OWNERS + " and "
|
|
NOT_ME_IN_OWNERS = "not " + ME_IN_OWNERS
|
|
NOT_ME_IN_OWNERS_AND = NOT_ME_IN_OWNERS + " and "
|
|
WITH_ANY_FILE_NAME = "name = '{0}'"
|
|
WITH_MY_FILE_NAME = ME_IN_OWNERS_AND + WITH_ANY_FILE_NAME
|
|
WITH_OTHER_FILE_NAME = NOT_ME_IN_OWNERS_AND + WITH_ANY_FILE_NAME
|
|
ROOT = 'root'
|
|
ORPHANS = 'Orphans'
|
|
SHARED_WITHME = 'SharedWithMe'
|
|
SHARED_DRIVES = 'SharedDrives'
|
|
|
|
def _getMain():
|
|
return sys.modules['gam']
|
|
|
|
def __getattr__(name):
|
|
"""Fall back to gam module for any undefined names."""
|
|
main = _getMain()
|
|
try:
|
|
return getattr(main, name)
|
|
except AttributeError:
|
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
|
|
def processFilenameReplacements(name, replacements):
|
|
for replacement in replacements:
|
|
name = re.sub(replacement[0], replacement[1], name)
|
|
return name
|
|
|
|
def addTimestampToFilename(parameters, body):
|
|
tdtime = arrow.now(GC.Values[GC.TIMEZONE])
|
|
body['name'] += ' - '
|
|
if not parameters[DFA_TIMEFORMAT]:
|
|
body['name'] += _getMain().ISOformatTimeStamp(tdtime)
|
|
else:
|
|
body['name'] += tdtime.strftime(parameters[DFA_TIMEFORMAT])
|
|
|
|
createReturnItemMap = {
|
|
'returnidonly': 'id',
|
|
'returnlinkonly': 'webViewLink',
|
|
'returneditlinkonly': 'editLink'
|
|
}
|
|
|
|
# gam <UserTypeEntity> create drivefile
|
|
# [(localfile <FileName>|-)|(url <URL>)]
|
|
# [(drivefilename|newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
|
|
# [stripnameprefix <String>]
|
|
# [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
|
|
# <DriveFileCreateAttribute>* [noduplicate]
|
|
# [(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*)) |
|
|
# (returnidonly|returnlinkonly|returneditlinkonly|showdetails)]
|
|
def createDriveFile(users):
|
|
csvPF = media_body = None
|
|
addCSVData = {}
|
|
returnIdLink = None
|
|
noDuplicate = showDetails = False
|
|
body = {}
|
|
newName = None
|
|
assignLocalName = True
|
|
parameters = initDriveFileAttributes()
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg in {'drivefilename', 'newfilename'}:
|
|
newName = _getMain().getString(Cmd.OB_DRIVE_FILE_NAME)
|
|
assignLocalName = False
|
|
elif myarg in createReturnItemMap:
|
|
returnIdLink = createReturnItemMap[myarg]
|
|
showDetails = False
|
|
elif myarg == 'showdetails':
|
|
returnIdLink = None
|
|
showDetails = True
|
|
elif myarg == 'noduplicate':
|
|
noDuplicate = True
|
|
elif myarg == 'csv':
|
|
csvPF = _getMain().CSVPrintFile()
|
|
elif csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif csvPF and myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
else:
|
|
getDriveFileAttribute(myarg, body, parameters, False)
|
|
if assignLocalName and parameters[DFA_LOCALFILENAME] and parameters[DFA_LOCALFILENAME] != '-':
|
|
newName = parameters[DFA_LOCALFILENAME]
|
|
if newName:
|
|
if parameters[DFA_STRIPNAMEPREFIX] and newName.startswith(parameters[DFA_STRIPNAMEPREFIX]):
|
|
newName = newName[len(parameters[DFA_STRIPNAMEPREFIX]):]
|
|
if parameters[DFA_REPLACEFILENAME]:
|
|
body['name'] = processFilenameReplacements(newName, parameters[DFA_REPLACEFILENAME])
|
|
else:
|
|
body['name'] = newName
|
|
else:
|
|
body['name'] = 'Untitled'
|
|
if parameters[DFA_TIMESTAMP]:
|
|
addTimestampToFilename(parameters, body)
|
|
if parameters[DFA_LOCALFILEPATH]:
|
|
if parameters[DFA_LOCALFILEPATH] != '-' and parameters[DFA_PRESERVE_FILE_TIMES]:
|
|
setPreservedFileTimes(body, parameters, False)
|
|
if body.get('mimeType') == MIMETYPE_GA_SCRIPT_JSON:
|
|
parameters[DFA_LOCALMIMETYPE] = body['mimeType']
|
|
media_body = getMediaBody(parameters)
|
|
elif parameters[DFA_URL]:
|
|
media_body = getMediaBody(parameters)
|
|
body['mimeType'] = parameters[DFA_LOCALMIMETYPE]
|
|
if csvPF:
|
|
csvPF.SetTitles(['User', 'name', 'id'])
|
|
if showDetails:
|
|
csvPF.AddTitles(['parentId', 'mimeType'])
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
Act.Set(Act.CREATE)
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive = _getMain().buildGAPIServiceObject(API.DRIVE3, user, i, count)
|
|
if not drive:
|
|
continue
|
|
if not _getDriveFileParentInfo(drive, user, i, count, body, parameters):
|
|
continue
|
|
entityType = _getMain()._getEntityMimeType(body) if 'mimeType' in body else Ent.DRIVE_FILE
|
|
try:
|
|
if noDuplicate:
|
|
# Check for existing file/folder, do not duplicate
|
|
files = _getMain().callGAPIitems(drive.files(), 'list', 'files',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID],
|
|
q=f"name = '{escapeDriveFileName(body['name'])}'and '{body['parents'][0]}' in parents and trashed = false",
|
|
fields='files(id)', **parameters['searchargs'])
|
|
if files:
|
|
_getMain().entityActionNotPerformedWarning([Ent.USER, user, entityType, body['name'], Ent.DRIVE_PARENT_FOLDER_ID, body['parents'][0]],
|
|
f"{Msg.DUPLICATE} IDs {','.join([file['id'] for file in files])}", i, count)
|
|
continue
|
|
result = _getMain().callGAPI(drive.files(), 'create',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
|
|
GAPI.PERMISSION_DENIED, GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
|
|
GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR, GAPI.INTERNAL_ERROR,
|
|
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
|
|
GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP,
|
|
GAPI.UPLOAD_TOO_LARGE, GAPI.TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED],
|
|
ocrLanguage=parameters[DFA_OCRLANGUAGE],
|
|
ignoreDefaultVisibility=parameters[DFA_IGNORE_DEFAULT_VISIBILITY],
|
|
keepRevisionForever=parameters[DFA_KEEP_REVISION_FOREVER],
|
|
useContentAsIndexableText=parameters[DFA_USE_CONTENT_AS_INDEXABLE_TEXT],
|
|
media_body=media_body, body=body, fields='id,name,mimeType,parents,webViewLink', supportsAllDrives=True)
|
|
parentId = result['parents'][0] if 'parents' in result and result['parents'] else _getMain().UNKNOWN
|
|
if returnIdLink:
|
|
writeReturnIdLink(returnIdLink, parameters[DFA_LOCALMIMETYPE], result)
|
|
elif not csvPF:
|
|
kvList = [Ent.USER, user]
|
|
if not showDetails:
|
|
titleInfo = f'{result["name"]}({result["id"]})'
|
|
if parameters[DFA_LOCALFILENAME]:
|
|
kvList.extend([Ent.DRIVE_FILE, titleInfo])
|
|
else:
|
|
kvList.extend([_getMain()._getEntityMimeType(result), titleInfo])
|
|
else:
|
|
if result['mimeType'] != MIMETYPE_GA_FOLDER:
|
|
kvList.extend([Ent.DRIVE_FILE, result['name'], Ent.DRIVE_FILE_ID, result['id']])
|
|
else:
|
|
kvList.extend([Ent.DRIVE_FOLDER, result['name'], Ent.DRIVE_FOLDER_ID, result['id']])
|
|
kvList.extend([Ent.DRIVE_PARENT_FOLDER_ID, parentId, Ent.MIMETYPE, result['mimeType']])
|
|
if media_body:
|
|
_getMain().entityModifierNewValueActionPerformed(kvList, Act.MODIFIER_WITH_CONTENT_FROM, parameters[DFA_LOCALFILENAME] or parameters[DFA_URL], i, count)
|
|
else:
|
|
_getMain().entityActionPerformed(kvList, i, count)
|
|
else:
|
|
row = {'User': user, 'name': result['name'], 'id': result['id']}
|
|
if showDetails:
|
|
row.update({'parentId': parentId, 'mimeType': result['mimeType']})
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRow(row)
|
|
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions,
|
|
GAPI.invalidQuery, GAPI.permissionDenied, GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent,
|
|
GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed,
|
|
GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep,
|
|
GAPI.uploadTooLarge, GAPI.teamDrivesShortcutFileNotSupported) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, entityType, body['name']], str(e), i, count)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Files')
|
|
|
|
# gam <UserTypeEntity> create drivefolderpath
|
|
# [pathdelimiter <Character>]
|
|
# ((fullpath <DriveFolderPath) |
|
|
# (path <DriveFolderPath [<DriveFileParentAttribute>]) |
|
|
# (list <DriveFolderNameList>) [<DriveFileParentAttribute>]))
|
|
# [(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*) |
|
|
# returnidonly]
|
|
def createDriveFolderPath(users):
|
|
csvPF = None
|
|
addCSVData = {}
|
|
fullPath = parentSpecified = returnIdOnly = False
|
|
parentBody = {}
|
|
parentParms = initDriveFileAttributes()
|
|
driveFolderNameList = []
|
|
pathDelimiter = '/'
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'pathdelimiter':
|
|
pathDelimiter = _getMain().getCharacter()
|
|
elif myarg == 'fullpath':
|
|
folderPathLocation = Cmd.Location()
|
|
driveFolderNameList = _getMain().getString(Cmd.OB_DRIVE_FOLDER_PATH).lstrip(pathDelimiter).strip(' ').split(pathDelimiter)
|
|
if len(driveFolderNameList) > 0:
|
|
if driveFolderNameList[0].lower() == _getMain().MY_DRIVE.lower():
|
|
parentParms[DFA_PARENTID] = ROOT
|
|
driveFolderNameList = driveFolderNameList[1:]
|
|
elif driveFolderNameList[0].lower() == SHARED_DRIVES.lower() and len(driveFolderNameList) > 1:
|
|
parentParms[DFA_SHAREDDRIVE_PARENT] = driveFolderNameList[1]
|
|
driveFolderNameList = driveFolderNameList[2:]
|
|
else:
|
|
_getMain().usageErrorExit(Msg.FULL_PATH_MUST_START_WITH_DRIVE.format(_getMain().MY_DRIVE, f'{SHARED_DRIVES}{pathDelimiter}<SharedDriveName>'))
|
|
fullPath = True
|
|
elif myarg == 'path':
|
|
folderPathLocation = Cmd.Location()
|
|
driveFolderNameList = _getMain().getString(Cmd.OB_DRIVE_FOLDER_PATH).strip(' ').split(pathDelimiter)
|
|
elif myarg == 'list':
|
|
folderPathLocation = Cmd.Location()
|
|
driveFolderNameList = _getMain().shlexSplitList(_getMain().getString(Cmd.OB_DRIVE_FOLDER_NAME_LIST), dataDelimiter=',')
|
|
elif getDriveFileParentAttribute(myarg, parentParms):
|
|
parentSpecified = True
|
|
elif myarg == 'returnidonly':
|
|
returnIdOnly = True
|
|
elif myarg == 'csv':
|
|
csvPF = _getMain().CSVPrintFile(['User', 'name', 'id', 'status', 'pathIndex', 'pathLength'], 'sortall')
|
|
elif csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif csvPF and myarg == 'addcsvdata':
|
|
_getMain().getAddCSVData(addCSVData)
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if not driveFolderNameList:
|
|
Cmd.SetLocation(folderPathLocation)
|
|
_getMain().emptyArgumentExit('fullpath|path|list')
|
|
if fullPath and parentSpecified:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('fullpath', '<DriveFileParentAttribute>'))
|
|
for folderName in driveFolderNameList:
|
|
if not folderName.strip():
|
|
Cmd.SetLocation(folderPathLocation)
|
|
_getMain().usageErrorExit(Msg.ALL_FOLDER_NAMES_MUST_BE_NON_BLANK.format(driveFolderNameList))
|
|
if csvPF:
|
|
if addCSVData:
|
|
csvPF.AddTitles(sorted(addCSVData.keys()))
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive = _getMain().buildGAPIServiceObject(API.DRIVE3, user, i, count)
|
|
if not drive:
|
|
continue
|
|
if not _getDriveFileParentInfo(drive, user, i, count, parentBody, parentParms,
|
|
defaultToRoot=True, entityType=Ent.DRIVE_FOLDER):
|
|
continue
|
|
parentId = parentBody['parents'][0]
|
|
if parentParms.get('searchargs', {}).get('corpora', ''):
|
|
query = _getMain().ANY_NON_TRASHED_FOLDER_NAME_WITH_PARENTS
|
|
else:
|
|
query = _getMain().MY_NON_TRASHED_FOLDER_NAME_WITH_PARENTS
|
|
errors = False
|
|
createOnly = False
|
|
if not returnIdOnly and not csvPF:
|
|
_getMain().entityPerformAction([Ent.USER, user, Ent.DRIVE_FOLDER_PATH, ''], i, count)
|
|
jcount = len(driveFolderNameList)
|
|
Ind.Increment()
|
|
j = 0
|
|
for folderName in driveFolderNameList:
|
|
j += 1
|
|
try:
|
|
folderFound = False
|
|
if not createOnly:
|
|
op = 'Find Folder'
|
|
result = _getMain().callGAPIpages(drive.files(), 'list', 'files',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.INSUFFICIENT_PERMISSIONS],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
q=query.format(escapeDriveFileName(folderName), parentId),
|
|
supportsAllDrives=True, includeItemsFromAllDrives=True,
|
|
fields='nextPageToken,files(id,name)')
|
|
if result:
|
|
if len(result) > 1:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FOLDER, folderName], f'{op}: {len(result)} Folders with same name')
|
|
errors = True
|
|
break
|
|
parentId = result[0]['id']
|
|
parentName = result[0]['name']
|
|
folderFound = True
|
|
Act.Set(Act.EXISTS)
|
|
if not folderFound:
|
|
op = 'Create Folder'
|
|
result = _getMain().callGAPI(drive.files(), 'create',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
|
|
GAPI.UNKNOWN_ERROR, GAPI.BAD_REQUEST,
|
|
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP],
|
|
body={'parents': [parentId], 'name': folderName, 'mimeType': MIMETYPE_GA_FOLDER}, fields='id,name', supportsAllDrives=True)
|
|
parentId = result['id']
|
|
parentName = result['name']
|
|
createOnly = True
|
|
Act.Set(Act.CREATE)
|
|
except (GAPI.forbidden, GAPI.insufficientPermissions, GAPI.insufficientParentPermissions,
|
|
GAPI.unknownError, GAPI.badRequest, GAPI.storageQuotaExceeded, GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FOLDER, folderName], f'{op}: {str(e)}', j, jcount)
|
|
errors = True
|
|
break
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
errors = True
|
|
break
|
|
if not returnIdOnly:
|
|
if not csvPF:
|
|
_getMain().entityActionPerformed([Ent.USER, user, Ent.DRIVE_FOLDER_NAME, f'{parentName}({parentId})'],
|
|
j, jcount)
|
|
else:
|
|
row = {'User': user, 'name': parentName, 'id': parentId, 'status': Act.Performed(), 'pathIndex': j, 'pathLength': jcount}
|
|
if addCSVData:
|
|
row.update(addCSVData)
|
|
csvPF.WriteRow(row)
|
|
if returnIdOnly and not errors:
|
|
_getMain().writeStdout(f'{parentId}\n')
|
|
Ind.Decrement()
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Folders')
|
|
|
|
# gam <UserTypeEntity> create drivefileshortcut <DriveFileEntity> [shortcutname <String>]
|
|
# [<DriveFileParentAttribute>|convertparents]
|
|
# [(csv [todrive <ToDriveAttribute>*]) | returnidonly]
|
|
def createDriveFileShortcut(users):
|
|
csvPF = baseShortcutName = None
|
|
convertParents = newParentsSpecified = returnIdOnly = False
|
|
fileIdEntity = getDriveFileEntity()
|
|
parentBody = {}
|
|
parentParms = initDriveFileAttributes()
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'shortcutname':
|
|
baseShortcutName = _getMain().getString(Cmd.OB_DRIVE_FILE_NAME)
|
|
elif myarg == 'returnidonly':
|
|
returnIdOnly = True
|
|
elif myarg == 'csv':
|
|
csvPF = _getMain().CSVPrintFile(['User', 'name', 'id', 'targetName', 'targetId'], 'sortall')
|
|
elif csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
elif getDriveFileParentAttribute(myarg, parentParms):
|
|
if convertParents:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'convertparents'))
|
|
newParentsSpecified = True
|
|
elif myarg == 'convertparents':
|
|
if newParentsSpecified:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, '<DriveFileParentAttribute>'))
|
|
convertParents = True
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
if fileIdEntity['query']:
|
|
fileIdEntity['query'] = fileIdEntity['query']+_getMain().AND_NOT_SHORTCUT
|
|
elif fileIdEntity['shareddrivefilequery']:
|
|
fileIdEntity['shareddrivefilequery'] = fileIdEntity['shareddrivefilequery']+_getMain().AND_NOT_SHORTCUT
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive, jcount = _validateUserGetFileIDs(user, i, count, fileIdEntity)
|
|
if not returnIdOnly and not csvPF:
|
|
_getMain().entityPerformActionSubItemModifierNumItems([Ent.USER, user], Ent.DRIVE_FILE_SHORTCUT,
|
|
Act.MODIFIER_FOR, jcount, Ent.DRIVE_FILE_OR_FOLDER, i, count)
|
|
if jcount == 0:
|
|
continue
|
|
if not _getDriveFileParentInfo(drive, user, i, count, parentBody, parentParms):
|
|
continue
|
|
if newParentsSpecified:
|
|
newParents = parentBody['parents']
|
|
numNewParents = len(newParents)
|
|
elif not convertParents:
|
|
try:
|
|
rootFolderId = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
|
fileId=ROOT, fields='id')['id']
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
continue
|
|
newParents = [rootFolderId]
|
|
numNewParents = 1
|
|
Ind.Increment()
|
|
j = 0
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
Act.Set(Act.CREATE)
|
|
try:
|
|
target = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=fileId, fields='mimeType,name,parents', supportsAllDrives=True)
|
|
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.invalid, GAPI.badRequest,
|
|
GAPI.fileNotFound, GAPI.unknownError, GAPI.teamDrivesSharingRestrictionNotAllowed) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, fileId], str(e), j, jcount)
|
|
continue
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
targetName = target['name']
|
|
if baseShortcutName:
|
|
shortcutName = baseShortcutName.replace('#filename#', targetName)
|
|
else:
|
|
shortcutName = targetName
|
|
targetEntityType = _getMain()._getEntityMimeType(target)
|
|
if convertParents:
|
|
newParents = target.get('parents', [])[:-1]
|
|
numNewParents = len(newParents)
|
|
if numNewParents <= 1:
|
|
_getMain().entityActionNotPerformedWarning([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_FILE_SHORTCUT, None],
|
|
Msg.NO_PARENTS_TO_CONVERT_TO_SHORTCUTS, j, jcount)
|
|
continue
|
|
removeParents = []
|
|
body = {'name': shortcutName, 'mimeType': MIMETYPE_GA_SHORTCUT, 'parents': None, 'shortcutDetails': {'targetId': fileId}}
|
|
if not returnIdOnly and not csvPF:
|
|
_getMain().entityPerformActionNumItems([Ent.USER, user, targetEntityType, targetName], numNewParents, Ent.DRIVE_FILE_SHORTCUT, j, jcount)
|
|
try:
|
|
existingShortcuts = _getMain().callGAPI(drive.files(), 'list',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID],
|
|
retryReasons=[GAPI.UNKNOWN_ERROR],
|
|
supportsAllDrives=True, includeItemsFromAllDrives=True,
|
|
q=f"shortcutDetails.targetId = '{fileId}' and trashed = False", fields='files(id,name,parents)')['files']
|
|
except (GAPI.invalidQuery, GAPI.invalid, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
|
|
existingShortcuts = []
|
|
Ind.Increment()
|
|
k = 0
|
|
for parentId in newParents:
|
|
k += 1
|
|
duplicateShortcut = False
|
|
for shortcut in existingShortcuts:
|
|
if parentId in shortcut['parents'] and shortcutName == shortcut['name']:
|
|
_getMain().entityActionNotPerformedWarning([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_FILE_SHORTCUT, f'{shortcut["name"]}({shortcut["id"]})'],
|
|
Msg.DUPLICATE, k, numNewParents)
|
|
duplicateShortcut = True
|
|
break
|
|
if duplicateShortcut:
|
|
continue
|
|
body['parents'] = [parentId]
|
|
try:
|
|
result = _getMain().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],
|
|
body=body, fields='id,name', supportsAllDrives=True)
|
|
removeParents.append(parentId)
|
|
if returnIdOnly:
|
|
_getMain().writeStdout(f'{result["id"]}\n')
|
|
elif not csvPF:
|
|
_getMain().entityActionPerformed([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_FILE_SHORTCUT, f'{result["name"]}({result["id"]})'],
|
|
k, numNewParents)
|
|
else:
|
|
csvPF.WriteRow({'User': user, 'name': result['name'], 'id': result['id'], 'targetName': targetName, 'targetId': fileId})
|
|
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, GAPI.invalid, GAPI.badRequest,
|
|
GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed,
|
|
GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep, GAPI.shortcutTargetInvalid) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_FILE_SHORTCUT, body['name']], str(e), k, numNewParents)
|
|
Ind.Decrement()
|
|
if convertParents and removeParents:
|
|
if not returnIdOnly and not csvPF:
|
|
lcount = len(removeParents)
|
|
Act.Set(Act.DELETE)
|
|
_getMain().entityPerformActionNumItems([Ent.USER, user, targetEntityType, targetName], lcount, Ent.DRIVE_PARENT_FOLDER_REFERENCE, j, jcount)
|
|
try:
|
|
_getMain().callGAPI(drive.files(), 'update',
|
|
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST],
|
|
fileId=fileId,
|
|
removeParents=','.join(removeParents), body={}, fields='id', supportsAllDrives=True)
|
|
if not returnIdOnly and not csvPF:
|
|
Ind.Increment()
|
|
for l, parent in enumerate(removeParents):
|
|
_getMain().entityActionPerformed([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_PARENT_FOLDER_REFERENCE, parent], l+1, lcount)
|
|
Ind.Decrement()
|
|
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.invalid, GAPI.badRequest,
|
|
GAPI.fileNotFound, GAPI.unknownError, GAPI.teamDrivesSharingRestrictionNotAllowed) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, targetEntityType, targetName, Ent.DRIVE_PARENT_FOLDER_REFERENCE, str(l)], str(e), j, jcount)
|
|
Ind.Decrement()
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Shortcuts')
|
|
|
|
SHORTCUT_CODE_VALID = 0
|
|
SHORTCUT_CODE_SHORTCUT_NOT_FOUND = 1
|
|
SHORTCUT_CODE_NOT_A_SHORTCUT = 2
|
|
SHORTCUT_CODE_TARGET_NOT_FOUND = 3
|
|
SHORTCUT_CODE_MIMETYPE_MISMATCH = 4
|
|
|
|
# gam <UserTypeEntity> check drivefileshortcut <DriveFileEntity>
|
|
# [csv [todrive <ToDriveAttribute>*]]
|
|
def checkDriveFileShortcut(users):
|
|
csvPF = None
|
|
fileIdEntity = getDriveFileEntity()
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'csv':
|
|
csvPF = _getMain().CSVPrintFile(['User', 'name', 'id', 'owner', 'parentId', 'shortcutDetails.targetId', 'shortcutDetails.targetMimeType',
|
|
'targetName', 'targetId', 'targetMimeType', 'code'], 'sortall')
|
|
elif csvPF and myarg == 'todrive':
|
|
csvPF.GetTodriveParameters()
|
|
else:
|
|
_getMain().unknownArgumentExit()
|
|
scfields = 'id,name,mimeType,owners(emailAddress),parents,shortcutDetails'
|
|
trfields = 'id,name,mimeType'
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive, jcount = _validateUserGetFileIDs(user, i, count, fileIdEntity)
|
|
if not csvPF:
|
|
_getMain().entityPerformActionNumItems([Ent.USER, user], jcount, Ent.DRIVE_FILE_SHORTCUT, i, count)
|
|
if jcount == 0:
|
|
continue
|
|
Ind.Increment()
|
|
j = 0
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
row = {'User': user, 'id': fileId}
|
|
try:
|
|
scresult = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=fileId, fields=scfields, supportsAllDrives=True)
|
|
row['name'] = scresult['name']
|
|
if scresult['mimeType'] != MIMETYPE_GA_SHORTCUT:
|
|
if not csvPF:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, _getMain()._getEntityMimeType(scresult), fileId],
|
|
Msg.INVALID_MIMETYPE.format(scresult['mimeType'], MIMETYPE_GA_SHORTCUT), j, jcount)
|
|
else:
|
|
row['code'] = SHORTCUT_CODE_NOT_A_SHORTCUT
|
|
csvPF.WriteRow(row)
|
|
continue
|
|
if 'owners' in scresult:
|
|
row['owner'] = scresult['owners'][0]['emailAddress']
|
|
row['parentId'] = scresult['parents'][0]
|
|
row[f'shortcutDetails{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}targetId'] = scresult['shortcutDetails']['targetId']
|
|
row[f'shortcutDetails{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}targetMimeType'] = scresult['shortcutDetails']['targetMimeType']
|
|
trfileId = scresult['shortcutDetails']['targetId']
|
|
try:
|
|
trresult = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=trfileId, fields=trfields, supportsAllDrives=True)
|
|
row['targetName'] = trresult['name']
|
|
row['targetId'] = trresult['id']
|
|
row['targetMimeType'] = trresult['mimeType']
|
|
entityList = [Ent.USER, user, Ent.DRIVE_FILE_SHORTCUT, f"{scresult['name']}({fileId})",
|
|
_getMain()._getEntityMimeType(trresult), f"{trresult['name']}({trfileId})"]
|
|
if scresult['shortcutDetails']['targetMimeType'] == trresult['mimeType']:
|
|
if not csvPF:
|
|
_getMain().entityActionPerformed(entityList, j, jcount)
|
|
else:
|
|
row['code'] = SHORTCUT_CODE_VALID
|
|
else:
|
|
if not csvPF:
|
|
_getMain().entityActionFailedWarning(entityList, Msg.MIMETYPE_MISMATCH.format(scresult['shortcutDetails']['targetMimeType'], trresult['mimeType']), j, jcount)
|
|
else:
|
|
row['code'] = SHORTCUT_CODE_MIMETYPE_MISMATCH
|
|
except GAPI.fileNotFound:
|
|
if not csvPF:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_SHORTCUT, f"{scresult['name']}({fileId})",
|
|
_getMain()._getTargetEntityMimeType(scresult), trfileId], Msg.NOT_FOUND, j, jcount)
|
|
else:
|
|
row['code'] = SHORTCUT_CODE_TARGET_NOT_FOUND
|
|
except GAPI.fileNotFound:
|
|
if not csvPF:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_SHORTCUT, fileId], Msg.NOT_FOUND, j, jcount)
|
|
else:
|
|
row['code'] = SHORTCUT_CODE_SHORTCUT_NOT_FOUND
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
if csvPF:
|
|
csvPF.WriteRow(row)
|
|
Ind.Decrement()
|
|
if csvPF:
|
|
csvPF.writeCSVfile('Check Shortcuts')
|
|
|
|
# gam <UserTypeEntity> update drivefile <DriveFileEntity> [copy] [returnidonly|returnlinkonly]
|
|
# [(localfile <FileName>|-)|(url <URL>)]
|
|
# [retainname | (newfilename <DriveFileName>) | (replacefilename <REMatchPattern> <RESubstitution>)*]
|
|
# [stripnameprefix <String>]
|
|
# [timestamp <Boolean>]] [timeformat <DateTimeFormat>]
|
|
# <DriveFileUpdateAttribute>*
|
|
# [(gsheet|csvsheet <SheetEntity> [clearfilter])|(addsheet <String>)]
|
|
# [charset <String>] [columndelimiter <Character>]
|
|
def updateDriveFile(users):
|
|
fileIdEntity = getDriveFileEntity()
|
|
body = {}
|
|
newName = None
|
|
assignLocalName = True
|
|
parameters = initDriveFileAttributes()
|
|
media_body = None
|
|
addSheetBody = addSheetEntity = None
|
|
updateSheetEntity = None
|
|
clearFilter = False
|
|
preserveModifiedTime = False
|
|
encoding = GC.Values[GC.CHARSET]
|
|
columnDelimiter = GC.Values[GC.CSV_INPUT_COLUMN_DELIMITER]
|
|
returnIdLink = None
|
|
operation = 'update'
|
|
while Cmd.ArgumentsRemaining():
|
|
myarg = _getMain().getArgument()
|
|
if myarg == 'copy':
|
|
operation = 'copy'
|
|
Act.Set(Act.COPY)
|
|
elif myarg == 'returnidonly':
|
|
returnIdLink = 'id'
|
|
elif myarg == 'returnlinkonly':
|
|
returnIdLink = 'webViewLink'
|
|
elif myarg == 'retainname':
|
|
assignLocalName = False
|
|
elif myarg == 'newfilename':
|
|
newName = _getMain().getString(Cmd.OB_DRIVE_FILE_NAME)
|
|
assignLocalName = False
|
|
elif getDriveFileAddRemoveParentAttribute(myarg, parameters):
|
|
pass
|
|
elif myarg == 'addsheet':
|
|
if updateSheetEntity:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'csvsheet'))
|
|
sheetTitle = _getMain().getString(Cmd.OB_STRING)
|
|
addSheetEntity = {'sheetType': Ent.SHEET, 'sheetValue': sheetTitle, 'sheetId': None, 'sheetTitle': sheetTitle}
|
|
addSheetBody = {'requests': [{'addSheet': {'properties': {'title': sheetTitle, 'sheetType': 'GRID'}}}]}
|
|
elif myarg in {'gsheet', 'csvsheet'}:
|
|
if addSheetEntity:
|
|
_getMain().usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'addsheet'))
|
|
updateSheetEntity = _getMain().getSheetEntity(False)
|
|
elif myarg == 'clearfilter':
|
|
clearFilter = _getMain().getBoolean()
|
|
elif myarg == 'charset':
|
|
encoding = _getMain().getString(Cmd.OB_CHAR_SET)
|
|
elif myarg == 'columndelimiter':
|
|
columnDelimiter = _getMain().getCharacter()
|
|
else:
|
|
getDriveFileAttribute(myarg, body, parameters, True)
|
|
if assignLocalName and parameters[DFA_LOCALFILENAME] and parameters[DFA_LOCALFILENAME] != '-':
|
|
newName = parameters[DFA_LOCALFILENAME]
|
|
if newName:
|
|
if parameters[DFA_STRIPNAMEPREFIX] and newName.startswith(parameters[DFA_STRIPNAMEPREFIX]):
|
|
newName = newName[len(parameters[DFA_STRIPNAMEPREFIX]):]
|
|
if operation == 'update' and parameters[DFA_LOCALFILEPATH]:
|
|
if parameters[DFA_LOCALFILEPATH] != '-' and parameters[DFA_PRESERVE_FILE_TIMES]:
|
|
setPreservedFileTimes(body, parameters, True)
|
|
if not addSheetEntity and not updateSheetEntity:
|
|
media_body = getMediaBody(parameters)
|
|
elif operation == 'update' and parameters[DFA_URL]:
|
|
addSheetEntity = updateSheetEntity = None
|
|
media_body = getMediaBody(parameters)
|
|
body['mimeType'] = parameters[DFA_LOCALMIMETYPE]
|
|
elif operation == 'update' and parameters[DFA_PRESERVE_FILE_TIMES]:
|
|
preserveModifiedTime = True
|
|
i, count, users = _getMain().getEntityArgument(users)
|
|
for user in users:
|
|
i += 1
|
|
user, drive, jcount = _validateUserGetFileIDs(user, i, count, fileIdEntity,
|
|
entityType=Ent.DRIVE_FILE_OR_FOLDER if returnIdLink is None else None)
|
|
if jcount == 0:
|
|
continue
|
|
if not _getDriveFileParentInfo(drive, user, i, count, body, parameters, defaultToRoot=False):
|
|
continue
|
|
newParents = body.pop('parents', [])
|
|
if operation == 'update':
|
|
status, addParentsBase, removeParentsBase = _getDriveFileAddRemoveParentInfo(user, i, count, parameters, drive)
|
|
if not status:
|
|
continue
|
|
Ind.Increment()
|
|
j = 0
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
try:
|
|
addParents = addParentsBase[:]
|
|
removeParents = removeParentsBase[:]
|
|
if newParents or (not newName and parameters[DFA_REPLACEFILENAME]) or preserveModifiedTime:
|
|
result = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
|
fileId=fileId, fields='name,parents,modifiedTime', supportsAllDrives=True)
|
|
if newParents:
|
|
addParents.extend(newParents)
|
|
removeParents.extend(result.get('parents', []))
|
|
if not newName and parameters[DFA_REPLACEFILENAME]:
|
|
body['name'] = processFilenameReplacements(result['name'], parameters[DFA_REPLACEFILENAME])
|
|
if preserveModifiedTime:
|
|
body['modifiedTime'] = result['modifiedTime']
|
|
if newName:
|
|
if parameters[DFA_REPLACEFILENAME]:
|
|
body['name'] = processFilenameReplacements(newName, parameters[DFA_REPLACEFILENAME])
|
|
else:
|
|
body['name'] = newName
|
|
if parameters[DFA_TIMESTAMP]:
|
|
addTimestampToFilename(parameters, body)
|
|
if addSheetEntity or updateSheetEntity:
|
|
entityValueList = [Ent.USER, user, Ent.DRIVE_FILE_ID, fileId]
|
|
try:
|
|
result = _getMain().callGAPI(drive.files(), 'get',
|
|
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND],
|
|
fileId=fileId, fields='id,mimeType,capabilities(canEdit)', supportsAllDrives=True)
|
|
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}', j, jcount)
|
|
continue
|
|
if not result['capabilities']['canEdit']:
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, Msg.NOT_WRITABLE, j, jcount)
|
|
continue
|
|
_, sheet = _getMain().buildGAPIServiceObject(API.SHEETS, user)
|
|
if sheet is None:
|
|
continue
|
|
if addSheetEntity:
|
|
addresult = _getMain().callGAPI(sheet.spreadsheets(), 'batchUpdate',
|
|
throwReasons=GAPI.SHEETS_ACCESS_THROW_REASONS,
|
|
spreadsheetId=fileId, body=addSheetBody)
|
|
sheetEntity = addSheetEntity.copy()
|
|
sheetEntity['sheetId'] = addresult['replies'][0]['addSheet']['properties']['sheetId']
|
|
entityValueList.extend([sheetEntity['sheetType'], sheetEntity['sheetTitle']])
|
|
sheetEntity['sheetType'] = Ent.SHEET_ID # Temporarily set addsheet type to ID
|
|
else:
|
|
sheetEntity = updateSheetEntity.copy()
|
|
entityValueList.extend([sheetEntity['sheetType'], sheetEntity['sheetValue']])
|
|
spreadsheet = _getMain().callGAPI(sheet.spreadsheets(), 'get',
|
|
throwReasons=GAPI.SHEETS_ACCESS_THROW_REASONS,
|
|
spreadsheetId=fileId,
|
|
fields='spreadsheetUrl,sheets(properties(sheetId,title),protectedRanges(range(sheetId),requestingUserCanEdit))')
|
|
sheetId = _getMain().getSheetIdFromSheetEntity(spreadsheet, sheetEntity)
|
|
if sheetId is None:
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, Msg.NOT_FOUND, j, jcount)
|
|
continue
|
|
if _getMain().protectedSheetId(spreadsheet, sheetId):
|
|
_getMain().entityActionNotPerformedWarning(entityValueList, Msg.NOT_WRITABLE, j, jcount)
|
|
continue
|
|
if addSheetEntity: # Restore addsheet type
|
|
sheetEntity['sheetType'] = Ent.SHEET
|
|
result = _getMain().callGAPI(drive.files(), 'update',
|
|
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
|
|
GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
|
|
GAPI.CANNOT_MODIFY_VIEWERS_CAN_COPY_CONTENT,
|
|
GAPI.TEAMDRIVES_PARENT_LIMIT,
|
|
GAPI.TEAMDRIVES_FOLDER_MOVE_IN_NOT_SUPPORTED,
|
|
GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED],
|
|
fileId=fileId,
|
|
ocrLanguage=parameters[DFA_OCRLANGUAGE],
|
|
keepRevisionForever=parameters[DFA_KEEP_REVISION_FOREVER],
|
|
useContentAsIndexableText=parameters[DFA_USE_CONTENT_AS_INDEXABLE_TEXT],
|
|
addParents=','.join(addParents), removeParents=','.join(removeParents),
|
|
body=body, fields='id,name,mimeType,webViewLink', supportsAllDrives=True)
|
|
### File size check??
|
|
sbody = {'requests': []}
|
|
if clearFilter and updateSheetEntity:
|
|
sbody['requests'].append({'clearBasicFilter': {'sheetId': sheetId}})
|
|
sbody['requests'].append({'updateCells': {'range': {'sheetId': sheetId}, 'fields': '*'}})
|
|
sbody['requests'].append({'pasteData': {'coordinate': {'sheetId': sheetId, 'rowIndex': '0', 'columnIndex': '0'},
|
|
'data': _getMain().readFile(parameters[DFA_LOCALFILEPATH], encoding=encoding),
|
|
'type': 'PASTE_NORMAL', 'delimiter': columnDelimiter}})
|
|
_getMain().callGAPI(sheet.spreadsheets(), 'batchUpdate',
|
|
throwReasons=GAPI.SHEETS_ACCESS_THROW_REASONS,
|
|
spreadsheetId=fileId, body=sbody)
|
|
if returnIdLink:
|
|
_getMain().writeStdout(f'{result[returnIdLink]}\n')
|
|
else:
|
|
_getMain().entityModifierNewValueActionPerformed([Ent.USER, user, Ent.DRIVE_FILE, result['name'], sheetEntity['sheetType'], sheetEntity['sheetValue']],
|
|
Act.MODIFIER_WITH_CONTENT_FROM, parameters[DFA_LOCALFILENAME], j, jcount)
|
|
except GAPI.fileNotFound as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_ID, fileId], str(e), j, jcount)
|
|
except (GAPI.notFound, GAPI.forbidden, GAPI.permissionDenied,
|
|
GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.badRequest,
|
|
GAPI.invalid, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
|
|
_getMain().entityActionFailedWarning(entityValueList, str(e), j, jcount)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
else:
|
|
result = _getMain().callGAPI(drive.files(), 'update',
|
|
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
|
|
GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
|
|
GAPI.FILE_NEVER_WRITABLE, GAPI.CANNOT_MODIFY_VIEWERS_CAN_COPY_CONTENT,
|
|
GAPI.SHARE_OUT_NOT_PERMITTED, GAPI.SHARE_OUT_NOT_PERMITTED_TO_USER,
|
|
GAPI.TEAMDRIVES_PARENT_LIMIT, GAPI.TEAMDRIVES_FOLDER_MOVE_IN_NOT_SUPPORTED,
|
|
GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
|
|
GAPI.CROSS_DOMAIN_MOVE_RESTRICTION, GAPI.UPLOAD_TOO_LARGE,
|
|
GAPI.TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED,
|
|
GAPI.FILE_OWNER_NOT_MEMBER_OF_WRITER_DOMAIN,
|
|
GAPI.FILE_WRITER_TEAMDRIVE_MOVE_IN_DISABLED],
|
|
fileId=fileId,
|
|
ocrLanguage=parameters[DFA_OCRLANGUAGE],
|
|
keepRevisionForever=parameters[DFA_KEEP_REVISION_FOREVER],
|
|
useContentAsIndexableText=parameters[DFA_USE_CONTENT_AS_INDEXABLE_TEXT],
|
|
addParents=','.join(addParents), removeParents=','.join(removeParents),
|
|
media_body=media_body, body=body, fields='id,name,mimeType,webViewLink',
|
|
supportsAllDrives=True)
|
|
if result:
|
|
if returnIdLink:
|
|
_getMain().writeStdout(f'{result[returnIdLink]}\n')
|
|
elif media_body:
|
|
_getMain().entityModifierNewValueActionPerformed([Ent.USER, user, Ent.DRIVE_FILE, result['name']],
|
|
Act.MODIFIER_WITH_CONTENT_FROM, parameters[DFA_LOCALFILENAME] or parameters[DFA_URL], j, jcount)
|
|
else:
|
|
_getMain().entityActionPerformed([Ent.USER, user, _getMain()._getEntityMimeType(result), result['name']], j, jcount)
|
|
else:
|
|
if returnIdLink:
|
|
_getMain().writeStdout(f'{fileId}\n')
|
|
else:
|
|
_getMain().entityActionPerformed([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER_ID, fileId], j, jcount)
|
|
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions,
|
|
GAPI.unknownError, GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent,
|
|
GAPI.fileNeverWritable, GAPI.cannotModifyViewersCanCopyContent,
|
|
GAPI.shareInNotPermitted, GAPI.shareOutNotPermitted, GAPI.shareOutNotPermittedToUser,
|
|
GAPI.teamDrivesParentLimit, GAPI.teamDrivesFolderMoveInNotSupported,
|
|
GAPI.teamDrivesSharingRestrictionNotAllowed, GAPI.crossDomainMoveRestriction,
|
|
GAPI.uploadTooLarge, GAPI.teamDrivesShortcutFileNotSupported,
|
|
GAPI.fileOwnerNotMemberOfWriterDomain, GAPI.fileWriterTeamDriveMoveInDisabled) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER_ID, fileId], str(e), j, jcount)
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
Ind.Decrement()
|
|
else:
|
|
if newName:
|
|
if parameters[DFA_REPLACEFILENAME]:
|
|
body['name'] = processFilenameReplacements(newName, parameters[DFA_REPLACEFILENAME])
|
|
else:
|
|
body['name'] = newName
|
|
if parameters[DFA_TIMESTAMP]:
|
|
addTimestampToFilename(parameters, body)
|
|
Ind.Increment()
|
|
j = 0
|
|
for fileId in fileIdEntity['list']:
|
|
j += 1
|
|
try:
|
|
result = _getMain().callGAPI(drive.files(), 'copy',
|
|
throwReasons=GAPI.DRIVE_COPY_THROW_REASONS+[GAPI.CANNOT_MODIFY_VIEWERS_CAN_COPY_CONTENT],
|
|
fileId=fileId,
|
|
ignoreDefaultVisibility=parameters[DFA_IGNORE_DEFAULT_VISIBILITY],
|
|
keepRevisionForever=parameters[DFA_KEEP_REVISION_FOREVER],
|
|
body=body, fields='id,name,webViewLink', supportsAllDrives=True)
|
|
if returnIdLink:
|
|
writeReturnIdLink(returnIdLink, parameters[DFA_LOCALMIMETYPE], result)
|
|
else:
|
|
_getMain().entityModifierNewValueItemValueListActionPerformed([Ent.USER, user, Ent.DRIVE_FILE, fileId],
|
|
Act.MODIFIER_TO, result['name'], [Ent.DRIVE_FILE_ID, result['id']], j, jcount)
|
|
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
|
|
GAPI.invalid, GAPI.cannotCopyFile, GAPI.badRequest, GAPI.responsePreparationFailure, GAPI.fileNeverWritable, GAPI.fieldNotWritable,
|
|
GAPI.teamDrivesSharingRestrictionNotAllowed, GAPI.rateLimitExceeded, GAPI.userRateLimitExceeded,
|
|
GAPI.cannotModifyViewersCanCopyContent) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, fileId], str(e), j, jcount)
|
|
except (GAPI.storageQuotaExceeded, GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep,) as e:
|
|
_getMain().entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE, fileId], str(e), j, jcount)
|
|
break
|
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
|
_getMain().userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
|
break
|
|
Ind.Decrement()
|
|
|
|
STAT_FOLDER_TOTAL = 0
|
|
STAT_FOLDER_COPIED_MOVED = 1
|
|
STAT_FOLDER_SHORTCUT_CREATED = 2
|
|
STAT_FOLDER_SHORTCUT_EXISTS = 3
|
|
STAT_FOLDER_MERGED = 4
|
|
STAT_FOLDER_DUPLICATE = 5
|
|
STAT_FOLDER_FAILED = 6
|
|
STAT_FOLDER_NOT_WRITABLE = 7
|
|
STAT_FOLDER_PERMISSIONS_FAILED = 8
|
|
STAT_FILE_TOTAL = 9
|
|
STAT_FILE_COPIED_MOVED = 10
|
|
STAT_FILE_SHORTCUT_CREATED = 11
|
|
STAT_FILE_SHORTCUT_EXISTS = 12
|
|
STAT_FILE_DUPLICATE = 13
|
|
STAT_FILE_FAILED = 14
|
|
STAT_FILE_NOT_COPYABLE_MOVABLE = 15
|
|
STAT_FILE_IN_SKIPIDS = 16
|
|
STAT_FILE_PERMISSIONS_FAILED = 17
|
|
STAT_FILE_PROTECTEDRANGES_FAILED = 18
|
|
STAT_USER_NOT_ORGANIZER = 19
|
|
STAT_LENGTH = 20
|
|
|
|
FOLDER_SUBTOTAL_STATS = [STAT_FOLDER_COPIED_MOVED, STAT_FOLDER_SHORTCUT_CREATED, STAT_FOLDER_SHORTCUT_EXISTS,
|
|
STAT_FOLDER_DUPLICATE, STAT_FOLDER_MERGED, STAT_FOLDER_FAILED, STAT_FOLDER_NOT_WRITABLE]
|
|
FILE_SUBTOTAL_STATS = [STAT_FILE_COPIED_MOVED, STAT_FILE_SHORTCUT_CREATED, STAT_FILE_SHORTCUT_EXISTS,
|
|
STAT_FILE_DUPLICATE, STAT_FILE_FAILED, STAT_FILE_NOT_COPYABLE_MOVABLE, STAT_FILE_IN_SKIPIDS]
|
|
|
|
def _initStatistics():
|
|
return [0] * STAT_LENGTH
|
|
|
|
def _incrStatistic(statistics, stat):
|
|
statistics[stat] += 1
|
|
if stat in FOLDER_SUBTOTAL_STATS:
|
|
statistics[STAT_FOLDER_TOTAL] += 1
|
|
elif stat in FILE_SUBTOTAL_STATS:
|
|
statistics[STAT_FILE_TOTAL] += 1
|
|
|