diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index 9e3c7da0..92e33ccd 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -10,6 +10,14 @@ 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.76.05 + +Added options `deletefromoldowner`, `addtonewowner *` and `nolistmessages` +to `gam transfer calendars ` that allow manipulation of the +source and target user's calendar lists. + +* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Calendars-Access#transfer-calendar-ownership + ### 6.76.04 Added the following fields to ``: diff --git a/docs/How-to-Upgrade-from-Standard-GAM.md b/docs/How-to-Upgrade-from-Standard-GAM.md index a065c519..836a6210 100644 --- a/docs/How-to-Upgrade-from-Standard-GAM.md +++ b/docs/How-to-Upgrade-from-Standard-GAM.md @@ -335,7 +335,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.76.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -1009,7 +1009,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.76.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final Windows-10-10.0.17134 AMD64 diff --git a/docs/Users-Calendars-Access.md b/docs/Users-Calendars-Access.md index 71a1bbfb..de13c148 100644 --- a/docs/Users-Calendars-Access.md +++ b/docs/Users-Calendars-Access.md @@ -37,6 +37,17 @@ Calendar ACL roles (as seen in Calendar GUI): ::= id: ::= || + ::= + (backgroundcolor )| + (color )| + (colorindex|colorid )| + (foregroundcolor )| + (hidden )| + (notification clear|(email ))| + (reminder clear|(email|pop )|( email|pop))| + (selected )| + (summary ) + ::= (description )| (location )| @@ -134,15 +145,15 @@ The `quotechar ` option allows you to choose an alternate quote chara ## Transfer calendar ownership You can transfer ownership of calendars from one user to another; only non-primary calendars owned by the source user can be transferred. -You can update calendar settings as part of the transfer. In description, location and summary, #email#, #user# and #username# will be replaced -by the original owner's full email address or just the name portion; #timestamp# will be replaced by the current date and time. ``` -gam transfer calendars +gam transfer calendars|seccals [] [keepuser | (retainrole )] [sendnotifications ] [noretentionmessages] [] [append description|location|summary] [noupdatemessages] -gam transfer seccals [keepuser] [sendnotifications ] + [deletefromoldowner] [addtonewowner *] [nolistmessages] ``` +If `` is not specified, all of a user's owned secondary calendars will be transferrdd. + By default, the users in `` retain no role in the transferred calendars. * `keepuser` - The users in `` retain their ownership. * `retainrole ` - The users in `` retain the specified role. @@ -150,11 +161,23 @@ By default, the users in `` retain no role in the transferred ca By default, when you add or update a calendar ACL, a notification is sent to the affected users; use `sendnotifications false` to suppress sending the notifications. +You can update calendar settings as part of the transfer. In description, location and summary, #email#, #user# and #username# will be replaced +by the original owner's full email address or just the name portion; #timestamp# will be replaced by the current date and time. * `` - The value specified will replace the existing value. * `append description|location|summary` - The specified value will be appended to the existing value. * `noupdatemessages` - Suppress the settings update messages. +You can manipulate the old and new owner's calendar lists. +* `deletefromoldowner` - Delete the calendar from the old owner's calendar list +* `addtonewowner *` - Add the calendar to the new owner's calendar list; optionally specify attributes +* `nolistmessages` - Suppress the calendar list add/delete messages. + ### Example +Transfer a secondary calendar from oldowner to newowner. Remove the calendar from the old owner's calendar list and add to the new owner's calendar list. +``` +gam user oldowner@domain.com transfer calendars newowner@domain.com c_aaa123zzz@group.calendar.google.com removefromoldowner addtonewowner +``` + Transfer ownership of all non-primary calendars from oldowner to newowner; append a message to the calendar description noting the old owner and the time of transfer. ``` gam user oldowner@domain.com transfer calendars newowner@domain.com minaccessrole owner description "(Transferred from #user# on #timestamp#)" append description diff --git a/docs/Version-and-Help.md b/docs/Version-and-Help.md index dd160e60..3714ba8b 100644 --- a/docs/Version-and-Help.md +++ b/docs/Version-and-Help.md @@ -3,7 +3,7 @@ Print the current version of Gam with details ``` gam version -GAMADV-XTD3 6.76.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -15,7 +15,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.76.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -27,7 +27,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.76.04 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource +GAMADV-XTD3 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 @@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64 Path: /Users/Admin/bin/gamadv-xtd3 Version Check: Current: 5.35.08 - Latest: 6.76.04 + Latest: 6.76.05 echo $? 1 ``` @@ -72,7 +72,7 @@ echo $? Print the current version number without details ``` gam version simple -6.76.04 +6.76.05 ``` In Linux/MacOS you can do: ``` @@ -82,7 +82,7 @@ echo $VER Print the current version of Gam and address of this Wiki ``` gam help -GAM 6.76.04 - https://github.com/taers232c/GAMADV-XTD3 +GAM 6.76.05 - https://github.com/taers232c/GAMADV-XTD3 Ross Scroggs Python 3.12.3 64-bit final MacOS Sonoma 14.4.1 x86_64 diff --git a/src/GamCommands.txt b/src/GamCommands.txt index a4a27c53..9dd49fc8 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -5701,12 +5701,12 @@ gam print calendaracls [todrive )* [formatjson [quotechar ]] -Transfer ownership of a selection of users calendars to another user +Transfer ownership of a selection of a users secondary calendars to another user -gam transfer calendars +gam transfer calendars|seccals [] [keepuser | (retainrole )] [sendnotifications ] [noretentionmessages] - ] [append description|location|summary] [noupdatemessages] -gam transfer seccals [keepuser] [sendnotifications ] + * [append description|location|summary] [noupdatemessages] + [deletefromoldowner] [addtonewowner *] [nolistmessages] ::= optional|required ::= accepted|declined|needsaction|tentative diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index 370cc5bd..f18a7709 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -2,6 +2,14 @@ Merged GAM-Team version +6.76.05 + +Added options `deletefromoldowner`, `addtonewowner *` and `nolistmessages` +to `gam transfer calendars ` that allow manipulation of the +old and new owners's calendar lists. + +* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Calendars-Access#transfer-calendar-ownership + 6.76.04 Added the following fields to ``: diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 0eb75ac5..abcb5f5a 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -48357,8 +48357,7 @@ CALENDAR_NOTIFICATION_TYPES_MAP = { 'agenda': 'agenda', } -def _getCalendarAttributes(body): - colorRgbFormat = False +def _getCalendarAttributes(body, returnOnUnknownArgument=False): while Cmd.ArgumentsRemaining(): myarg = getArgument() if myarg == 'selected': @@ -48374,10 +48373,8 @@ def _getCalendarAttributes(body): elif myarg in {'backgroundcolor', 'backgroundcolour'}: body['backgroundColor'] = getColor() body.setdefault('foregroundColor', '#000000') - colorRgbFormat = True elif myarg in {'foregroundcolor', 'foregroundcolour'}: body['foregroundColor'] = getColor() - colorRgbFormat = True elif myarg == 'reminder': body.setdefault('defaultReminders', []) if not checkArgumentPresent(Cmd.CLEAR_NONE_ARGUMENT): @@ -48394,9 +48391,11 @@ def _getCalendarAttributes(body): invalidChoiceExit(ntype, CALENDAR_NOTIFICATION_TYPES_MAP, True) else: body['notificationSettings']['notifications'] = [] + elif returnOnUnknownArgument: + Cmd.Backup() + return else: unknownArgumentExit() - return colorRgbFormat def _showCalendar(calendar, j, jcount, FJQC, acls=None): if FJQC.formatJSON: @@ -48453,11 +48452,12 @@ def _processCalendarList(user, calId, j, jcount, cal, function, **kwargs): GAPI.cannotChangeOwnAcl, GAPI.cannotChangeOwnPrimarySubscription) as e: entityActionFailedWarning([Ent.USER, user, Ent.CALENDAR, calId], str(e), j, jcount) -# gam add calendars +# gam add calendars * def addCalendars(users): calendarEntity = getUserCalendarEntity() body = {'selected': True, 'hidden': False} - colorRgbFormat = _getCalendarAttributes(body) + _getCalendarAttributes(body) + colorRgbFormat = 'backgroundColor' in body or 'foregroundColor' in body i, count, users = getEntityArgument(users) for user in users: i += 1 @@ -48489,11 +48489,12 @@ def _updateDeleteCalendars(users, calendarEntity, function, **kwargs): calendarId=calId, **kwargs) Ind.Decrement() -# gam update calendars +# gam update calendars + def updateCalendars(users): calendarEntity = getUserCalendarEntity() body = {} - colorRgbFormat = _getCalendarAttributes(body) + _getCalendarAttributes(body) + colorRgbFormat = 'backgroundColor' in body or 'foregroundColor' in body _updateDeleteCalendars(users, calendarEntity, 'patch', body=body, colorRgbFormat=colorRgbFormat, fields='') # gam delete calendars @@ -48923,18 +48924,20 @@ def printShowCalendarACLs(users): TRANSFER_CALENDAR_APPEND_FIELDS = ['description', 'location', 'summary'] -# gam transfer calendars +# gam transfer calendars|seccals [] # [keepuser | (retainrole )] [sendnotifications ] [noretentionmessages] -# [] [append description|location|summary] [noupdatemessages] -# gam transfer seccals [keepuser] [sendnotifications ] +# * [append description|location|summary] [noupdatemessages] +# [deletefromoldowner] [addtonewowner *] [nolistmessages] def transferCalendars(users): targetUser = getEmailAddress() calendarEntity = getUserCalendarEntity(noSelectionKwargs={'minAccessRole': 'owner', 'showHidden': True}) notAllowedForbidden = Msg.FORBIDDEN if (not calendarEntity['all']) and (not calendarEntity.get('kwargs', {}).get('minAccessRole', '') == 'owner') else Msg.NOT_ALLOWED retainRoleBody = {'role': 'none'} - sendNotifications = showUpdateMessages = showRetentionMessages = True + sendNotifications = showListMessages = showRetentionMessages = showUpdateMessages = True updateBody = {} appendFieldsList = [] + addToNewOwner = deleteFromOldOwner = False + targetListBody = {'selected': True, 'hidden': False} while Cmd.ArgumentsRemaining(): myarg = getArgument() if myarg == 'keepuser': @@ -48943,8 +48946,6 @@ def transferCalendars(users): retainRoleBody['role'] = getChoice(CALENDAR_ACL_ROLES_MAP, mapChoice=True) elif myarg == 'sendnotifications': sendNotifications = getBoolean() - elif myarg == 'noretentionmessages': - showRetentionMessages = False elif _getCalendarSetting(myarg, updateBody): pass elif myarg == 'append': @@ -48953,13 +48954,25 @@ def transferCalendars(users): appendFieldsList.append(field) else: invalidChoiceExit(field, TRANSFER_CALENDAR_APPEND_FIELDS, True) + elif myarg == 'nolistmessages': + showListMessages = False + elif myarg == 'noretentionmessages': + showRetentionMessages = False elif myarg == 'noupdatemessages': showUpdateMessages = False + elif myarg == 'deletefromoldowner': + deleteFromOldOwner = True + elif myarg == 'addtonewowner': + addToNewOwner = True + _getCalendarAttributes(targetListBody, returnOnUnknownArgument=True) else: unknownArgumentExit() targetUser, targetCal = validateCalendar(targetUser) if not targetCal: return + colorRgbFormat = 'backgroundColor' in targetListBody or 'foregroundColor' in targetListBody + if 'summaryOverride' in targetListBody and 'summary' not in updateBody: + updateBody['summary'] = targetListBody.pop('summaryOverride') if updateBody: timestamp = currentISOformatTimeStamp('seconds') appendFields = ','.join(set(appendFieldsList)) @@ -48992,7 +49005,7 @@ def transferCalendars(users): calendarId=calId, body=targetRoleBody, sendNotifications=sendNotifications, fields='') entityModifierNewValueItemValueListActionPerformed([Ent.CALENDAR, calId], Act.MODIFIER_TO, None, [Ent.USER, targetUser], j, jcount) except (GAPI.forbidden, GAPI.requiredAccessLevel) as e: - entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount) + entityActionFailedWarning([Ent.USER, user, Ent.CALENDAR, calId], str(e), j, jcount) continue except (GAPI.notFound, GAPI.invalid): entityUnknownWarning(Ent.CALENDAR, calId, j, jcount) @@ -49024,6 +49037,19 @@ def transferCalendars(users): entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount) except (GAPI.serviceNotAvailable, GAPI.authError): entityServiceNotApplicableWarning(Ent.CALENDAR, calId, j, jcount) + if addToNewOwner: + Act.Set(Act.ADD) + targetListBody['id'] = calId + try: + callGAPI(targetCal.calendarList(), 'insert', + throwReasons=[GAPI.NOT_FOUND, GAPI.DUPLICATE, GAPI.UNKNOWN_ERROR, GAPI.SERVICE_NOT_AVAILABLE, + GAPI.CANNOT_CHANGE_OWN_ACL, GAPI.CANNOT_CHANGE_OWN_PRIMARY_SUBSCRIPTION], + body=targetListBody, colorRgbFormat=colorRgbFormat, fields='') + if showListMessages: + entityModifierNewValueItemValueListActionPerformed([Ent.CALENDAR, calId], Act.MODIFIER_TO, None, [Ent.USER, targetUser], j, jcount) + except (GAPI.notFound, GAPI.duplicate, GAPI.unknownError, GAPI.serviceNotAvailable, + GAPI.cannotChangeOwnAcl, GAPI.cannotChangeOwnPrimarySubscription) as e: + entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount) Act.Set(Act.RETAIN) if retainRoleBody['role'] == 'owner': if showRetentionMessages: @@ -49052,6 +49078,23 @@ def transferCalendars(users): entityActionPerformed([Ent.CALENDAR, calId, Ent.CALENDAR_ACL, formatACLScopeRole(sourceRuleId, retainRoleBody['role'])], j, jcount) except (GAPI.notFound, GAPI.invalid): entityUnknownWarning(Ent.CALENDAR, calId, j, jcount) + if deleteFromOldOwner: + Act.Set(Act.DELETE) + try: + callGAPI(sourceCal.calendarList(), 'delete', + throwReasons=[GAPI.NOT_FOUND, GAPI.DUPLICATE, GAPI.UNKNOWN_ERROR, GAPI.SERVICE_NOT_AVAILABLE, + GAPI.CANNOT_CHANGE_OWN_ACL, GAPI.CANNOT_CHANGE_OWN_PRIMARY_SUBSCRIPTION], + calendarId=calId) + entityModifierNewValueItemValueListActionPerformed([Ent.CALENDAR, calId], Act.MODIFIER_FROM, None, [Ent.USER, user], j, jcount) + except GAPI.notFound as e: + if retainRoleBody['role'] == 'none': + if showListMessages: + entityModifierNewValueItemValueListActionPerformed([Ent.CALENDAR, calId], Act.MODIFIER_FROM, None, [Ent.USER, user], j, jcount) + else: + entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount) + except (GAPI.duplicate, GAPI.unknownError, GAPI.serviceNotAvailable, + GAPI.cannotChangeOwnAcl, GAPI.cannotChangeOwnPrimarySubscription) as e: + entityActionFailedWarning([Ent.CALENDAR, calId], str(e), j, jcount) Ind.Decrement() def _createImportCalendarEvent(users, function):