diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index ec4e52d3..20e76329 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -12,6 +12,10 @@ See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Win ### 6.65.09 +Added option `noduplicate` to `gam create drivefile` that causes GAM +to issue a warning and not perform the create if a non-trashed item with the same name (regardless of MIME type) +exists in the parent folder. + Updated `gam get drivefile ` to handle the following error that seems to occur when multiple tabs from a Google sheet are being downloaded in parallel. ``` diff --git a/docs/Users-Calendars-Access.md b/docs/Users-Calendars-Access.md index d2f8ae6d..71a1bbfb 100644 --- a/docs/Users-Calendars-Access.md +++ b/docs/Users-Calendars-Access.md @@ -98,7 +98,7 @@ gam add calendaracls gam update calendaracls [sendnotifications ] gam delete calendaracls - ] + [] ``` By default, when you add or update a calendar ACL, notification is sent to the members referenced in the ``. Use `sendnotifications false` to suppress sending the notification. diff --git a/docs/Users-Drive-Files-Manage.md b/docs/Users-Drive-Files-Manage.md index 4e71f01c..493cc379 100644 --- a/docs/Users-Drive-Files-Manage.md +++ b/docs/Users-Drive-Files-Manage.md @@ -165,7 +165,7 @@ gam create|add drivefile [(localfile |-)|(url )] [(drivefilename|newfilename ) | (replacefilename )*] - [stripnameprefix ] + [stripnameprefix ] [noduplicate] * [(csv [todrive *] (addcsvdata )*) | (returnidonly|returnlinkonly|returneditlinkonly|showdetails)] @@ -201,6 +201,9 @@ These are the naming rules: If `stripnameprefix ` is specified, `` will be stripped from the front of the Google Drive file name if present. +If `noduplicate` is specfied, GAM will issue a warning and not perform the create if a non-trashed item with the same name (regardless of MIME type) +exists in the parent folder. + By default, when files are uploaded from local content, they are created with `binary` format, i.e., the data is uploaded without any conversion. Standard GAM had an option `convert` that was passed to the Drive API v2 that it used. * convert - Whether to convert this file to the corresponding Docs Editors format diff --git a/src/GamCommands.txt b/src/GamCommands.txt index df688960..9985d81b 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -5902,7 +5902,7 @@ gam print chatmessages [todrive *] create|add drivefile [(localfile |-)|(url )] [(drivefilename|newfilename ) | (replacefilename )*] - [stripnameprefix ] + [stripnameprefix ] [noduplicate] * [(csv [todrive *] (addcsvdata currenttime|)*) | (returnidonly|returnlinkonly|returneditlinkonly|showdetails)] diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index c9e18d13..846dda9f 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -4,6 +4,10 @@ Merged GAM-Team version 6.65.09 +Added option `noduplicate` to `gam create drivefile` that causes GAM +to issue a warning and not perform the create if a non-trashed item with the same name (regardless of MIME type) +exists in the parent folder. + Updated `gam get drivefile ` to handle the following error that seems to occur when multiple tabs from a Google sheet are being downloaded in parallel. ``` diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 6265da61..a049e517 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -9116,10 +9116,10 @@ def doCheckConnection(): def doComment(): writeStdout(Cmd.QuotedArgumentList(Cmd.Remaining())+'\n') -# gam version [check|checkrc|simple|extended] [timeoffset] [location ] +# gam version [check|checkrc|simple|extended] [timeoffset] [nooffseterror] [location ] def doVersion(checkForArgs=True): forceCheck = 0 - extended = timeOffset = simple = False + extended = noOffsetError = timeOffset = simple = False testLocation = GOOGLE_TIMECHECK_LOCATION if checkForArgs: while Cmd.ArgumentsRemaining(): @@ -9134,6 +9134,8 @@ def doVersion(checkForArgs=True): extended = timeOffset = True elif myarg == 'timeoffset': timeOffset = True + elif myarg == 'nooffseterror': + noOffsetError = True elif myarg == 'location': testLocation = getString(Cmd.OB_HOST_NAME) else: @@ -9156,7 +9158,9 @@ def doVersion(checkForArgs=True): offsetSeconds, offsetFormatted = getLocalGoogleTimeOffset(testLocation) printKeyValueList([Msg.YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE.format(testLocation, offsetFormatted)]) if offsetSeconds > MAX_LOCAL_GOOGLE_TIME_OFFSET: - systemErrorExit(NETWORK_ERROR_RC, Msg.PLEASE_CORRECT_YOUR_SYSTEM_TIME) + if not noOffsetError: + systemErrorExit(NETWORK_ERROR_RC, Msg.PLEASE_CORRECT_YOUR_SYSTEM_TIME) + stderrWarningMsg(Msg.PLEASE_CORRECT_YOUR_SYSTEM_TIME) if forceCheck: doGAMCheckForUpdates(forceCheck) if extended: @@ -53738,14 +53742,14 @@ createReturnItemMap = { # [(localfile |-)|(url )] # [(drivefilename|newfilename ) | (replacefilename )*] # [stripnameprefix ] -# * +# * [noduplicate] # [(csv [todrive *] (addcsvdata )*)) | # (returnidonly|returnlinkonly|returneditlinkonly|showdetails)] def createDriveFile(users): csvPF = media_body = None addCSVData = {} returnIdLink = None - showDetails = False + noDuplicate = showDetails = False body = {} newName = None assignLocalName = True @@ -53761,6 +53765,8 @@ def createDriveFile(users): elif myarg == 'showdetails': returnIdLink = None showDetails = True + elif myarg == 'noduplicate': + noDuplicate = True elif myarg == 'csv': csvPF = CSVPrintFile() elif csvPF and myarg == 'todrive': @@ -53804,7 +53810,18 @@ def createDriveFile(users): continue if not _getDriveFileParentInfo(drive, user, i, count, body, parameters): continue + entityType = _getEntityMimeType(body) if 'mimeType' in body else Ent.DRIVE_FILE try: + if noDuplicate: + # Check for existing file/folder, do not duplicate + files = 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: + 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 = 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.CANNOT_ADD_PARENT, @@ -53846,10 +53863,10 @@ def createDriveFile(users): row.update(addCSVData) csvPF.WriteRow(row) except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, - GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent, + GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent, GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed, GAPI.teamDriveHierarchyTooDeep, GAPI.uploadTooLarge, GAPI.teamDrivesShortcutFileNotSupported) as e: - entityActionFailedWarning([Ent.USER, user, Ent.DRIVE_FILE_OR_FOLDER, body['name']], str(e), i, count) + entityActionFailedWarning([Ent.USER, user, entityType, body['name']], str(e), i, count) except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e: userSvcNotApplicableOrDriveDisabled(user, str(e), i, count) if csvPF: