mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-16 20:21:37 +00:00
Compare commits
6 Commits
20231207.0
...
20231212.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bb0088ade | ||
|
|
d113b3ec8e | ||
|
|
97e13b92be | ||
|
|
dc832b8c7f | ||
|
|
56c33fec87 | ||
|
|
48862997b0 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -114,7 +114,7 @@ jobs:
|
||||
path: |
|
||||
bin.tar.xz
|
||||
src/cpython
|
||||
key: gam-${{ matrix.jid }}-202311118
|
||||
key: gam-${{ matrix.jid }}-20231212
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,38 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
||||
|
||||
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation
|
||||
|
||||
### 6.66.12
|
||||
|
||||
Upgraded to Python 3.12.1 where possible.
|
||||
|
||||
Updated all drive commands to handle the following error:
|
||||
```
|
||||
ERROR: 401: Active session is invalid. Error code: 4 - authError
|
||||
```
|
||||
This is due to the Drive SDK API being disabled in the user's OU.
|
||||
* See: https://support.google.com/a/answer/6105699
|
||||
|
||||
### 6.66.11
|
||||
|
||||
Fixed/improved handling of shortcuts in `gam <UserTypeEntity> transfer drive`.
|
||||
|
||||
### 6.66.10
|
||||
|
||||
Updated `gam create datatransfer` to handle the following error:
|
||||
```
|
||||
ERROR: 401: Active session is invalid. Error code: 4 - authError
|
||||
```
|
||||
|
||||
### 6.66.09
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print filelist ... allfields` that caused a trap
|
||||
when `gam.cfg` contained `drive_v3_native_names = False`.
|
||||
|
||||
### 6.66.08
|
||||
|
||||
Added additional columns `isBase` and `baseId` to `gam <UserTypeEntity> print fileparenttree`
|
||||
to simplify processing the output in a script.
|
||||
|
||||
### 6.66.07
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print diskusage` that caused a trap.
|
||||
|
||||
@@ -334,7 +334,7 @@ writes the credentials into the file oauth2.txt.
|
||||
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||
GAMADV-XTD3 6.66.07 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.66.12 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.10.8 64-bit final
|
||||
MacOS High Sierra 10.13.6 x86_64
|
||||
@@ -1002,7 +1002,7 @@ writes the credentials into the file oauth2.txt.
|
||||
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
|
||||
C:\GAMADV-XTD3>gam version
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||
GAMADV-XTD3 6.66.07 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.66.12 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.0 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
|
||||
@@ -585,13 +585,6 @@ gam <UserTypeEntity> delete events <UserCalendarEntity> [doit] [<EventNotificati
|
||||
```
|
||||
No events are deleted unless you specify the `doit` option; omit `doit` to verify that you properly selected the events to delete.
|
||||
|
||||
## Move calendar events to another calendar
|
||||
Generally you won't move all events from one calendar to another; typically, you'll move events created by the event creator
|
||||
using `matchfield creatoremail <RegularExpression>` in conjunction with other `<EventSelectProperty>` and `<EventMatchProperty>` options.
|
||||
```
|
||||
gam <UserTypeEntity> move events <UserCalendarEntity> [<EventEntity>] destination|to <CalendarItem> [<EventNotificationAttribute>]
|
||||
```
|
||||
|
||||
## Empty calendar trash
|
||||
A user signed in to Google Calendar can empty the calendar trash but there is no direct API support for this operation.
|
||||
To empty the calendar trash a temporary calendar is created, the deleted events are moved to the temporary calendar and then the temporary calendar is deleted.
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
## API documentation
|
||||
* https://developers.google.com/drive/api/v3/reference/files
|
||||
* https://support.google.com/a/answer/6105699
|
||||
|
||||
## Definitions
|
||||
* [`<DriveFileEntity>`](Drive-File-Selection)
|
||||
@@ -80,6 +81,7 @@
|
||||
canaddfolderfromanotherdrive|
|
||||
canaddmydriveparent|
|
||||
canchangecopyrequireswriterpermission|
|
||||
canchangecopyrequireswriterpermissionrestriction|
|
||||
canchangedomainusersonlyrestriction|
|
||||
canchangedrivebackground|
|
||||
canchangedrivemembersonlyrestriction|
|
||||
@@ -97,11 +99,14 @@
|
||||
canmanagemembers|
|
||||
canmodifycontent|
|
||||
canmodifycontentrestriction|
|
||||
canmodifyeditorcontentrestriction|
|
||||
canmodifylabels|
|
||||
canmodifyownercontentrestriction|
|
||||
canmovechildrenoutofdrive|
|
||||
canmovechildrenoutofteamdrive|
|
||||
canmovechildrenwithindrive|
|
||||
canmovechildrenwithinteamdrive|
|
||||
canmoveitemintodrive|
|
||||
canmoveitemintoteamdrive|
|
||||
canmoveitemoutofdrive|
|
||||
canmoveitemoutofteamdrive|
|
||||
@@ -113,6 +118,7 @@
|
||||
canreadrevisions|
|
||||
canreadteamdrive|
|
||||
canremovechildren|
|
||||
canremovecontentrestriction|
|
||||
canremovemydriveparent|
|
||||
canrename|
|
||||
canrenamedrive|
|
||||
@@ -1076,6 +1082,12 @@ Use the following option to select a subset of files based on their permissions.
|
||||
* `<PermissionMatch>* [<PermissionMatchAction>]` - Use permission matching to select files
|
||||
|
||||
## File selection starting point for Display file list
|
||||
You can limit the selection for files on a specific Shared drive.
|
||||
Any query will be applied to the Shared drive.
|
||||
```
|
||||
select <SharedDriveEntity>
|
||||
```
|
||||
|
||||
You can specify a specific folder from which to select files.
|
||||
```
|
||||
select <DriveFileEntity> [selectsubquery <QueryDriveFile>]
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
* https://developers.google.com/drive/api/v3/ref-single-parent
|
||||
* https://developers.google.com/drive/api/v3/shared-drives-diffs
|
||||
* https://developers.google.com/drive/api/v3/shortcuts
|
||||
* https://support.google.com/a/answer/6105699
|
||||
* https://support.google.com/a/answer/7374057
|
||||
* https://developers.google.com/docs/api/reference/rest
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
- [Display a selected set of messages](#display-a-selected-set-of-messages)
|
||||
- [Choose information to display](#choose-information-to-display)
|
||||
- [Display message content](#display-message-content)
|
||||
- [Display message count](#display-message-count)
|
||||
- [Display message counts](#display-message-counts)
|
||||
- [Display label counts](#display-label-counts)
|
||||
- [Print only options](#print-only-options)
|
||||
- [Show only options](#show-only-options)
|
||||
@@ -539,7 +539,7 @@ The `dateheaderconverttimezone [<Boolean>]>` option converts `<SMTPDateHeader>`
|
||||
* `showsize` - Display the message size
|
||||
* `showsnippet` - Display the message snippet
|
||||
|
||||
### Display message count and optionally cumulative message size
|
||||
### Display message counts
|
||||
* `countsonly` - Display the count of the number of messages
|
||||
* `showsize` - Display the cumulative message size
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAMADV-XTD3 6.66.07 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.66.12 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.0 64-bit final
|
||||
MacOS Monterey 12.7 x86_64
|
||||
@@ -16,7 +16,7 @@ Time: 2023-06-02T21:10:00-07:00
|
||||
Print the current version of Gam with details and time offset information
|
||||
```
|
||||
gam version timeoffset
|
||||
GAMADV-XTD3 6.66.07 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.66.12 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.0 64-bit final
|
||||
MacOS Monterey 12.7 x86_64
|
||||
@@ -28,7 +28,7 @@ Your system time differs from www.googleapis.com by less than 1 second
|
||||
Print the current version of Gam with extended details and SSL information
|
||||
```
|
||||
gam version extended
|
||||
GAMADV-XTD3 6.66.07 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.66.12 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.0 64-bit final
|
||||
MacOS Monterey 12.7 x86_64
|
||||
@@ -65,7 +65,7 @@ MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 6.66.07
|
||||
Latest: 6.66.12
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -73,7 +73,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
6.66.07
|
||||
6.66.12
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -83,7 +83,7 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 6.66.07 - https://github.com/taers232c/GAMADV-XTD3
|
||||
GAM 6.66.12 - https://github.com/taers232c/GAMADV-XTD3
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.12.0 64-bit final
|
||||
MacOS Monterey 12.7 x86_64
|
||||
|
||||
@@ -6284,6 +6284,7 @@ gam <UserTypeEntity> collect orphans
|
||||
canaddfolderfromanotherdrive|
|
||||
canaddmydriveparent|
|
||||
canchangecopyrequireswriterpermission|
|
||||
canchangecopyrequireswriterpermissionrestriction|
|
||||
canchangedomainusersonlyrestriction|
|
||||
canchangedrivebackground|
|
||||
canchangedrivemembersonlyrestriction|
|
||||
@@ -6301,11 +6302,14 @@ gam <UserTypeEntity> collect orphans
|
||||
canmanagemembers|
|
||||
canmodifycontent|
|
||||
canmodifycontentrestriction|
|
||||
canmodifyeditorcontentrestriction|
|
||||
canmodifylabels|
|
||||
canmodifyownercontentrestriction|
|
||||
canmovechildrenoutofdrive|
|
||||
canmovechildrenoutofteamdrive|
|
||||
canmovechildrenwithindrive|
|
||||
canmovechildrenwithinteamdrive|
|
||||
canmoveitemintodrive|
|
||||
canmoveitemintoteamdrive|
|
||||
canmoveitemoutofdrive|
|
||||
canmoveitemoutofteamdrive|
|
||||
@@ -6317,6 +6321,7 @@ gam <UserTypeEntity> collect orphans
|
||||
canreadrevisions|
|
||||
canreadteamdrive|
|
||||
canremovechildren|
|
||||
canremovecontentrestriction|
|
||||
canremovemydriveparent|
|
||||
canrename|
|
||||
canrenamedrive|
|
||||
|
||||
@@ -2,6 +2,38 @@
|
||||
|
||||
Merged GAM-Team version
|
||||
|
||||
6.66.12
|
||||
|
||||
Upgraded to Python 3.12.1 where possible.
|
||||
|
||||
Updated all drive commands to handle the following error:
|
||||
```
|
||||
ERROR: 401: Active session is invalid. Error code: 4 - authError
|
||||
```
|
||||
This is due to the Drive SDK API being disabled in the user's OU.
|
||||
* See: https://support.google.com/a/answer/6105699
|
||||
|
||||
6.66.11
|
||||
|
||||
Fixed/improved handling of shortcuts in `gam <UserTypeEntity> transfer drive`.
|
||||
|
||||
6.66.10
|
||||
|
||||
Updated `gam create datatransfer` to handle the following error:
|
||||
```
|
||||
ERROR: 401: Active session is invalid. Error code: 4 - authError
|
||||
```
|
||||
|
||||
6.66.09
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print filelist ... allfields` that caused a trap
|
||||
when `gam.cfg` contained `drive_v3_native_names = False`.
|
||||
|
||||
6.66.08
|
||||
|
||||
Added additional columns `isBase` and `baseId` to `gam <UserTypeEntity> print fileparenttree`
|
||||
to simplify processing the output in a script.
|
||||
|
||||
6.66.07
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print diskusage` that caused a trap.
|
||||
|
||||
@@ -2521,7 +2521,7 @@ def entityBadRequestWarning(entityValueList, errMessage, i=0, count=0):
|
||||
currentCountNL(i, count)))
|
||||
|
||||
def userSvcNotApplicableOrDriveDisabled(user, errMessage, i=0, count=0):
|
||||
if errMessage.find('Drive apps') == -1:
|
||||
if errMessage.find('Drive apps') == -1 and errMessage.find('Active session is invalid') == -1:
|
||||
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
|
||||
else:
|
||||
entityActionNotPerformedWarning([Ent.USER, user], errMessage, i, count)
|
||||
@@ -5082,15 +5082,16 @@ def checkGAPIError(e, softErrors=False, retryOnHttpError=False, mapNotFound=True
|
||||
systemErrorExit(HTTP_ERROR_RC, eContent)
|
||||
if 'error' in error:
|
||||
http_status = error['error']['code']
|
||||
reason = ''
|
||||
if 'errors' in error['error'] and 'message' in error['error']['errors'][0]:
|
||||
message = error['error']['errors'][0]['message']
|
||||
status = ''
|
||||
if 'reason' in error['error']['errors'][0]:
|
||||
reason = error['error']['errors'][0]['reason']
|
||||
elif 'errors' in error['error'] and 'Unknown Error' in error['error']['message'] and 'reason' in error['error']['errors'][0]:
|
||||
message = error['error']['errors'][0]['reason']
|
||||
status = error['error'].get('status', '')
|
||||
else:
|
||||
message = error['error']['message']
|
||||
status = error['error'].get('status', '')
|
||||
status = error['error'].get('status', '')
|
||||
lmessage = message.lower() if message is not None else ''
|
||||
if http_status == 500:
|
||||
if not lmessage or status == 'UNKNOWN':
|
||||
@@ -5109,30 +5110,36 @@ def checkGAPIError(e, softErrors=False, retryOnHttpError=False, mapNotFound=True
|
||||
error = makeErrorDict(http_status, GAPI.OPERATION_NOT_SUPPORTED, message)
|
||||
elif 'failed status in update settings response' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.INVALID_INPUT, message)
|
||||
elif status == 'INTERNAL':
|
||||
error = makeErrorDict(http_status, GAPI.INTERNAL_ERROR, message)
|
||||
elif 'cannot delete a field in use.resource.fields' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.FIELD_IN_USE, message)
|
||||
elif status == 'INTERNAL':
|
||||
error = makeErrorDict(http_status, GAPI.INTERNAL_ERROR, message)
|
||||
elif http_status == 502:
|
||||
if 'bad gateway' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.BAD_GATEWAY, message)
|
||||
elif http_status == 503:
|
||||
if status == 'UNAVAILABLE' or 'the service is currently unavailable' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.SERVICE_NOT_AVAILABLE, message)
|
||||
elif message.startswith('quota exceeded for the current request'):
|
||||
if message.startswith('quota exceeded for the current request'):
|
||||
error = makeErrorDict(http_status, GAPI.QUOTA_EXCEEDED, message)
|
||||
elif status == 'UNAVAILABLE' or 'the service is currently unavailable' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.SERVICE_NOT_AVAILABLE, message)
|
||||
elif http_status == 504:
|
||||
if 'gateway timeout' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.GATEWAY_TIMEOUT, message)
|
||||
elif http_status == 400:
|
||||
if '@attachmentnotvisible' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.BAD_REQUEST, message)
|
||||
elif 'does not match' in lmessage or 'invalid' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.INVALID, message)
|
||||
elif status == 'FAILED_PRECONDITION' or 'precondition check failed' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.FAILED_PRECONDITION, message)
|
||||
elif status == 'INVALID_ARGUMENT':
|
||||
error = makeErrorDict(http_status, GAPI.INVALID_ARGUMENT, message)
|
||||
elif 'does not match' in lmessage or 'invalid' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.INVALID, message)
|
||||
elif http_status == 401:
|
||||
if 'active session is invalid' in lmessage and reason == 'authError':
|
||||
message += ' Drive SDK API access disabled'
|
||||
error = makeErrorDict(http_status, GAPI.AUTH_ERROR, message)
|
||||
elif status == 'PERMISSION_DENIED':
|
||||
error = makeErrorDict(http_status, GAPI.PERMISSION_DENIED, message)
|
||||
elif http_status == 403:
|
||||
if 'quota exceeded for quota metric' in lmessage:
|
||||
error = makeErrorDict(http_status, GAPI.QUOTA_EXCEEDED, message)
|
||||
@@ -7695,7 +7702,6 @@ class CSVPrintFile():
|
||||
|
||||
def MapDrive3TitlesToDrive2(self):
|
||||
_mapDrive3TitlesToDrive2(self.titlesList, API.DRIVE3_TO_DRIVE2_FILES_FIELDS_MAP)
|
||||
_mapDrive3TitlesToDrive2(self.titlesList, API.DRIVE3_TO_DRIVE2_CAPABILITIES_TITLES_MAP)
|
||||
self.titlesSet = set(self.titlesList)
|
||||
|
||||
def AddJSONTitle(self, title):
|
||||
@@ -50979,6 +50985,7 @@ DRIVE_CAPABILITIES_SUBFIELDS_CHOICE_MAP = {
|
||||
'canaddfolderfromanotherdrive': 'canAddFolderFromAnotherDrive',
|
||||
'canaddmydriveparent': 'canAddMyDriveParent',
|
||||
'canchangecopyrequireswriterpermission': 'canChangeCopyRequiresWriterPermission',
|
||||
'canchangecopyrequireswriterpermissionrestriction': 'canChangeCopyRequiresWriterPermissionRestriction',
|
||||
'canchangedomainusersonlyrestriction': 'canChangeDomainUsersOnlyRestriction',
|
||||
'canchangedrivebackground': 'canChangeDriveBackground',
|
||||
'canchangedrivemembersonlyrestriction': 'canChangeDriveMembersOnlyRestriction',
|
||||
@@ -50996,22 +51003,26 @@ DRIVE_CAPABILITIES_SUBFIELDS_CHOICE_MAP = {
|
||||
'canmanagemembers': 'canManageMembers',
|
||||
'canmodifycontent': 'canModifyContent',
|
||||
'canmodifycontentrestriction': 'canModifyContentRestriction',
|
||||
'canmodifyeditorcontentrestriction': 'canModifyEditorContentRestriction',
|
||||
'canmodifylabels': 'canModifyLabels',
|
||||
'canmodifyownercontentrestriction': 'canModifyOwnerContentRestriction',
|
||||
'canmovechildrenoutofdrive': 'canMoveChildrenOutOfDrive',
|
||||
'canmovechildrenoutofteamdrive': 'canMoveChildrenOutOfDrive',
|
||||
'canmovechildrenwithindrive': 'canMoveChildrenWithinDrive',
|
||||
'canmovechildrenwithinteamdrive': 'canMoveChildrenWithinDrive',
|
||||
'canmoveitemintoteamdrive': 'canMoveItemOutOfDrive',
|
||||
'canmoveitemintodrive': 'canMoveItemIntoDrive',
|
||||
'canmoveitemintoteamdrive': 'canMoveItemIntoDrive',
|
||||
'canmoveitemoutofdrive': 'canMoveItemOutOfDrive',
|
||||
'canmoveitemoutofteamdrive': 'canMoveItemOutOfDrive',
|
||||
'canmoveitemwithindrive': 'canMoveItemWithinDrive',
|
||||
'canmoveitemwithinteamdrive': 'canMoveItemWithinDrive',
|
||||
'canmoveteamdriveitem': ['canMoveItemOutOfDrive', 'canMoveItemWithinDrive'],
|
||||
'canmoveteamdriveitem': 'canMoveTeamDriveItem',
|
||||
'canreaddrive': 'canReadDrive',
|
||||
'canreadlabels': 'canReadLabels',
|
||||
'canreadrevisions': 'canReadRevisions',
|
||||
'canreadteamdrive': 'canReadDrive',
|
||||
'canremovechildren': 'canRemoveChildren',
|
||||
'canremovecontentrestriction': 'canRemoveContentRestriction',
|
||||
'canremovemydriveparent': 'canRemoveMyDriveParent',
|
||||
'canrename': 'canRename',
|
||||
'canrenamedrive': 'canRenameDrive',
|
||||
@@ -53344,7 +53355,7 @@ def printShowFilePaths(users):
|
||||
# [stripcrsfromname]
|
||||
def printFileParentTree(users):
|
||||
fileNameTitle = 'title' if not GC.Values[GC.DRIVE_V3_NATIVE_NAMES] else 'name'
|
||||
csvPF = CSVPrintFile(['Owner', 'id', fileNameTitle, 'parentId', 'depth', 'isRoot'], 'sortall')
|
||||
csvPF = CSVPrintFile(['Owner', 'isBase', 'baseId', 'id', fileNameTitle, 'parentId', 'depth', 'isRoot'], 'sortall')
|
||||
fileIdEntity = getDriveFileEntity()
|
||||
stripCRsFromName = False
|
||||
while Cmd.ArgumentsRemaining():
|
||||
@@ -53372,6 +53383,7 @@ def printFileParentTree(users):
|
||||
for fileId in fileIdEntity['list']:
|
||||
j += 1
|
||||
fileList = []
|
||||
baseId = fileId
|
||||
while True:
|
||||
try:
|
||||
result = callGAPI(drive.files(), 'get',
|
||||
@@ -53401,9 +53413,11 @@ def printFileParentTree(users):
|
||||
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
|
||||
break
|
||||
kcount = len(fileList)
|
||||
isBase = True
|
||||
for result in fileList:
|
||||
csvPF.WriteRow({'Owner': user, 'id': result['id'], fileNameTitle: result['name'], 'parentId': result['parents'][0],
|
||||
'depth': kcount, 'isRoot': result['isRoot']})
|
||||
csvPF.WriteRow({'Owner': user, 'isBase': isBase, 'baseId': baseId, 'id': result['id'], fileNameTitle: result['name'],
|
||||
'parentId': result['parents'][0], 'depth': kcount, 'isRoot': result['isRoot']})
|
||||
isBase = False
|
||||
kcount -= 1
|
||||
csvPF.writeCSVfile('Drive File Parent Tree')
|
||||
|
||||
@@ -57911,17 +57925,55 @@ def transferDrive(users):
|
||||
entityModifierItemValueListActionPerformed(kvList, Act.MODIFIER_IN,
|
||||
[Ent.DRIVE_FOLDER, newParentId, targetEntityType, f"{childName}({result['id']})"],
|
||||
j, jcount)
|
||||
Act.Set(action)
|
||||
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, GAPI.invalid, GAPI.badRequest,
|
||||
GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed,
|
||||
GAPI.teamDriveHierarchyTooDeep, GAPI.shortcutTargetInvalid) as e:
|
||||
entityActionFailedWarning(kvList+[Ent.DRIVE_FILE_SHORTCUT, childName], str(e), j, jcount)
|
||||
Act.Set(action)
|
||||
|
||||
# Recreate source user shortcut in target user
|
||||
def _transferShortcut(j, jcount, childEntryInfo, childId, childName, newParentId):
|
||||
entityType = Ent.DRIVE_FOLDER_SHORTCUT if childEntryInfo['shortcutDetails']['targetMimeType'] == MIMETYPE_GA_FOLDER else Ent.DRIVE_FILE_SHORTCUT
|
||||
kvList = [Ent.USER, sourceUser, entityType, f'{childName}({childId})']
|
||||
action = Act.Get()
|
||||
body = {'name': childName, 'mimeType': MIMETYPE_GA_SHORTCUT,
|
||||
'parents': [newParentId], 'shortcutDetails': {'targetId': childEntryInfo['shortcutDetails']['targetId']}}
|
||||
Act.Set(Act.RECREATE)
|
||||
try:
|
||||
result = 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_HIERARCHY_TOO_DEEP, GAPI.SHORTCUT_TARGET_INVALID],
|
||||
body=body, fields='id', supportsAllDrives=True)
|
||||
shortcutId = result['id']
|
||||
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_IN, None, [Ent.USER, targetUser,
|
||||
Ent.DRIVE_FOLDER, newParentId, entityType, f"{shortcutId})"],
|
||||
j, jcount)
|
||||
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, GAPI.invalid, GAPI.badRequest,
|
||||
GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed,
|
||||
GAPI.teamDriveHierarchyTooDeep, GAPI.shortcutTargetInvalid) as e:
|
||||
entityActionFailedWarning(kvList+[Ent.DRIVE_FILE_SHORTCUT, childName], str(e), j, jcount)
|
||||
Act.Set(action)
|
||||
return
|
||||
if ownerRetainRoleBody['role'] == 'none':
|
||||
Act.Set(Act.DELETE_SHORTCUT)
|
||||
kvList = [Ent.USER, sourceUser, entityType, f'{childName}({childId})']
|
||||
try:
|
||||
callGAPI(sourceDrive.files(), 'delete',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
|
||||
fileId=childId, supportsAllDrives=True)
|
||||
entityActionPerformed(kvList, j, jcount)
|
||||
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError, GAPI.fileNeverWritable) as e:
|
||||
entityActionFailedWarning(kvList, str(e), j, jcount)
|
||||
Act.Set(action)
|
||||
|
||||
def _transferFile(childEntry, i, count, j, jcount, atSelectTop):
|
||||
childEntryInfo = childEntry['info']
|
||||
childFileId = childEntryInfo['id']
|
||||
childFileName = childEntryInfo['name']
|
||||
childFileType = _getEntityMimeType(childEntryInfo)
|
||||
# Owned files
|
||||
if childEntryInfo['ownedByMe']:
|
||||
childEntryInfo['sourcePermission'] = {'role': 'owner'}
|
||||
for permission in childEntryInfo.get('permissions', []):
|
||||
@@ -57967,20 +58019,25 @@ def transferDrive(users):
|
||||
GAPI.PERMISSION_NOT_FOUND, GAPI.SHARING_RATE_LIMIT_EXCEEDED],
|
||||
fileId=childFileId, permissionId=targetPermissionId,
|
||||
transferOwnership=True, body={'role': 'owner'}, fields='')
|
||||
if removeSourceParents:
|
||||
op = 'Remove Source Parents'
|
||||
callGAPI(sourceDrive.files(), 'update',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS, retryReasons=[GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND], triesLimit=3,
|
||||
fileId=childFileId, removeParents=','.join(removeSourceParents), fields='')
|
||||
actionUser = targetUser
|
||||
if addTargetParent or removeTargetParents:
|
||||
op = 'Add/Remove Target Parents'
|
||||
callGAPI(targetDrive.files(), 'update',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
|
||||
retryReasons=[GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND], triesLimit=3,
|
||||
fileId=childFileId,
|
||||
addParents=addTargetParent, removeParents=','.join(removeTargetParents), fields='')
|
||||
entityModifierNewValueItemValueListActionPerformed([Ent.USER, sourceUser, childFileType, childFileName], Act.MODIFIER_TO, None, [Ent.USER, targetUser], j, jcount)
|
||||
if removeSourceParents:
|
||||
op = 'Remove Source Parents'
|
||||
callGAPI(sourceDrive.files(), 'update',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS, retryReasons=[GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND], triesLimit=3,
|
||||
fileId=childFileId, removeParents=','.join(removeSourceParents), fields='')
|
||||
actionUser = targetUser
|
||||
if addTargetParent or removeTargetParents:
|
||||
op = 'Add/Remove Target Parents'
|
||||
callGAPI(targetDrive.files(), 'update',
|
||||
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.INSUFFICIENT_PARENT_PERMISSIONS],
|
||||
retryReasons=[GAPI.BAD_REQUEST, GAPI.FILE_NOT_FOUND], triesLimit=3,
|
||||
fileId=childFileId,
|
||||
addParents=addTargetParent, removeParents=','.join(removeTargetParents), fields='')
|
||||
entityModifierNewValueItemValueListActionPerformed([Ent.USER, sourceUser, childFileType, childFileName], Act.MODIFIER_TO, None, [Ent.USER, targetUser], j, jcount)
|
||||
else:
|
||||
if topSourceId in childParents:
|
||||
_transferShortcut(j, jcount, childEntryInfo, childFileId, childFileName, addTargetParent)
|
||||
else:
|
||||
entityModifierNewValueItemValueListActionPerformed([Ent.USER, sourceUser, childFileType, childFileName], Act.MODIFIER_TO, None, [Ent.USER, targetUser], j, jcount)
|
||||
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.unknownError,
|
||||
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.insufficientParentPermissions) as e:
|
||||
entityActionFailedWarning([Ent.USER, actionUser, childFileType, childFileName], f'{op}: {str(e)}', j, jcount)
|
||||
@@ -57995,6 +58052,7 @@ def transferDrive(users):
|
||||
entityActionFailedWarning([Ent.USER, actionUser, childFileType, childFileName], Ent.TypeNameMessage(Ent.PERMISSION_ID, targetPermissionId, str(e)), j, jcount)
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
userSvcNotApplicableOrDriveDisabled(actionUser, str(e), i, count)
|
||||
# Non-owned files
|
||||
else:
|
||||
Act.Set(Act.PROCESS)
|
||||
for permission in childEntryInfo.get('permissions', []):
|
||||
@@ -58137,6 +58195,10 @@ def transferDrive(users):
|
||||
childFileId = childEntryInfo['id']
|
||||
childFileName = childEntryInfo['name']
|
||||
childFileType = _getEntityMimeType(childEntryInfo)
|
||||
if childEntryInfo['mimeType'] == MIMETYPE_GA_SHORTCUT:
|
||||
if showRetentionMessages:
|
||||
entityActionNotPerformedWarning([Ent.USER, sourceUser, childFileType, childFileName, Ent.ROLE, ownerRetainRoleBody['role']], Msg.NOT_APPROPRIATE, j, jcount)
|
||||
return
|
||||
if childEntryInfo['ownedByMe']:
|
||||
try:
|
||||
if ownerRetainRoleBody['role'] != 'none':
|
||||
@@ -58285,7 +58347,7 @@ def transferDrive(users):
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
||||
retryReasons=[GAPI.UNKNOWN_ERROR],
|
||||
orderBy=OBY.orderBy, q=WITH_PARENTS.format(fileId),
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId),permissions(id,role))',
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId),permissions(id,role),shortcutDetails)',
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
userSvcNotApplicableOrDriveDisabled(sourceUser, str(e), i, count)
|
||||
@@ -58504,6 +58566,7 @@ def transferDrive(users):
|
||||
return
|
||||
Ind.Increment()
|
||||
if buildTree:
|
||||
topSourceId = sourceRootId
|
||||
parentIdMap = {sourceRootId: targetIds[TARGET_PARENT_ID]}
|
||||
printGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, Ent.TypeName(Ent.SOURCE_USER, user), i, count)
|
||||
feed = callGAPIpages(sourceDrive.files(), 'list', 'files',
|
||||
@@ -58511,7 +58574,7 @@ def transferDrive(users):
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
||||
retryReasons=[GAPI.UNKNOWN_ERROR],
|
||||
orderBy=OBY.orderBy, q=NON_TRASHED,
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,owners(emailAddress,permissionId),permissions(id,role))',
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,owners(emailAddress,permissionId),permissions(id,role),shortcutDetails)',
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
||||
fileTree = buildFileTree(feed, sourceDrive)
|
||||
del feed
|
||||
@@ -58538,7 +58601,7 @@ def transferDrive(users):
|
||||
fileEntry = callGAPI(sourceDrive.files(), 'get',
|
||||
throwReasons=GAPI.DRIVE_GET_THROW_REASONS,
|
||||
fileId=fileId,
|
||||
fields='id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId),permissions(id,role)')
|
||||
fields='id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId),permissions(id,role),shortcutDetails')
|
||||
entityType = _getEntityMimeType(fileEntry)
|
||||
if fileId in skipFileIdEntity['list']:
|
||||
entityActionNotPerformedWarning([Ent.USER, sourceUser, entityType, f'{fileEntry["name"]} ({fileId})'],
|
||||
@@ -58546,9 +58609,11 @@ def transferDrive(users):
|
||||
continue
|
||||
entityPerformActionItemValue([Ent.USER, sourceUser], entityType, f'{fileEntry["name"]} ({fileId})', j, jcount)
|
||||
if not mergeWithTarget:
|
||||
topSourceId = None
|
||||
for parentId in fileEntry.get('parents', []):
|
||||
parentIdMap[parentId] = targetIds[TARGET_PARENT_ID]
|
||||
else:
|
||||
topSourceId = fileId
|
||||
parentIdMap[fileId] = targetIds[TARGET_PARENT_ID]
|
||||
_identifyDriveFileAndChildren(fileEntry, i, count)
|
||||
filesTransferred = set()
|
||||
@@ -66825,7 +66890,8 @@ def printShowMessagesThreads(users, entityType):
|
||||
if senderMatchPattern:
|
||||
row['Sender'] = sender
|
||||
if not show_size:
|
||||
labelsMap.pop('size', None)
|
||||
for label in labelsMap.values():
|
||||
label.pop('size', None)
|
||||
csvPF.WriteRowTitles(flattenJSON({'Labels': sorted(iter(labelsMap.values()), key=lambda k: k['name'])}, flattened=row))
|
||||
elif not senderMatchPattern:
|
||||
if not csvPF:
|
||||
|
||||
@@ -49,6 +49,7 @@ class GamAction():
|
||||
DELETE = 'dele'
|
||||
DELETE_EMPTY = 'delm'
|
||||
DELETE_PREVIEW = 'delp'
|
||||
DELETE_SHORTCUT = 'desc'
|
||||
DEPROVISION = 'depr'
|
||||
DISABLE = 'disa'
|
||||
DOWNLOAD = 'down'
|
||||
@@ -160,11 +161,12 @@ class GamAction():
|
||||
COPY_MERGE: ['Copied(Merge)', 'Copy(Merge)'],
|
||||
CREATE: ['Created', 'Create'],
|
||||
CREATE_PREVIEW: ['Created (Preview)', 'Create (Preview)'],
|
||||
CREATE_SHORTCUT: ['Created Shortcut', 'Create SHORTCUT'],
|
||||
CREATE_SHORTCUT: ['Created Shortcut', 'Create Shortcut'],
|
||||
DEDUP: ['Duplicates Deleted', 'Delete Duplicates'],
|
||||
DELETE: ['Deleted', 'Delete'],
|
||||
DELETE_EMPTY: ['Deleted', 'Delete Empty'],
|
||||
DELETE_PREVIEW: ['Deleted (Preview)', 'Delete (Preview)'],
|
||||
DELETE_SHORTCUT: ['Deleted Shortcut', 'Delete Shortcut'],
|
||||
DEPROVISION: ['Deprovisioned', 'Deprovision'],
|
||||
DISABLE: ['Disabled', 'Disable'],
|
||||
DOWNLOAD: ['Downloaded', 'Download'],
|
||||
|
||||
@@ -691,14 +691,6 @@ DRIVE3_TO_DRIVE2_CAPABILITIES_NAMES_MAP = {
|
||||
'canChangeViewersCanCopyContent': 'canChangeRestrictedDownload',
|
||||
}
|
||||
|
||||
DRIVE3_TO_DRIVE2_CAPABILITIES_TITLES_MAP = {
|
||||
'capabilities.canComment': 'canComment',
|
||||
'capabilities.canReadRevisions': 'canReadRevisions',
|
||||
'capabilities.canCopy': 'copyable',
|
||||
'capabilities.canEdit': 'editable',
|
||||
'capabilities.canShare': 'shareable',
|
||||
}
|
||||
|
||||
DRIVE3_TO_DRIVE2_FILES_FIELDS_MAP = {
|
||||
'allowFileDiscovery': 'withLink',
|
||||
'createdTime': 'createdDate',
|
||||
|
||||
@@ -338,6 +338,7 @@ NOT_A_MEMBER = 'Not a member'
|
||||
NOT_ACTIVE = 'Not Active'
|
||||
NOT_ALLOWED = 'Not Allowed'
|
||||
NOT_AN_ENTITY = 'Not a {0}'
|
||||
NOT_APPROPRIATE = 'Not Appropriate'
|
||||
NOT_COMPATIBLE = 'Not Compatible'
|
||||
NOT_COPYABLE = 'Not Copyable'
|
||||
NOT_COPYABLE_INTO_ITSELF = 'Not copyable into itself'
|
||||
|
||||
Reference in New Issue
Block a user