Hier heb je eveneens build-in, apps script en rest API's
Het gebruik van de class Calendar kan met de advanced API.
Echter voor het toevoegen van een agenda aan een gebruiker, zodat deze ook automatisch in zijn calendar lijst verschijnt zonder dat hij deze moet aanvaarden heb je admin rechten nodig, ook voor cursisten die door jou worden toegevoegd aan een classroom is dit het geval.
Het principe is om een calendar te delen, met de gebruiker met bepaalde rechten, zonder dat hij verwittigd wordt (sendNotifications=false) en nadien door een Domain wide delegation de calendar toe te voegen en zichtbaar te maken in zijn calendar lijst.
Hiervoor heb je een service account nodig: De procedeure staat verklaard op:: yagisanatode.com
deze functie maakt een map van alle gebruikers en hun rechten op de verschillende calendars.
Deze rechten kunnen bestaan als owner, reader en writer.
Met Calendar.CalendarList.list kan je een lijst opvragen van de calendars van een gebruiker.
Het is zinvol dat iemand met de nodige rechten dit doet om alle calendars die aangemaakt zijn op domain niveau te kunnen zien.
Met de Calendar.Acl.list kan je daarna per calendar de rechten van iedere gebruiker terug vinden.
Deze vind je terug in de items.id/items.role
Vermits de acl lijst ook generieke toegangen als gebruikers weergeeft dien je deze te filteren.
Met de volgende code haal je de "echte" gebruikers eruit:
let User = acls[ac][0] //id
// zoek uit of de user een echte user is van het domain of een virtuele van Google.
if (User != "default") {
let U = User.split(":")
let re = '@myuba\.be$'
let MyubaUser = new RegExp(re).test(U[1])
if (MyubaUser == true) {
/**
* Lists the calendars shown in the user's calendar list.
* @see https://developers.google.com/calendar/api/v3/reference/calendarList/list
*/
function listCalendars() {
let calendars;
let pageToken;
do {
calendars = Calendar.CalendarList.list({
maxResults: 100,
pageToken: pageToken
});
if (!calendars.items || calendars.items.length === 0) {
Logger.log('No calendars found.');
return;
}
// Print the calendar id and calendar summary
for (const calendar of calendars.items) {
Logger.log('%s (ID: %s)', calendar.summary, calendar.id);
}
pageToken = calendars.nextPageToken;
} while (pageToken);
Logger.log('einde')
}
Deze functie lijst alle calenders op, waarop een user van het domain toegang heeft.
Hiermee worden alle calenders gefilterd met generieke google toegang, zoals holidays (default) etc en kalenders die toegekend zijn aan resources, behalve wanneer er specifieke user access op is.
Met een regex zien we of de kalender toebehoort aan een gebruiker van het domain, slechts dan worden de gegeven gebruikt.
De summary bevat een leesbare naam.
Vermits iedere user wordt vooraf gegaan door een type, worden deze gegeven ook gefilterd.
Dit kan een domain, group, user zijn, zo kan je group adressen herkennen.
Wanneer het een group adres betreft dat aan het domain is toegekend, kan het komen van een group mail, maar ook van een classroom.
Bij classrooms worden 2 adressen gebruikt, met verschillende toegangsrechten op de calendar: teachers en users.
vb:
b_ears_opleiding_nl_b_ears_97c4f3cd@myuba.be met role=reader
b_ears_opleiding_nl_b_ears_teachers_6845a634@myuba.be met role=owner
} while (pageToken);
//return object
let CalList = {}
if (!CalList.name) { CalList.name = [] }
if (!CalList.id) { CalList.id = [] }
if (!CalList.usertype) { CalList.usertype = [] }
if (!CalList.userid) { CalList.userid = [] }
if (!CalList.role) { CalList.role = [] }
let idx = 0
for (cal = 0; cal < SharedCalendarList.length; cal++) {
let acls = getCalRolesUsers_(SharedCalendarList[cal].getId())
if (acls) {
for (ac = 0; ac < acls.length; ac++) {
let User = acls[ac][0] //id
// zoek uit of de user een echte user is van het domain of een virtuele van Google.
if (User != "default") {
let U = User.split(":")
let re = '@myuba\.be$'
let MyubaUser = new RegExp(re).test(U[1])
if (MyubaUser == true) {
CalList.name[idx] = SharedCalendarList[cal].getSummary()
CalList.id[idx] = SharedCalendarList[cal].getId()
CalList.usertype[idx] = U[0]
CalList.userid[idx] = U[1]
CalList.role[idx] = acls[ac][1] //role
idx++
}
}
}
Logger.log(SharedCalendarList[cal].getSummary() + " " + ac)
}
}
return CalList
Deze functie haalt via de ACL lijst alle gebruikers op die toegang hebben tot de calendar.
Dit betreft ook alle google gebruikers of specifieke door google aangemaakte gebruikers.
De list wordt gebruikt in de vorige functie on de eigen gebruikers te filteren.
/**helperfunction
* function getCalRolesUsers_(calId)
* get all users form a calendar and their role
* @param {string} calId Calendar ID
*
* @retuns {string} roles Optional permissions, default = "reader":
* "none, "freeBusyReader", "reader", "writer", "owner"
*
*/
function getCalRolesUsers_(calId) {
var acl = null
try {
// Check whether there are already acl rules for this calendar
var acls = Calendar.Acl.list(calId)
}
catch (e) {
return false
// no existing acl record for calendar.
}
// get all rules in 2d array userId/role
let ACLS = []
if (acls) {
for (a = 0; a < acls.items.length; a++) {
ACLS.push([acls.items[a].id, acls.items[a].role])
}
}
return ACLS
}
Deze functie maakt een 2d map die je naar een sheet kan exporteren met de functie ObjectToTable()
De kolommen bestaan uit alle calendars die users hebben van je domain, gemaakt in de vorige functies
De rijen bestaan uit de gebruikte users.
/**
* Map Users to Calendars
* @param {object} SS, object of calendar list:summary, calid, usertype, userid, role
* @return {object} obj, object with map colums= calendars, rows = users, field=role
*/
function MapCalendarUserRoles(SS) {
let obj = {}
if (!obj.User) { obj.User = [] }
// create Cal properties of object
for (c = 0; c < SS.name.length; c++) {
if (!obj[SS.name[c]]) { obj[SS.name[c]] = [] }
}
// loop through every record and assign call property
for (i = 0; i < SS.name.length; i++) {
let IDX = obj.User.indexOf(SS.userid[i])
if (IDX == -1) {
obj.User.push(SS.userid[i])
IDX = obj.User.indexOf(SS.userid[i])
}
obj[SS.name[i]][IDX] = SS.role[i]
}
return obj
}
Wanneer we voor een individuele gebruiker de toegang tot een calendar dienen aan te passen, kan dit eveneens met een aantal functies.
Alvorens een aanpassing te doen van een gebruiker gaan we in alle calendars waarin een gebruiker rechten heeft, zijn rechten verwijderen.
Dit moet verhinderen dat er "restproducten" over blijven en de toegang tot bepaalde calendars blijft bestaan.
Je kan natuurlijk ook enkel de betreffende calendar, waarop je de rechten aanpast, benanderen op dezelfde wijze: eerst rechten afnemen en dan terug bijvoegen.
removeUserCalendar_(calId, userId) en removeCalendarFromUserList(userId, calId) zorgen voor het verwijderen van de gebruiker.
Nadien wordt de user toegevoed dmv updateUserCalendar_(calId, user, role) en addCalendarToUserList(user, calendar).
Beide zorgen voor het toevoegen van de gebruiker aan de calendar en dat de calendar automatisch zichtbaar wordt in zijn calendars.
Deze functie neemt bestaande rechten voor een bepaalde gebruiker af en voegt nieuwe rechten toe.
Dit heeft de uitwerking van een update functie.
We gebruiken de Map van de calendars om een bepaalde efficientie in te bouwen, zoniet zou er voor iedere user onnoemelijk tijd verloren gaan voor het doorzoeken van alle calendars.
/**helperfunction
* UpdateCalendarAccess_(SS, userId)
* user die behoort tot een provincie, tevens heeft hij de functie van manager en heeft toegang tot de calendar van de provincie.
* @param {object} SS, object met sheets van alle data *
* @param {string} userId, email user
*
*/
function UpdateCalendarAccess_(SS, userId) {
let IDX = SS.MapCalendarUsers.User.indexOf(userId)
let MBU = SS.MyubaBulkUsers["Email Address [Required]"].indexOf(userId)
var keys = Object.keys(SS.MapCalendarUsers);
//Als de user tot een bepaalde calender reeds toegang heeft, dient dit verwijdert te worden als hij geen eigenaar is en niet meer tot die groep behoort..
if (MBU != -1) {
var provCal = "B-EARS-" + SS.MyubaBulkUsers["Department"][MBU]
} else {
var provCal = ""
}
if (IDX != -1) {
// loop door alle calendars en haal hem weg waar hij geen owner is of niet bij hoort
for (i = 1; i < keys.length; i++) {
if (keys[i] != "B-EARS Events" && keys[i] != provCal) {
let role = SS.MapCalendarUsers[keys[i]][IDX]
if (role != "owner" && role != "" && role != undefined) {
let calId = CalendarApp.getCalendarsByName(keys[i])[0].getId() //get calendar by name, returns array of calendars, take first element.
removeUserCalendar_(calId, userId)
removeCalendarFromUserList(userId, calId)
}
}
}
}
// Maak toegang tot agenda met bepaalde rechten
// De cal die gebruikt wordt is de B-EARS-Events en deze van de provicie waartoe de gebruiker behoort as read
// als de user een manager is, dan krijgt hij writer access.
//let MBU = SS.MyubaBulkUsers["Email Address [Required]"].indexOf(userId)
if (MBU != -1) {
//Enkel B-EARS leden hebben toegang tot generieke agenda's
if (SS.MyubaBulkUsers["Org Unit Path [Required]"][MBU] == "/B-EARS") {
let calId = CalendarApp.getCalendarsByName("B-EARS Events")[0].getId()
if (SS.MyubaBulkUsers["Department"][MBU] != "") {
var provCalName = "B-EARS-" + SS.MyubaBulkUsers["Department"][MBU]
var provcalId = CalendarApp.getCalendarsByName(provCalName)[0].getId()
}
// Manager hebben writer toegang
if (SS.MyubaBulkUsers["Employee Title"][MBU] != "MEMBER") {
// Toegang tot B-EARS Event als die al niet bestaat.
if (SS.MapCalendarUsers["B-EARS Events"][IDX] != "writer") {
updateUserCalendar_(calId, userId, "writer")
addCalendarToUserList(userId, calId)
}
// Toegang tot provinciaal calendar
if (SS.MyubaBulkUsers["Department"][MBU] != "") {
if (SS.MapCalendarUsers[provCalName][IDX] != "writer") {
updateUserCalendar_(provcalId, userId, "writer")
addCalendarToUserList(userId, provcalId)
}
}
} else {
// Toegang tot B-EARS Event
if (SS.MapCalendarUsers["B-EARS Events"][IDX] != "reader") {
updateUserCalendar_(calId, userId, "reader")
addCalendarToUserList(userId, calId)
}
// Toegang tot provinciaal calendar
if (SS.MyubaBulkUsers["Department"][MBU] != "") {
if (SS.MapCalendarUsers[provCalName][IDX] != "reader") {
updateUserCalendar_(provcalId, userId, "reader")
addCalendarToUserList(userId, provcalId)
}
}
}
}
Logger.log(`Calendars updated for ${userId}`)
}
}
Deze functie laat je toe om in een calendar een bepaalde user te wissen.
Doordat users ook "niet-callsign" kunnen hebben en omdat we de fuctie willen gebruiken om via een loop een lijst van alle mogelijke users te kunnen doorlopen, voorzien we een check via een regex om te zien of het een echte user betreft of een groep email etc.
De userId moet een volledige Id zijn, dus bestaande uit de usertype+userid.
Dit wordt user:on5clm@myuba.be voor een user acl lijst in de calendar.
Het vewijderen van een user is enkel nodig indien hij geen toegang meer mag hebben tot een agenda, anders plaats je de status op "none", zodat hij geen access heeft maar toch nog tot de acl lijst behoort.
Owner verwijder je best niet, waardoor deze check ook in de code is voorzien.
The role assigned to the scope. Possible values are:
"none" - Provides no access.
"freeBusyReader" - Provides read access to free/busy information.
"reader" - Provides read access to the calendar. Private events will appear to users with reader access, but event details will be hidden.
"writer" - Provides read and write access to the calendar. Private events will appear to users with writer access, and event details will be visible.
"owner" - Provides ownership of the calendar. This role has all of the permissions of the writer role with the additional ability to see and manipulate ACLs.
/**helperfunction
* Remove User from ACl list
* @param {string} calId, calendar Id
* @param {string} userid, id of user to remove
*/
function removeUserCalendar_(calId, userId) {
let acls = getCalRolesUsers_(calId)
if (acls) {
for (ac = 0; ac < acls.length; ac++) {
//owners kunnen niet verwijdert worden.
if (acls[ac][1] != "owner") {
let User = acls[ac][0] //id
// zoek uit of de user een echte user is van het domain of een virtuele van Google. Echte user is callsign@myuba.be
if (User == "user:" + userId) {
let U = User.split(":")
if (U[0] == "user") {
let re = '((on|ot)([0-9]{1})([a-z]{1,3}))|((onl)([0-9]{1,5}))$'
// let re = '@myuba\.be$'
let MyubaUser = new RegExp(re).test(U[1])
if (MyubaUser == true) {
Calendar.Acl.remove(calId, User)
}
}
}
}
}
}
}
Deze REST API aanroep zorgt voor het verwijderen van de zichtbaarheid van de calendar in de user calendar lijst, door de calendar hidden en not selected te plaatsen.
De nodige admin credentials zijn nodig, om dit mogelijk te maken en worden verkregen via de OAuth service
/**
* removeCalendarFromUserList(user, calendar)
* Remove the calendar from the the users calendar list and
* select the calendar.
* your calendar with the user.
* @param{string} calId - The calendar ID to be shared.
* @param{string} user - the user's email.
* @returns{Object} Containing details of the added calendar.
*/
function removeCalendarFromUserList(user, calendar) {
let service = getOAuthService2(user);
service.reset();
if (service.hasAccess()) {
const content = JSON.stringify({
'id': calendar,
'hidden': true,
'selected': false
});
const endPoint = 'https://www.googleapis.com/calendar/v3/users/me/calendarList';
const response = UrlFetchApp.fetch(endPoint, {
method: 'POST',
headers: {
"Content-type": "application/json",
"Authorization": 'Bearer ' + service.getAccessToken(),
},
payload: content
});
//console.log(response.getContentText());
return response.getContentText();
};
Deze subfunctie kijkt eerst of er een acl lijst voor deze gebruiker bestaat. (try-catch)
Indien de acl rule niet bestaat, wordt deze nu aangemaakt door het definieren van een user scope en role.
acl = {
"scope": {
"type": "user",
"value": user
},
"role": role
};
Var newRule = Calendar.Acl.insert(acl, calId, { sendNotifications: false })
Met de call wordt de rule aangemaakt (insert) of aangepast (update).
De optie sendNotifications: false moet er voor zorgen dat de gebruiker geen extra email krijgt ikv deze aanpassing om deel te nemen aan deze agenda.
/**helperfunction
* updateUserCalendar_(calId, user, role)
* Set up calendar sharing for a single user. Refer to
* https://developers.google.com/google-apps/calendar/v3/reference/acl/insert.
*
* @param {string} calId Calendar ID
* @param {string} user Email address to share with
* @param {string} role Optional permissions, default = "reader":
* "none, "freeBusyReader", "reader", "writer", "owner"
*
* @returns {aclResource} See https://developers.google.com/google-apps/calendar/v3/reference/acl#resource
*/
function updateUserCalendar_(calId, user, role) {
role = role || "reader";
var acl = null;
// Check whether there is already a rule for this user
try {
var acl = Calendar.Acl.get(calId, "user:" + user);
}
catch (e) {
// no existing acl record for this user - as expected. Carry on.
}
if (!acl) {
// No existing rule - insert one.
acl = {
"scope": {
"type": "user",
"value": user
},
"role": role
};
var newRule = Calendar.Acl.insert(acl, calId, { sendNotifications: false });
}
else {
// There was a rule for this user - update it.
if (acl.role != "owner") {
acl.role = role;
newRule = Calendar.Acl.update(acl, calId, acl.id)
}
}
return newRule;
}
In deze functie wordt dmv de REST api gebruik gemaakt van een service account , wat de scope calendar toe laat en ADMIN rechten heeft door de domain wide delegation.
Door de content settings aan te passen komt de calendar automatisch zichbaar in de " andere agenda's" van een gebruiker.
Indien de betrokken gebruiker rechten heeft om de events aan te passen (writer of owner) dan kan hij ook in deze agenda afspraken maken en mensen uitnodigen.
Je kan standaard heel wat mensen deze agenda laten raadplegen (reader) en slechts enkele de afspraken laten beheren (writer).
De OAUth2 lib moet geintegreerd zijn en in de AUTH const zet je de credentials.(let op, code script niet publiek delen, want zo deel je ook je credentials)
Of je gebruikt de truc, die ook gebruikt wordt via de document properties voor de toegang tot de FireStore.
function getOAuthService2(user) {
return OAuth2.createService('Service Account')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
.setPrivateKey(AUTH.private_key)
.setIssuer(AUTH.client_email)
.setSubject(user)
.setPropertyStore(PropertiesService.getScriptProperties())
.setParam('access_type', 'offline')
.setScope('https://www.googleapis.com/auth/calendar');
}
/**
* Add the calendar to the the users calendar list and
* select the calendar.
* your calendar with the user.
* @param{string} calId - The calendar ID to be shared.
* @param{string} user - the user's email.
* @returns{Object} Containing details of the added calendar.
*/
function addCalendarToUserList(user, calendar) {
let service = getOAuthService2(user);
service.reset();
if (service.hasAccess()) {
const content = JSON.stringify({
'id':calendar,
'hidden':false,
'selected': true
});
const endPoint = 'https://www.googleapis.com/calendar/v3/users/me/calendarList';
const response = UrlFetchApp.fetch(endPoint, {
method:'POST',
headers: {
"Content-type": "application/json",
"Authorization": 'Bearer ' + service.getAccessToken(),
},
payload: content
});
console.log(response.getContentText());
return response.getContentText();
};
};