mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-03 12:21:35 +00:00
Added input_dir directory variable to gam.cfg
Added support for the new resource calendar setting `autoAcceptInvitations`. # 1865
This commit is contained in:
@@ -1962,6 +1962,7 @@ gam calendar <CalendarEntity> printevents <EventSelectProperty>* <EventDisplayPr
|
|||||||
[formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*]
|
[formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*]
|
||||||
|
|
||||||
<CalendarSettingsField> ::=
|
<CalendarSettingsField> ::=
|
||||||
|
autoacceptinvitations|
|
||||||
conferenceproperties|
|
conferenceproperties|
|
||||||
description|
|
description|
|
||||||
id|
|
id|
|
||||||
@@ -1971,6 +1972,7 @@ gam calendar <CalendarEntity> printevents <EventSelectProperty>* <EventDisplayPr
|
|||||||
<CalendarSettingsFieldList> ::= "<CalendarSettingsField>(,<CalendarSettingsField>)*"
|
<CalendarSettingsFieldList> ::= "<CalendarSettingsField>(,<CalendarSettingsField>)*"
|
||||||
|
|
||||||
<CalendarSettings> ::=
|
<CalendarSettings> ::=
|
||||||
|
(autoacceptinvitations [<Boolean>])|
|
||||||
(description <String>)|
|
(description <String>)|
|
||||||
(location <String>)|
|
(location <String>)|
|
||||||
(summary <String>)|
|
(summary <String>)|
|
||||||
@@ -1987,10 +1989,10 @@ gam calendar|calendars <CalendarEntity> modify <CalendarSettings>+
|
|||||||
# Chat Bot
|
# Chat Bot
|
||||||
|
|
||||||
<ChatContent> ::=
|
<ChatContent> ::=
|
||||||
((text <String>)|
|
(text <String>)|
|
||||||
(textfile <FileName> [charset <Charset>])|
|
(textfile <FileName> [charset <Charset>])|
|
||||||
(gdoc <UserGoogleDoc>)|
|
(gdoc <UserGoogleDoc>)|
|
||||||
(gcsdoc <StorageBucketObjectName>))
|
(gcsdoc <StorageBucketObjectName>)
|
||||||
|
|
||||||
<ChatMember> ::= spaces/<String>/members/<String>
|
<ChatMember> ::= spaces/<String>/members/<String>
|
||||||
<ChatMessage> ::= spaces/<String>/messages/<String>
|
<ChatMessage> ::= spaces/<String>/messages/<String>
|
||||||
@@ -3201,10 +3203,10 @@ gam course <CourseID> create|add alias <CourseAlias>
|
|||||||
gam course <CourseID> delete alias <CourseAlias>
|
gam course <CourseID> delete alias <CourseAlias>
|
||||||
|
|
||||||
<CourseAnnouncementContent> ::=
|
<CourseAnnouncementContent> ::=
|
||||||
((text <String>)|
|
(text <String>)|
|
||||||
(textfile <FileName> [charset <Charset>])|
|
(textfile <FileName> [charset <Charset>])|
|
||||||
(gdoc <UserGoogleDoc>)|
|
(gdoc <UserGoogleDoc>)|
|
||||||
(gcsdoc <StorageBucketObjectName>))
|
(gcsdoc <StorageBucketObjectName>)
|
||||||
|
|
||||||
gam course <CourseID> create announcement
|
gam course <CourseID> create announcement
|
||||||
<CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
|
<CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
|
||||||
@@ -3575,6 +3577,12 @@ gam show domainaliases|aliasdomains
|
|||||||
|
|
||||||
# Domain - Contacts and Global Address List
|
# Domain - Contacts and Global Address List
|
||||||
|
|
||||||
|
<ContactNoteContent> ::=
|
||||||
|
<String>|
|
||||||
|
(file|textfile <FileName> [charset <Charset>])|
|
||||||
|
(gdoc <UserGoogleDoc>)
|
||||||
|
(gcsdoc <StorageBucketObjectName>)
|
||||||
|
|
||||||
<ContactAttribute> ::=
|
<ContactAttribute> ::=
|
||||||
(additionalname|middlename <String>)|
|
(additionalname|middlename <String>)|
|
||||||
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
|
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
|
||||||
@@ -3600,7 +3608,7 @@ gam show domainaliases|aliasdomains
|
|||||||
(mileage <String>)|
|
(mileage <String>)|
|
||||||
(name <String>)|
|
(name <String>)|
|
||||||
(nickname <String>)|
|
(nickname <String>)|
|
||||||
(note <NoteContent>)|
|
(note <ContactNoteContent>)|
|
||||||
(occupation <String>)|
|
(occupation <String>)|
|
||||||
(organization clear|(work|other|<String> <String> ((location <String>)|(department <String>)|(title <String>)|(jobdescription <String>)|(symbol <String>))* notprimary|primary))|
|
(organization clear|(work|other|<String> <String> ((location <String>)|(department <String>)|(title <String>)|(jobdescription <String>)|(symbol <String>))* notprimary|primary))|
|
||||||
(phone clear|(work|home|other|fax|work_fax|home_fax|other_fax|main|company_main|assistant|mobile|work_mobile|pager|work_pager|car|radio|callback|isdn|telex|tty_tdd|<String> <String> notprimary|primary))|
|
(phone clear|(work|home|other|fax|work_fax|home_fax|other_fax|main|company_main|assistant|mobile|work_mobile|pager|work_pager|car|radio|callback|isdn|telex|tty_tdd|<String> <String> notprimary|primary))|
|
||||||
@@ -4728,6 +4736,7 @@ gam print resoldsubscriptions [todrive <ToDriveAttribute>*]
|
|||||||
(zipcode|postalcode <String>)
|
(zipcode|postalcode <String>)
|
||||||
|
|
||||||
<ResourceAttribute> ::=
|
<ResourceAttribute> ::=
|
||||||
|
(autoacceptinvitations [<Boolean>])|
|
||||||
(addfeatures <FeatureNameList>)|
|
(addfeatures <FeatureNameList>)|
|
||||||
(buildingid <BuildingID>)|
|
(buildingid <BuildingID>)|
|
||||||
(capacity <Number>)|
|
(capacity <Number>)|
|
||||||
@@ -4743,6 +4752,7 @@ gam print resoldsubscriptions [todrive <ToDriveAttribute>*]
|
|||||||
|
|
||||||
<ResourceFieldName> ::=
|
<ResourceFieldName> ::=
|
||||||
acls|
|
acls|
|
||||||
|
autoacceptinvitations|
|
||||||
buildingid|
|
buildingid|
|
||||||
calendar|
|
calendar|
|
||||||
capacity|
|
capacity|
|
||||||
@@ -5668,6 +5678,11 @@ gam download storagefile <StorageBucketObjectName>
|
|||||||
<UserOrderByFieldName> ::=
|
<UserOrderByFieldName> ::=
|
||||||
familyname|lastname|givenname|firstname|email
|
familyname|lastname|givenname|firstname|email
|
||||||
|
|
||||||
|
<UserNoteContent> ::=
|
||||||
|
<String>|
|
||||||
|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
|
||||||
|
(gdoc|ghtml <UserGoogleDoc>)
|
||||||
|
|
||||||
<UserBasicAttribute> ::=
|
<UserBasicAttribute> ::=
|
||||||
(archive|archived <Boolean>)|
|
(archive|archived <Boolean>)|
|
||||||
(changepassword|changepasswordatnextlogin <Boolean>)|
|
(changepassword|changepasswordatnextlogin <Boolean>)|
|
||||||
@@ -5681,9 +5696,7 @@ gam download storagefile <StorageBucketObjectName>
|
|||||||
(ipwhitelisted <Boolean>)|
|
(ipwhitelisted <Boolean>)|
|
||||||
(language clear|<LanguageList>)|
|
(language clear|<LanguageList>)|
|
||||||
(lastname|familyname <String>)|
|
(lastname|familyname <String>)|
|
||||||
(note clear|([text_html|text_plain] <String>|
|
(note clear|([text_html|text_plain] <UserNoteContent))|
|
||||||
(file|htmlfile <FileName> [charset <Charset>])|
|
|
||||||
(gdoc|ghtml <UserGoogleDoc>)))|
|
|
||||||
(ou|org|orgunitpath <OrgUnitPath>|<OrgUnitID>)
|
(ou|org|orgunitpath <OrgUnitPath>|<OrgUnitID>)
|
||||||
(password (random [<Integer>])|(uniquerandom [<Integer>])|
|
(password (random [<Integer>])|(uniquerandom [<Integer>])|
|
||||||
blocklogin|
|
blocklogin|
|
||||||
@@ -6054,6 +6067,7 @@ gam <UserTypeEntity> print backupcodes|verificationcodes [todrive <ToDriveAttrib
|
|||||||
(summary <String>)
|
(summary <String>)
|
||||||
|
|
||||||
<CalendarSettings> ::=
|
<CalendarSettings> ::=
|
||||||
|
(autoacceptinvitations [<Boolean>])|
|
||||||
(description <String>)|
|
(description <String>)|
|
||||||
(location <String>)|
|
(location <String>)|
|
||||||
(summary <String>)|
|
(summary <String>)|
|
||||||
@@ -6380,10 +6394,10 @@ gam <UserTypeEntity> print focustime|outofoffice|workinglocation
|
|||||||
# Users - Chat
|
# Users - Chat
|
||||||
|
|
||||||
<ChatContent> ::=
|
<ChatContent> ::=
|
||||||
((text <String>)|
|
(text <String>)|
|
||||||
(textfile <FileName> [charset <Charset>])|
|
(textfile <FileName> [charset <Charset>])|
|
||||||
(gdoc <UserGoogleDoc>)|
|
(gdoc <UserGoogleDoc>)|
|
||||||
(gcsdoc <StorageBucketObjectName>))
|
(gcsdoc <StorageBucketObjectName>)
|
||||||
|
|
||||||
<ChatMember> ::= spaces/<String>/members/<String>
|
<ChatMember> ::= spaces/<String>/members/<String>
|
||||||
<ChatMemberList> ::= "<ChatMember>(,<ChatMember>)*"
|
<ChatMemberList> ::= "<ChatMember>(,<ChatMember>)*"
|
||||||
@@ -8071,16 +8085,16 @@ gam <UserTypeEntity> check isinvitable [todrive <ToDriveAttribute>*]
|
|||||||
|
|
||||||
# Users - Keep Notes
|
# Users - Keep Notes
|
||||||
|
|
||||||
<NoteContent> ::=
|
<KeepNoteContent> ::=
|
||||||
((text <String>)|
|
(text <String>)|
|
||||||
(textfile <FileName> [charset <Charset>])|
|
(textfile <FileName> [charset <Charset>])|
|
||||||
(gdoc <UserGoogleDoc>)|
|
(gdoc <UserGoogleDoc>)|
|
||||||
(gcsdoc <StorageBucketObjectName>)|
|
(gcsdoc <StorageBucketObjectName>)|
|
||||||
<JSONData>)
|
<JSONData>
|
||||||
|
|
||||||
gam <UserTypeEntity> create note [title <String>]
|
gam <UserTypeEntity> create note [title <String>]
|
||||||
[missingtextvalue <String>]
|
[missingtextvalue <String>]
|
||||||
<NoteContent>
|
<KeepNoteContent>
|
||||||
[copyacls [copyowneraswriter]]
|
[copyacls [copyowneraswriter]]
|
||||||
[compact|formatjson|nodetails]
|
[compact|formatjson|nodetails]
|
||||||
|
|
||||||
@@ -8251,12 +8265,17 @@ gam <UserItem> print meettranscripts <MeetConferenceName> [todrive <ToDriveAttri
|
|||||||
|
|
||||||
# Users - Contacts and Profiles
|
# Users - Contacts and Profiles
|
||||||
|
|
||||||
|
<BiographyContent> ::=
|
||||||
|
<String>|
|
||||||
|
(file|textfile <FileName> [charset <Charset>])|
|
||||||
|
(gdoc <UserGoogleDoc>)
|
||||||
|
|
||||||
<PeopleContactAttribute> ::=
|
<PeopleContactAttribute> ::=
|
||||||
(additionalname|middlename <String>)|
|
(additionalname|middlename <String>)|
|
||||||
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
|
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
|
||||||
(neighborhood <String>)|(locality <String>)|(region <String>)|(postalcode <String>)|(country <String>))* notprimary|primary))|
|
(neighborhood <String>)|(locality <String>)|(region <String>)|(postalcode <String>)|(country <String>))* notprimary|primary))|
|
||||||
(billinginfo <String>)|
|
(billinginfo <String>)|
|
||||||
(biography|biographies <String>|(file <FileName> [charset <Charset>])|(gdoc <UserGoogleDoc>))|
|
(biography|biographies <BiographyContent)|
|
||||||
(birthday <Date>)|
|
(birthday <Date>)|
|
||||||
(calendar clear|(work|home|free-busy|<String> <URL> notprimary|primary))|
|
(calendar clear|(work|home|free-busy|<String> <URL> notprimary|primary))|
|
||||||
(clientdata <String> <String>)|
|
(clientdata <String> <String>)|
|
||||||
|
|||||||
@@ -1,3 +1,11 @@
|
|||||||
|
7.30.00
|
||||||
|
|
||||||
|
Added `input_dir` directory variable to `gam.cfg` that is used to select a directory for reading files with non-absolute file names.
|
||||||
|
The default is an empty string that matches the current behavior where these files are read from the current working directory.
|
||||||
|
This will be most useful in multiple domain situations where each domain will have distinct `drive_dir` and `input_dir` values.
|
||||||
|
|
||||||
|
Added support for the new resource calendar setting `autoAcceptInvitations`.
|
||||||
|
|
||||||
7.29.04
|
7.29.04
|
||||||
|
|
||||||
Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>`
|
Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>`
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||||
__version__ = '7.29.04'
|
__version__ = '7.30.00'
|
||||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||||
|
|
||||||
#pylint: disable=wrong-import-position
|
#pylint: disable=wrong-import-position
|
||||||
@@ -1856,7 +1856,7 @@ def getStringOrFile(myarg, minLen=0, unescapeCRLF=False):
|
|||||||
if myarg in {'file', 'textfile', 'htmlfile'}:
|
if myarg in {'file', 'textfile', 'htmlfile'}:
|
||||||
filename = getString(Cmd.OB_FILE_NAME)
|
filename = getString(Cmd.OB_FILE_NAME)
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
return (readFile(filename, encoding=encoding), encoding, html)
|
return (readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding), encoding, html)
|
||||||
if myarg in {'gdoc', 'ghtml'}:
|
if myarg in {'gdoc', 'ghtml'}:
|
||||||
f = getGDocData(myarg)
|
f = getGDocData(myarg)
|
||||||
data = f.read()
|
data = f.read()
|
||||||
@@ -2254,7 +2254,7 @@ def getJSON(deleteFields):
|
|||||||
filename = getString(Cmd.OB_FILE_NAME)
|
filename = getString(Cmd.OB_FILE_NAME)
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
try:
|
try:
|
||||||
jsonData = json.loads(readFile(filename, encoding=encoding))
|
jsonData = json.loads(readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding))
|
||||||
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
||||||
Cmd.Backup()
|
Cmd.Backup()
|
||||||
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
|
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
|
||||||
@@ -2906,14 +2906,13 @@ def entityModifierNewValueKeyValueActionPerformed(entityValueList, modifier, new
|
|||||||
def cleanFilename(filename):
|
def cleanFilename(filename):
|
||||||
return sanitize_filename(filename, '_')
|
return sanitize_filename(filename, '_')
|
||||||
|
|
||||||
def setFilePath(fileName):
|
def setFilePath(filename, cfgDir):
|
||||||
if fileName.startswith('./') or fileName.startswith('.\\'):
|
if filename.startswith('./') or filename.startswith('.\\'):
|
||||||
fileName = os.path.join(os.getcwd(), fileName[2:])
|
return os.path.join(os.getcwd(), filename[2:])
|
||||||
else:
|
filename = os.path.expanduser(filename)
|
||||||
fileName = os.path.expanduser(fileName)
|
if os.path.isabs(filename):
|
||||||
if not os.path.isabs(fileName):
|
return filename
|
||||||
fileName = os.path.join(GC.Values[GC.DRIVE_DIR], fileName)
|
return os.path.join(GC.Values[cfgDir], filename)
|
||||||
return fileName
|
|
||||||
|
|
||||||
def uniqueFilename(targetFolder, filetitle, overwrite, extension=None):
|
def uniqueFilename(targetFolder, filetitle, overwrite, extension=None):
|
||||||
filename = filetitle
|
filename = filetitle
|
||||||
@@ -3278,6 +3277,7 @@ def openCSVFileReader(filename, fieldnames=None):
|
|||||||
getCharSet()
|
getCharSet()
|
||||||
else:
|
else:
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
|
filename = setFilePath(filename, GC.INPUT_DIR)
|
||||||
f = openFile(filename, mode=DEFAULT_CSV_READ_MODE, encoding=encoding)
|
f = openFile(filename, mode=DEFAULT_CSV_READ_MODE, encoding=encoding)
|
||||||
if checkArgumentPresent('warnifnodata'):
|
if checkArgumentPresent('warnifnodata'):
|
||||||
loc = f.tell()
|
loc = f.tell()
|
||||||
@@ -3888,7 +3888,7 @@ def SetGlobalVariables():
|
|||||||
|
|
||||||
def _setCSVFile(fileName, mode, encoding, writeHeader, multi):
|
def _setCSVFile(fileName, mode, encoding, writeHeader, multi):
|
||||||
if fileName != '-':
|
if fileName != '-':
|
||||||
fileName = setFilePath(fileName)
|
fileName = setFilePath(fileName, GC.DRIVE_DIR)
|
||||||
GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] = fileName
|
GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] = fileName
|
||||||
GM.Globals[GM.CSVFILE][GM.REDIRECT_MODE] = mode
|
GM.Globals[GM.CSVFILE][GM.REDIRECT_MODE] = mode
|
||||||
GM.Globals[GM.CSVFILE][GM.REDIRECT_ENCODING] = encoding
|
GM.Globals[GM.CSVFILE][GM.REDIRECT_ENCODING] = encoding
|
||||||
@@ -3909,7 +3909,7 @@ def SetGlobalVariables():
|
|||||||
else:
|
else:
|
||||||
GM.Globals[stdtype][GM.REDIRECT_FD] = os.fdopen(os.dup(sys.stderr.fileno()), mode, encoding=GM.Globals[GM.SYS_ENCODING])
|
GM.Globals[stdtype][GM.REDIRECT_FD] = os.fdopen(os.dup(sys.stderr.fileno()), mode, encoding=GM.Globals[GM.SYS_ENCODING])
|
||||||
else:
|
else:
|
||||||
fileName = setFilePath(fileName)
|
fileName = setFilePath(fileName, GC.DRIVE_DIR)
|
||||||
if multi and mode == DEFAULT_FILE_WRITE_MODE:
|
if multi and mode == DEFAULT_FILE_WRITE_MODE:
|
||||||
deleteFile(fileName)
|
deleteFile(fileName)
|
||||||
mode = DEFAULT_FILE_APPEND_MODE
|
mode = DEFAULT_FILE_APPEND_MODE
|
||||||
@@ -6815,6 +6815,7 @@ def getEntitiesFromFile(shlexSplit, returnSet=False):
|
|||||||
filenameLower = filename.lower()
|
filenameLower = filename.lower()
|
||||||
if filenameLower not in {'gcsv', 'gdoc', 'gcscsv', 'gcsdoc'}:
|
if filenameLower not in {'gcsv', 'gdoc', 'gcscsv', 'gcsdoc'}:
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
|
filename = setFilePath(filename, GC.INPUT_DIR)
|
||||||
f = openFile(filename, encoding=encoding, stripUTFBOM=True)
|
f = openFile(filename, encoding=encoding, stripUTFBOM=True)
|
||||||
elif filenameLower in {'gcsv', 'gdoc'}:
|
elif filenameLower in {'gcsv', 'gdoc'}:
|
||||||
f = getGDocData(filenameLower)
|
f = getGDocData(filenameLower)
|
||||||
@@ -7238,7 +7239,7 @@ def checkUserSuspended(cd, user, entityType=Ent.USER, i=0, count=0):
|
|||||||
def _addAttachmentsToMessage(message, attachments):
|
def _addAttachmentsToMessage(message, attachments):
|
||||||
for attachment in attachments:
|
for attachment in attachments:
|
||||||
try:
|
try:
|
||||||
attachFilename = attachment[0]
|
attachFilename = setFilePath(attachment[0], GC.INPUT_DIR)
|
||||||
attachContentType, attachEncoding = mimetypes.guess_type(attachFilename)
|
attachContentType, attachEncoding = mimetypes.guess_type(attachFilename)
|
||||||
if attachContentType is None or attachEncoding is not None:
|
if attachContentType is None or attachEncoding is not None:
|
||||||
attachContentType = 'application/octet-stream'
|
attachContentType = 'application/octet-stream'
|
||||||
@@ -7263,7 +7264,7 @@ def _addAttachmentsToMessage(message, attachments):
|
|||||||
def _addEmbeddedImagesToMessage(message, embeddedImages):
|
def _addEmbeddedImagesToMessage(message, embeddedImages):
|
||||||
for embeddedImage in embeddedImages:
|
for embeddedImage in embeddedImages:
|
||||||
try:
|
try:
|
||||||
imageFilename = embeddedImage[0]
|
imageFilename = setFilePath(embeddedImage[0], GC.INPUT_DIR)
|
||||||
imageContentType, imageEncoding = mimetypes.guess_type(imageFilename)
|
imageContentType, imageEncoding = mimetypes.guess_type(imageFilename)
|
||||||
if imageContentType is None or imageEncoding is not None:
|
if imageContentType is None or imageEncoding is not None:
|
||||||
imageContentType = 'application/octet-stream'
|
imageContentType = 'application/octet-stream'
|
||||||
@@ -10270,6 +10271,7 @@ def doBatch(threadBatch=False):
|
|||||||
filenameLower = filename.lower()
|
filenameLower = filename.lower()
|
||||||
if filenameLower not in {'gdoc', 'gcsdoc'}:
|
if filenameLower not in {'gdoc', 'gcsdoc'}:
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
|
filename = setFilePath(filename, GC.INPUT_DIR)
|
||||||
f = openFile(filename, encoding=encoding, stripUTFBOM=True)
|
f = openFile(filename, encoding=encoding, stripUTFBOM=True)
|
||||||
elif filenameLower == 'gdoc':
|
elif filenameLower == 'gdoc':
|
||||||
f = getGDocData(filenameLower)
|
f = getGDocData(filenameLower)
|
||||||
@@ -18500,7 +18502,7 @@ def doCheckOrgUnit():
|
|||||||
else:
|
else:
|
||||||
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
|
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
|
||||||
elif myarg == 'filename':
|
elif myarg == 'filename':
|
||||||
fileName = setFilePath(getString(Cmd.OB_FILE_NAME))
|
fileName = setFilePath(getString(Cmd.OB_FILE_NAME), GC.DRIVE_DIR)
|
||||||
elif myarg == 'movetoou':
|
elif myarg == 'movetoou':
|
||||||
movetoouLocation = Cmd.Location()
|
movetoouLocation = Cmd.Location()
|
||||||
status, moveToOrgUnitPath, _ = checkOrgUnitPathExists(cd, getOrgUnitItem())
|
status, moveToOrgUnitPath, _ = checkOrgUnitPathExists(cd, getOrgUnitItem())
|
||||||
@@ -18517,7 +18519,7 @@ def doCheckOrgUnit():
|
|||||||
usageErrorExit(Msg.OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL.format(orgUnitPath, moveToOrgUnitPath))
|
usageErrorExit(Msg.OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL.format(orgUnitPath, moveToOrgUnitPath))
|
||||||
if 'subous' in fieldsList and moveToOrgUnitPathLower.startswith(orgUnitPathLower):
|
if 'subous' in fieldsList and moveToOrgUnitPathLower.startswith(orgUnitPathLower):
|
||||||
usageErrorExit(Msg.OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU.format(orgUnitPath, moveToOrgUnitPath))
|
usageErrorExit(Msg.OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU.format(orgUnitPath, moveToOrgUnitPath))
|
||||||
fileName = setFilePath(fileName)
|
fileName = setFilePath(fileName, GC.DRIVE_DIR)
|
||||||
f = openFile(fileName, DEFAULT_FILE_WRITE_MODE)
|
f = openFile(fileName, DEFAULT_FILE_WRITE_MODE)
|
||||||
orgUnitItemCounts = {}
|
orgUnitItemCounts = {}
|
||||||
for field in sorted(fieldsList):
|
for field in sorted(fieldsList):
|
||||||
@@ -29136,7 +29138,7 @@ def doDeleteChromePolicy():
|
|||||||
policy = result['resolvedPolicies'][0]
|
policy = result['resolvedPolicies'][0]
|
||||||
if request['policyTargetKey']['targetResource'] == policy['addedSourceKey'].get('targetResource', ''):
|
if request['policyTargetKey']['targetResource'] == policy['addedSourceKey'].get('targetResource', ''):
|
||||||
request['policySchema'] = 'chrome.users.apps.*'
|
request['policySchema'] = 'chrome.users.apps.*'
|
||||||
except (GAPI.notFound, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.serviceNotAvailable, GAPI.quotaExceeded) as e:
|
except (GAPI.notFound, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.serviceNotAvailable, GAPI.quotaExceeded):
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
callGAPI(service, function,
|
callGAPI(service, function,
|
||||||
@@ -37107,6 +37109,134 @@ def _checkPoliciesWithDASA():
|
|||||||
systemErrorExit(USAGE_ERROR_RC,
|
systemErrorExit(USAGE_ERROR_RC,
|
||||||
Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES))
|
Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES))
|
||||||
|
|
||||||
|
def _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail):
|
||||||
|
if groupEmail:
|
||||||
|
Cmd.Backup()
|
||||||
|
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'group'))
|
||||||
|
targetName, targetResource = _getOrgunitsOrgUnitIdPath(cd, getString(Cmd.OB_ORGUNIT_PATH))
|
||||||
|
return (targetName, targetResource)
|
||||||
|
|
||||||
|
def _getCIPolicyGroupTarget(cd, myarg, orgUnit):
|
||||||
|
if orgUnit:
|
||||||
|
Cmd.Backup()
|
||||||
|
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'ou|org|orgunit'))
|
||||||
|
targetName = getEmailAddress(returnUIDprefix='uid:')
|
||||||
|
targetResource = f"groups/{convertEmailAddressToUID(targetName, cd, emailType='group')}"
|
||||||
|
return (targetName, targetResource)
|
||||||
|
|
||||||
|
# gam create policy
|
||||||
|
# json <JSONData>
|
||||||
|
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)]
|
||||||
|
# gam update policy
|
||||||
|
# json <JSONData>
|
||||||
|
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)]
|
||||||
|
def doCreateUpdateCIPolicy():
|
||||||
|
_checkPoliciesWithDASA()
|
||||||
|
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA)
|
||||||
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
|
updateCmd = Act.Get() == Act.UPDATE
|
||||||
|
groupEmail = orgUnit = None
|
||||||
|
checkArgumentPresent('json', True)
|
||||||
|
jsonData = getJSON(['type'])
|
||||||
|
if updateCmd:
|
||||||
|
pname = jsonData.pop('name', None)
|
||||||
|
else:
|
||||||
|
pname = 'New Policy'
|
||||||
|
if 'policyQuery' in jsonData:
|
||||||
|
jsonData['policyQuery'].pop('orgUnitPath', None)
|
||||||
|
jsonData['policyQuery'].pop('groupEmail', None)
|
||||||
|
jsonData['policyQuery'].pop('sortOrder', None)
|
||||||
|
if 'setting' in jsonData and 'value' in jsonData['setting']:
|
||||||
|
jsonData['setting']['value'].pop('createTime', None)
|
||||||
|
jsonData['setting']['value'].pop('updateTime', None)
|
||||||
|
while Cmd.ArgumentsRemaining():
|
||||||
|
myarg = getArgument()
|
||||||
|
if myarg in {'ou', 'org', 'orgunit'}:
|
||||||
|
orgUnit, targetResource = _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail)
|
||||||
|
jsonData.setdefault('policyQuery', {})
|
||||||
|
jsonData['policyQuery'].pop('group', None)
|
||||||
|
jsonData['policyQuery']['orgUnit'] = targetResource
|
||||||
|
elif myarg == 'group':
|
||||||
|
groupEmail, targetResource = _getCIPolicyGroupTarget(cd, myarg, orgUnit)
|
||||||
|
jsonData.setdefault('policyQuery', {})
|
||||||
|
jsonData['policyQuery'].pop('orgUnit', None)
|
||||||
|
jsonData['policyQuery']['group'] = targetResource
|
||||||
|
else:
|
||||||
|
unknownArgumentExit()
|
||||||
|
jsonData['customer'] = _getCustomersCustomerIdWithC()
|
||||||
|
try:
|
||||||
|
if updateCmd:
|
||||||
|
result = callGAPI(ci.policies(), 'patch',
|
||||||
|
bailOnInternalError=True,
|
||||||
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
|
||||||
|
name=pname, body=jsonData)
|
||||||
|
else:
|
||||||
|
result = callGAPI(ci.policies(), 'create',
|
||||||
|
bailOnInternalError=True,
|
||||||
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
|
||||||
|
body=jsonData)
|
||||||
|
if result['done']:
|
||||||
|
if 'error' not in result:
|
||||||
|
if not updateCmd:
|
||||||
|
pname = result['response'].get('id', pname)
|
||||||
|
entityActionPerformed([Ent.POLICY, pname])
|
||||||
|
else:
|
||||||
|
entityActionFailedWarning([Ent.POLICY, pname], result['error']['message'])
|
||||||
|
else:
|
||||||
|
entityActionPerformedMessage([Ent.POLICY, pname], Msg.ACTION_IN_PROGRESS.format('delete'))
|
||||||
|
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
|
||||||
|
entityActionFailedWarning([Ent.POLICY, pname], str(e))
|
||||||
|
|
||||||
|
|
||||||
|
# gam delete policies <CIPolicyNameEntity>
|
||||||
|
def doDeleteCIPolicies():
|
||||||
|
_checkPoliciesWithDASA()
|
||||||
|
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA)
|
||||||
|
entityList = getEntityList(Cmd.OB_CIPOLICY_NAME_ENTITY)
|
||||||
|
checkForExtraneousArguments()
|
||||||
|
i = 0
|
||||||
|
count = len(entityList)
|
||||||
|
for pname in entityList:
|
||||||
|
i += 1
|
||||||
|
if pname.startswith('policies/'):
|
||||||
|
try:
|
||||||
|
policies = [callGAPI(ci.policies(), 'get',
|
||||||
|
bailOnInternalError=True,
|
||||||
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
|
||||||
|
name=pname,
|
||||||
|
fields='name')]
|
||||||
|
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
|
||||||
|
entityActionFailedWarning([Ent.POLICY, pname], str(e), i, count)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if pname.startswith('settings/'):
|
||||||
|
pname = pname.split('/')[1]
|
||||||
|
ifilter = f"setting.type.matches('{pname}')"
|
||||||
|
printGettingAllAccountEntities(Ent.POLICY, ifilter)
|
||||||
|
policies = _filterPolicies(ci, getPageMessage(), ifilter)
|
||||||
|
jcount = len(policies)
|
||||||
|
performActionNumItems(jcount, Ent.POLICY)
|
||||||
|
Ind.Increment()
|
||||||
|
j = 0
|
||||||
|
for policy in policies:
|
||||||
|
j += 1
|
||||||
|
pname = policy['name']
|
||||||
|
try:
|
||||||
|
result = callGAPI(ci.policies(), 'delete',
|
||||||
|
bailOnInternalError=True,
|
||||||
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
|
||||||
|
name=pname)
|
||||||
|
if result['done']:
|
||||||
|
if 'error' not in result:
|
||||||
|
entityActionPerformed([Ent.POLICY, pname], j, jcount)
|
||||||
|
else:
|
||||||
|
entityActionFailedWarning([Ent.POLICY, pname], result['error']['message'], j, jcount)
|
||||||
|
else:
|
||||||
|
entityActionPerformedMessage([Ent.POLICY, pname], Msg.ACTION_IN_PROGRESS.format('delete'), j, jcount)
|
||||||
|
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
|
||||||
|
entityActionFailedWarning([Ent.POLICY, pname], str(e), j, jcount)
|
||||||
|
Ind.Decrement()
|
||||||
|
|
||||||
# gam info policies <CIPolicyNameEntity>
|
# gam info policies <CIPolicyNameEntity>
|
||||||
# [nowarnings] [noappnames]
|
# [nowarnings] [noappnames]
|
||||||
# [formatjson]
|
# [formatjson]
|
||||||
@@ -38851,6 +38981,7 @@ RESOURCE_CATEGORY_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def _getResourceCalendarAttributes(cd, body, updateMode):
|
def _getResourceCalendarAttributes(cd, body, updateMode):
|
||||||
|
autoAcceptInvitations = None
|
||||||
featureChanges = {'add': set(), 'remove': set()}
|
featureChanges = {'add': set(), 'remove': set()}
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
@@ -38887,26 +39018,45 @@ def _getResourceCalendarAttributes(cd, body, updateMode):
|
|||||||
body['resourceCategory'] = getChoice(RESOURCE_CATEGORY_MAP, mapChoice=True)
|
body['resourceCategory'] = getChoice(RESOURCE_CATEGORY_MAP, mapChoice=True)
|
||||||
elif myarg in {'userdescription', 'uservisibledescription'}:
|
elif myarg in {'userdescription', 'uservisibledescription'}:
|
||||||
body['userVisibleDescription'] = getString(Cmd.OB_STRING)
|
body['userVisibleDescription'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'autoacceptinvitations':
|
||||||
|
autoAcceptInvitations = getBoolean()
|
||||||
else:
|
else:
|
||||||
unknownArgumentExit()
|
unknownArgumentExit()
|
||||||
if ('featureInstances' in body and not body['featureInstances'] and
|
if ('featureInstances' in body and not body['featureInstances'] and
|
||||||
not featureChanges['add'] and not featureChanges['remove']):
|
not featureChanges['add'] and not featureChanges['remove']):
|
||||||
body['featureInstances'] = [{}]
|
body['featureInstances'] = [{}]
|
||||||
if not updateMode:
|
if not updateMode:
|
||||||
return body
|
return body, autoAcceptInvitations, None
|
||||||
return body, featureChanges
|
return body, autoAcceptInvitations, featureChanges
|
||||||
|
|
||||||
|
def updateAutoAcceptInvitations(cal, calId, autoAcceptInvitations, i=0, count=0):
|
||||||
|
Ind.Increment()
|
||||||
|
try:
|
||||||
|
callGAPI(cal.calendars(), 'patch',
|
||||||
|
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID],
|
||||||
|
calendarId=calId, body={'autoAcceptInvitations': autoAcceptInvitations})
|
||||||
|
entityActionPerformed([Ent.CALENDAR, calId], i, count)
|
||||||
|
except (GAPI.notFound, GAPI.forbidden, GAPI.invalid) as e:
|
||||||
|
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), i, count)
|
||||||
|
except GAPI.notACalendarUser:
|
||||||
|
userCalServiceNotEnabledWarning(calId, i, count)
|
||||||
|
Ind.Decrement()
|
||||||
|
|
||||||
# gam create resource <ResourceID> <Name> <ResourceAttribute>*
|
# gam create resource <ResourceID> <Name> <ResourceAttribute>*
|
||||||
def doCreateResourceCalendar():
|
def doCreateResourceCalendar():
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
body = _getResourceCalendarAttributes(cd, {'resourceId': getString(Cmd.OB_RESOURCE_ID), 'resourceName': getString(Cmd.OB_NAME)}, False)
|
body, autoAcceptInvitations, _ = _getResourceCalendarAttributes(cd, {'resourceId': getString(Cmd.OB_RESOURCE_ID), 'resourceName': getString(Cmd.OB_NAME)}, False)
|
||||||
|
if autoAcceptInvitations is not None:
|
||||||
|
cal = buildGAPIObject(API.CALENDAR)
|
||||||
try:
|
try:
|
||||||
callGAPI(cd.resources().calendars(), 'insert',
|
result = callGAPI(cd.resources().calendars(), 'insert',
|
||||||
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.SERVICE_NOT_AVAILABLE,
|
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.SERVICE_NOT_AVAILABLE,
|
||||||
GAPI.REQUIRED, GAPI.DUPLICATE, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
GAPI.REQUIRED, GAPI.DUPLICATE, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
|
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='resourceEmail')
|
||||||
entityActionPerformed([Ent.RESOURCE_CALENDAR, body['resourceId']])
|
entityActionPerformed([Ent.RESOURCE_CALENDAR, body['resourceId']])
|
||||||
|
if autoAcceptInvitations is not None:
|
||||||
|
updateAutoAcceptInvitations(cal, result['resourceEmail'], autoAcceptInvitations)
|
||||||
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable) as e:
|
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable) as e:
|
||||||
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, body['resourceId']], str(e))
|
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, body['resourceId']], str(e))
|
||||||
except GAPI.required as e:
|
except GAPI.required as e:
|
||||||
@@ -38924,17 +39074,19 @@ def doCreateResourceCalendar():
|
|||||||
|
|
||||||
def _doUpdateResourceCalendars(entityList):
|
def _doUpdateResourceCalendars(entityList):
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
body, featureChanges = _getResourceCalendarAttributes(cd, {}, True)
|
body, autoAcceptInvitations, featureChanges = _getResourceCalendarAttributes(cd, {}, True)
|
||||||
|
if autoAcceptInvitations is not None:
|
||||||
|
cal = buildGAPIObject(API.CALENDAR)
|
||||||
i = 0
|
i = 0
|
||||||
count = len(entityList)
|
count = len(entityList)
|
||||||
for resourceId in entityList:
|
for resourceId in entityList:
|
||||||
i += 1
|
i += 1
|
||||||
try:
|
try:
|
||||||
if featureChanges['add'] or featureChanges['remove']:
|
if autoAcceptInvitations is not None or featureChanges['add'] or featureChanges['remove']:
|
||||||
features = callGAPI(cd.resources().calendars(), 'get',
|
result = callGAPI(cd.resources().calendars(), 'get',
|
||||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FORBIDDEN],
|
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FORBIDDEN],
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, fields='featureInstances(feature(name))')
|
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, fields='resourceEmail,featureInstances(feature(name))')
|
||||||
bodyFeatures = body.pop('featureInstances', [])
|
bodyFeatures = body.pop('featureInstances', [])
|
||||||
body['featureInstances'] = []
|
body['featureInstances'] = []
|
||||||
featureSet = set()
|
featureSet = set()
|
||||||
@@ -38943,7 +39095,7 @@ def _doUpdateResourceCalendars(entityList):
|
|||||||
if featureName not in featureChanges['remove'] and featureName not in featureSet:
|
if featureName not in featureChanges['remove'] and featureName not in featureSet:
|
||||||
body['featureInstances'].append({'feature': {'name': featureName}})
|
body['featureInstances'].append({'feature': {'name': featureName}})
|
||||||
featureSet.add(featureName)
|
featureSet.add(featureName)
|
||||||
for feature in features.get('featureInstances', []):
|
for feature in result.get('featureInstances', []):
|
||||||
featureName = feature['feature']['name']
|
featureName = feature['feature']['name']
|
||||||
if featureName not in featureChanges['remove'] and featureName not in featureSet:
|
if featureName not in featureChanges['remove'] and featureName not in featureSet:
|
||||||
body['featureInstances'].append({'feature': {'name': featureName}})
|
body['featureInstances'].append({'feature': {'name': featureName}})
|
||||||
@@ -38960,6 +39112,8 @@ def _doUpdateResourceCalendars(entityList):
|
|||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, body=body, fields='')
|
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, body=body, fields='')
|
||||||
entityActionPerformed([Ent.RESOURCE_CALENDAR, resourceId], i, count)
|
entityActionPerformed([Ent.RESOURCE_CALENDAR, resourceId], i, count)
|
||||||
|
if autoAcceptInvitations is not None:
|
||||||
|
updateAutoAcceptInvitations(cal, result['resourceEmail'], autoAcceptInvitations, i, count)
|
||||||
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable, GAPI.required) as e:
|
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable, GAPI.required) as e:
|
||||||
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, resourceId], str(e), i, count)
|
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, resourceId], str(e), i, count)
|
||||||
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
||||||
@@ -41171,6 +41325,7 @@ def doCalendarsPrintShowEvents(calIds):
|
|||||||
|
|
||||||
# <CalendarSettings> ::==
|
# <CalendarSettings> ::==
|
||||||
# [description <String>] [location <String>] [summary <String>] [timezone <TimeZone>]
|
# [description <String>] [location <String>] [summary <String>] [timezone <TimeZone>]
|
||||||
|
# [autoacceptinvitations [<Boolean>]]
|
||||||
def _getCalendarSetting(myarg, body):
|
def _getCalendarSetting(myarg, body):
|
||||||
if myarg == 'description':
|
if myarg == 'description':
|
||||||
body['description'] = getStringWithCRsNLs()
|
body['description'] = getStringWithCRsNLs()
|
||||||
@@ -41180,6 +41335,8 @@ def _getCalendarSetting(myarg, body):
|
|||||||
body['summary'] = getString(Cmd.OB_STRING)
|
body['summary'] = getString(Cmd.OB_STRING)
|
||||||
elif myarg == 'timezone':
|
elif myarg == 'timezone':
|
||||||
body['timeZone'] = getString(Cmd.OB_STRING)
|
body['timeZone'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'autoacceptinvitations':
|
||||||
|
body['autoAcceptInvitations'] = getBoolean()
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -41232,9 +41389,12 @@ def _showCalendarSettings(calendar, j, jcount):
|
|||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
printKeyValueList(['AllowedConferenceSolutionTypes', ','.join(calendar.get('conferenceProperties', {}).get('allowedConferenceSolutionTypes', []))])
|
printKeyValueList(['AllowedConferenceSolutionTypes', ','.join(calendar.get('conferenceProperties', {}).get('allowedConferenceSolutionTypes', []))])
|
||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
|
if 'autoAcceptInvitations' in calendar:
|
||||||
|
printKeyValueList(['AutoAcceptInvitations', calendar['autoAcceptInvitations']])
|
||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
|
|
||||||
CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = {
|
CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = {
|
||||||
|
'autoacceptinvitations': 'autoAcceptInvitations',
|
||||||
'conferenceproperties': 'conferenceProperties',
|
'conferenceproperties': 'conferenceProperties',
|
||||||
'dataowner': 'dataOwner',
|
'dataowner': 'dataOwner',
|
||||||
'description': 'description',
|
'description': 'description',
|
||||||
@@ -41720,6 +41880,26 @@ def _copyStorageObjects(objects, target_bucket, target_prefix):
|
|||||||
ClientAPIAccessDeniedExit()
|
ClientAPIAccessDeniedExit()
|
||||||
Act.Set(action)
|
Act.Set(action)
|
||||||
|
|
||||||
|
def md5MatchesFile(filename, expected_md5, j=0, jcount=0):
|
||||||
|
action = Act.Get()
|
||||||
|
Act.Set(Act.VERIFY)
|
||||||
|
try:
|
||||||
|
f = openFile(filename, 'rb')
|
||||||
|
hash_md5 = hashlib.md5()
|
||||||
|
for chunk in iter(lambda: f.read(4096), b""):
|
||||||
|
hash_md5.update(chunk)
|
||||||
|
closeFile(f)
|
||||||
|
actual_hash = hash_md5.hexdigest()
|
||||||
|
if actual_hash == expected_md5:
|
||||||
|
entityActionPerformed([Ent.FILE, filename, Ent.MD5HASH, expected_md5], j, jcount)
|
||||||
|
Act.Set(action)
|
||||||
|
return True
|
||||||
|
entityActionFailedWarning([Ent.FILE, filename, Ent.MD5HASH, expected_md5], Msg.DOES_NOT_MATCH.format(actual_hash), j, jcount)
|
||||||
|
Act.Set(action)
|
||||||
|
return False
|
||||||
|
except IOError as e:
|
||||||
|
systemErrorExit(FILE_ERROR_RC, fileErrorMessage(filename, e))
|
||||||
|
|
||||||
def _getCloudStorageObject(s, bucket, s_object, localFilename, expectedMd5=None, zipToStdout=False, j=0, jcount=0):
|
def _getCloudStorageObject(s, bucket, s_object, localFilename, expectedMd5=None, zipToStdout=False, j=0, jcount=0):
|
||||||
if not zipToStdout:
|
if not zipToStdout:
|
||||||
localFilename = cleanFilepath(localFilename)
|
localFilename = cleanFilepath(localFilename)
|
||||||
@@ -42550,26 +42730,6 @@ def doPrintShowVaultExports():
|
|||||||
if csvPF:
|
if csvPF:
|
||||||
csvPF.writeCSVfile('Vault Exports')
|
csvPF.writeCSVfile('Vault Exports')
|
||||||
|
|
||||||
def md5MatchesFile(filename, expected_md5, j=0, jcount=0):
|
|
||||||
action = Act.Get()
|
|
||||||
Act.Set(Act.VERIFY)
|
|
||||||
try:
|
|
||||||
f = openFile(filename, 'rb')
|
|
||||||
hash_md5 = hashlib.md5()
|
|
||||||
for chunk in iter(lambda: f.read(4096), b""):
|
|
||||||
hash_md5.update(chunk)
|
|
||||||
closeFile(f)
|
|
||||||
actual_hash = hash_md5.hexdigest()
|
|
||||||
if actual_hash == expected_md5:
|
|
||||||
entityActionPerformed([Ent.FILE, filename, Ent.MD5HASH, expected_md5], j, jcount)
|
|
||||||
Act.Set(action)
|
|
||||||
return True
|
|
||||||
entityActionFailedWarning([Ent.FILE, filename, Ent.MD5HASH, expected_md5], Msg.DOES_NOT_MATCH.format(actual_hash), j, jcount)
|
|
||||||
Act.Set(action)
|
|
||||||
return False
|
|
||||||
except IOError as e:
|
|
||||||
systemErrorExit(FILE_ERROR_RC, fileErrorMessage(filename, e))
|
|
||||||
|
|
||||||
# gam copy vaultexport|export <ExportItem> matter <MatterItem>
|
# gam copy vaultexport|export <ExportItem> matter <MatterItem>
|
||||||
# [targetbucket <String>] [targetprefix <String>]
|
# [targetbucket <String>] [targetprefix <String>]
|
||||||
# [bucketmatchpattern <REMatchPattern>] [objectmatchpattern <REMatchPattern>]
|
# [bucketmatchpattern <REMatchPattern>] [objectmatchpattern <REMatchPattern>]
|
||||||
@@ -47372,7 +47532,7 @@ def doCreateInboundSSOCredential():
|
|||||||
if not profile:
|
if not profile:
|
||||||
return
|
return
|
||||||
elif myarg == 'pemfile':
|
elif myarg == 'pemfile':
|
||||||
pemData = readFile(getString(Cmd.OB_FILE_NAME))
|
pemData = readFile(setFilePath(getString(Cmd.OB_FILE_NAME), GC.INPUT_DIR))
|
||||||
elif myarg == 'generatekey':
|
elif myarg == 'generatekey':
|
||||||
generateKey = True
|
generateKey = True
|
||||||
elif myarg == 'replaceoldest':
|
elif myarg == 'replaceoldest':
|
||||||
@@ -73211,6 +73371,7 @@ def _draftImportInsertMessage(users, operation):
|
|||||||
filename = getString(Cmd.OB_FILE_NAME)
|
filename = getString(Cmd.OB_FILE_NAME)
|
||||||
if checkArgumentPresent('charset'):
|
if checkArgumentPresent('charset'):
|
||||||
emlEncoding = getString(Cmd.OB_CHAR_SET)
|
emlEncoding = getString(Cmd.OB_CHAR_SET)
|
||||||
|
filename = setFilePath(filename, GC.INPUT_DIR)
|
||||||
msgText = readFile(filename, encoding=emlEncoding)
|
msgText = readFile(filename, encoding=emlEncoding)
|
||||||
emlFile = True
|
emlFile = True
|
||||||
internalDateSource = 'dateHeader'
|
internalDateSource = 'dateHeader'
|
||||||
@@ -76042,8 +76203,7 @@ def createSmime(users):
|
|||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if myarg == 'file':
|
if myarg == 'file':
|
||||||
smimefile = getString(Cmd.OB_FILE_NAME)
|
smimeData = readFile(setFilePath(getString(Cmd.OB_FILE_NAME), GC.INPUT_DIR), mode='rb')
|
||||||
smimeData = readFile(smimefile, mode='rb')
|
|
||||||
body['pkcs12'] = base64.urlsafe_b64encode(smimeData).decode(UTF8)
|
body['pkcs12'] = base64.urlsafe_b64encode(smimeData).decode(UTF8)
|
||||||
elif myarg == 'password':
|
elif myarg == 'password':
|
||||||
body['encryptedKeyPassword'] = getString(Cmd.OB_PASSWORD)
|
body['encryptedKeyPassword'] = getString(Cmd.OB_PASSWORD)
|
||||||
@@ -78124,7 +78284,7 @@ def importTasklist(users):
|
|||||||
filename = getString(Cmd.OB_FILE_NAME)
|
filename = getString(Cmd.OB_FILE_NAME)
|
||||||
encoding = getCharSet()
|
encoding = getCharSet()
|
||||||
try:
|
try:
|
||||||
jsonData = json.loads(readFile(filename, encoding=encoding))
|
jsonData = json.loads(readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding))
|
||||||
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
||||||
Cmd.Backup()
|
Cmd.Backup()
|
||||||
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
|
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
|
||||||
@@ -78648,6 +78808,7 @@ MAIN_ADD_CREATE_FUNCTIONS = {
|
|||||||
Cmd.ARG_CHROMEPOLICYIMAGE: doCreateChromePolicyImage,
|
Cmd.ARG_CHROMEPOLICYIMAGE: doCreateChromePolicyImage,
|
||||||
Cmd.ARG_CHROMEPROFILECOMMAND: doCreateChromeProfileCommand,
|
Cmd.ARG_CHROMEPROFILECOMMAND: doCreateChromeProfileCommand,
|
||||||
Cmd.ARG_CIGROUP: doCreateCIGroup,
|
Cmd.ARG_CIGROUP: doCreateCIGroup,
|
||||||
|
Cmd.ARG_CIPOLICY: doCreateUpdateCIPolicy,
|
||||||
Cmd.ARG_CONTACT: doCreateDomainContact,
|
Cmd.ARG_CONTACT: doCreateDomainContact,
|
||||||
Cmd.ARG_COURSE: doCreateCourse,
|
Cmd.ARG_COURSE: doCreateCourse,
|
||||||
Cmd.ARG_COURSESTUDENTGROUP: doCreateCourseStudentGroups,
|
Cmd.ARG_COURSESTUDENTGROUP: doCreateCourseStudentGroups,
|
||||||
@@ -78768,6 +78929,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|||||||
Cmd.ARG_CHROMEPOLICY: doDeleteChromePolicy,
|
Cmd.ARG_CHROMEPOLICY: doDeleteChromePolicy,
|
||||||
Cmd.ARG_CHROMEPROFILE: doDeleteChromeProfile,
|
Cmd.ARG_CHROMEPROFILE: doDeleteChromeProfile,
|
||||||
Cmd.ARG_CIGROUP: doDeleteCIGroups,
|
Cmd.ARG_CIGROUP: doDeleteCIGroups,
|
||||||
|
Cmd.ARG_CIPOLICY: doDeleteCIPolicies,
|
||||||
Cmd.ARG_CLASSROOMINVITATION: doDeleteClassroomInvitations,
|
Cmd.ARG_CLASSROOMINVITATION: doDeleteClassroomInvitations,
|
||||||
Cmd.ARG_CONTACT: doDeleteDomainContacts,
|
Cmd.ARG_CONTACT: doDeleteDomainContacts,
|
||||||
Cmd.ARG_CONTACTPHOTO: doDeleteDomainContactPhoto,
|
Cmd.ARG_CONTACTPHOTO: doDeleteDomainContactPhoto,
|
||||||
@@ -79161,6 +79323,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|||||||
Cmd.ARG_CHATMESSAGE: doUpdateChatMessage,
|
Cmd.ARG_CHATMESSAGE: doUpdateChatMessage,
|
||||||
Cmd.ARG_CHROMEPOLICY: doUpdateChromePolicy,
|
Cmd.ARG_CHROMEPOLICY: doUpdateChromePolicy,
|
||||||
Cmd.ARG_CIGROUP: doUpdateCIGroups,
|
Cmd.ARG_CIGROUP: doUpdateCIGroups,
|
||||||
|
Cmd.ARG_CIPOLICY: doCreateUpdateCIPolicy,
|
||||||
Cmd.ARG_CONTACT: doUpdateDomainContacts,
|
Cmd.ARG_CONTACT: doUpdateDomainContacts,
|
||||||
Cmd.ARG_CONTACTPHOTO: doUpdateDomainContactPhoto,
|
Cmd.ARG_CONTACTPHOTO: doUpdateDomainContactPhoto,
|
||||||
Cmd.ARG_COURSE: doUpdateCourse,
|
Cmd.ARG_COURSE: doUpdateCourse,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ CLOUDIDENTITY_INBOUND_SSO = 'cloudidentityinboundsso'
|
|||||||
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
|
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
|
||||||
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
|
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
|
||||||
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
|
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
|
||||||
|
CLOUDIDENTITY_POLICY_BETA = 'cloudidentitypolicybeta'
|
||||||
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
|
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
|
||||||
CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
|
CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
|
||||||
CONTACTS = 'contacts'
|
CONTACTS = 'contacts'
|
||||||
@@ -245,6 +246,7 @@ _INFO = {
|
|||||||
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||||
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||||
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||||
|
CLOUDIDENTITY_POLICY_BETA: {'name': 'Cloud Identity API - Policy Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||||
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||||
CLOUDRESOURCEMANAGER: {'name': 'Cloud Resource Manager API v3', 'version': 'v3', 'v2discovery': True},
|
CLOUDRESOURCEMANAGER: {'name': 'Cloud Resource Manager API v3', 'version': 'v3', 'v2discovery': True},
|
||||||
CONTACTS: {'name': 'Contacts API', 'version': 'v3', 'v2discovery': False},
|
CONTACTS: {'name': 'Contacts API', 'version': 'v3', 'v2discovery': False},
|
||||||
@@ -398,6 +400,11 @@ _CLIENT_SCOPES = [
|
|||||||
'subscopes': READONLY,
|
'subscopes': READONLY,
|
||||||
'roByDefault': True,
|
'roByDefault': True,
|
||||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||||
|
{'name': 'Cloud Identity API - Policy Beta',
|
||||||
|
'api': CLOUDIDENTITY_POLICY_BETA,
|
||||||
|
'subscopes': [],
|
||||||
|
'offByDefault': True,
|
||||||
|
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||||
{'name': 'Cloud Identity API - User Invitations',
|
{'name': 'Cloud Identity API - User Invitations',
|
||||||
'api': CLOUDIDENTITY_USERINVITATIONS,
|
'api': CLOUDIDENTITY_USERINVITATIONS,
|
||||||
'subscopes': READONLY,
|
'subscopes': READONLY,
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
|||||||
DEVICE_MAX_RESULTS = 'device_max_results'
|
DEVICE_MAX_RESULTS = 'device_max_results'
|
||||||
# Domain obtained from gam.cfg or oauth2.txt
|
# Domain obtained from gam.cfg or oauth2.txt
|
||||||
DOMAIN = 'domain'
|
DOMAIN = 'domain'
|
||||||
# Google Drive download directory
|
# directory for file output
|
||||||
DRIVE_DIR = 'drive_dir'
|
DRIVE_DIR = 'drive_dir'
|
||||||
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
||||||
DRIVE_MAX_RESULTS = 'drive_max_results'
|
DRIVE_MAX_RESULTS = 'drive_max_results'
|
||||||
@@ -177,6 +177,8 @@ EXTRA_ARGS = 'extra_args'
|
|||||||
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
|
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
|
||||||
# Gmail CSE KACL wrapped key files
|
# Gmail CSE KACL wrapped key files
|
||||||
GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir'
|
GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir'
|
||||||
|
# directory for file input
|
||||||
|
INPUT_DIR = 'input_dir'
|
||||||
# When processing items in batches, how many seconds should GAM wait between batches
|
# When processing items in batches, how many seconds should GAM wait between batches
|
||||||
INTER_BATCH_WAIT = 'inter_batch_wait'
|
INTER_BATCH_WAIT = 'inter_batch_wait'
|
||||||
# When retrieving lists of licenses from API, how many should be retrieved in each chunk
|
# When retrieving lists of licenses from API, how many should be retrieved in each chunk
|
||||||
@@ -394,6 +396,7 @@ Defaults = {
|
|||||||
EXTRA_ARGS: '',
|
EXTRA_ARGS: '',
|
||||||
GMAIL_CSE_INCERT_DIR: '',
|
GMAIL_CSE_INCERT_DIR: '',
|
||||||
GMAIL_CSE_INKEY_DIR: '',
|
GMAIL_CSE_INKEY_DIR: '',
|
||||||
|
INPUT_DIR: '',
|
||||||
INTER_BATCH_WAIT: '0',
|
INTER_BATCH_WAIT: '0',
|
||||||
LICENSE_MAX_RESULTS: '100',
|
LICENSE_MAX_RESULTS: '100',
|
||||||
LICENSE_SKUS: '',
|
LICENSE_SKUS: '',
|
||||||
@@ -564,6 +567,7 @@ VAR_INFO = {
|
|||||||
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
|
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
|
||||||
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||||
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||||
|
INPUT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||||
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
|
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
|
||||||
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
|
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
|
||||||
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||||
|
|||||||
@@ -231,6 +231,11 @@ When specifying a `<UserMultiAttribute>` you have to specify all instances; ther
|
|||||||
|
|
||||||
You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribute>`.
|
You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribute>`.
|
||||||
```
|
```
|
||||||
|
<UserNoteContent> ::=
|
||||||
|
<String>|
|
||||||
|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
|
||||||
|
(gdoc|ghtml <UserGoogleDoc>)
|
||||||
|
|
||||||
<UserBasicAttribute> ::=
|
<UserBasicAttribute> ::=
|
||||||
(archive|archived <Boolean>)|
|
(archive|archived <Boolean>)|
|
||||||
(changepassword|changepasswordatnextlogin <Boolean>)|
|
(changepassword|changepasswordatnextlogin <Boolean>)|
|
||||||
@@ -244,9 +249,7 @@ You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribu
|
|||||||
(ipwhitelisted <Boolean>)|
|
(ipwhitelisted <Boolean>)|
|
||||||
(language clear|<LanguageList>)|
|
(language clear|<LanguageList>)|
|
||||||
(lastname|familyname <String>)|
|
(lastname|familyname <String>)|
|
||||||
(note clear|([text_html|text_plain] <String>|
|
(note clear|([text_html|text_plain] <UserNoteContent>))|
|
||||||
(file|htmlfile <FileName> [charset <Charset>])|
|
|
||||||
(gdoc|ghtml <UserGoogleDoc>)))|
|
|
||||||
(org|ou|orgunitpath <OrgUnitPath>|<OrgUnitID>)
|
(org|ou|orgunitpath <OrgUnitPath>|<OrgUnitID>)
|
||||||
(password (random [<Integer>])|(uniquerandom [<Integer>])|
|
(password (random [<Integer>])|(uniquerandom [<Integer>])|
|
||||||
blocklogin|
|
blocklogin|
|
||||||
|
|||||||
Reference in New Issue
Block a user