Teams planner tab add using graph API error - microsoft-graph-api

POST: https://graph.microsoft.com/beta/teams/teamsId/channels/channelId
Request body
{
"displayName": "Task management",
"teamsApp#odata.bind"
"https://graph.microsoft.com/beta/appCatalogs/teamsApps/com.microsoft.teamspace.tab.planner",
"configuration": {
"entityId": "",
"contentUrl": "",
"websiteUrl": "",
"removeUrl": ""
}
}
entityId = plan id
contentUrl = websiteUrl = removeUrl = https://tasks.office.com/{tenantName}/Home/PlannerFrame?page=7&planId={planId}
Graph API's docs referred
https://learn.microsoft.com/en-us/graph/teams-configuring-builtin-tabs
https://learn.microsoft.com/en-us/graph/api/teamstab-add?view=graph-rest-beta
But works fine if created using teams app or even update the same tab using settings.

Looking at the logs, the URL you are specifying for the contentUrl seems to be missing the "page=7" query parameter, which looks like the reason the page doesn't load.

Related

How to send a chat message of type 'announcement' to a Teams Channel via Microsoft Graph?

I'm trying to send a chatMessage of type 'announcement' to a Teams channel. However, I receive the following error message:
Attachment Id missing from the body.
Since the attachment id is read-only, I first tried to omit it. When I received the error, I tried to set a random id in the attachment and add this also to the body's content. In both cases I do receive the same error.
I have not found any example that shows how to send an announcement to a channel, so I hope to find help here.
I'm using PowerShell and the Microsoft.Graph module, but if you are able to provide help or even a solution using any other SDK or pure JSON/REST, I'm happy about that as well.
This is my code:
Select-MgProfile beta
Connect-MgGraph -Scopes "ChannelMessage.Send"
$attachments = #{
contentType = "application/vnd.microsoft.teams.messaging-announcementBanner"
content = #{
#id = "<Insert random id>"
title = "This is an important announcement"
cardImageType = "colorTheme"
cardImageDetails = '{"colorTheme":"periwinkleBlue"}'
}
}
$bodyContent = #'
<attachment id="<Insert random id>"></attachment>
<div>Hi all, ...</div>
'#
$params = #{
ChannelId = "..."
TeamId = "..."
Attachments = $attachments
Importance = "high"
Body = #{
ContentType = "html"
Content = $bodyContent
}
}
New-MgTeamChannelMessage #params
Can you please provide the link to the documentation? In my provided solution, I will assume that you reference to chatMessageAttachment.
{
"id": "string (identifier)",
"contentType": "string",
"contentUrl": "string",
"content": "string",
"name": "string",
"thumbnailUrl": "string"
}
From the documentation.
{
"contentType": "application/vnd.microsoft.teams.messaging-announcementBanner",
"content": {
"id": "<Insert random id>",
"title": "This is an important announcement",
"cardImageType": "colorTheme",
"cardImageDetails": "{\"colorTheme\":\"periwinkleBlue\"}"
}
}
From your powershell script.
content is defined as string, not as an object. I guess, you have to stringify the json and add it into content.
{
"id": "<insert id>",
"contentType": "application/vnd.microsoft.teams.messaging-announcementBanner",
"content": "{\"id\":\"<Insert random id>\",\"title\":\"This is an important announcement\",\"cardImageType\":\"colorTheme\",\"cardImageDetails\":\"{\"colorTheme\":\"periwinkleBlue\"}\"}"
}

How can I set the email for a Team with Microsoft Graph API?

We are creating a team group (with the beta Graph API) and we want the emailaddress to contain another value than the value that's based on what's provided in displayName.
While searching through the documentation it seems that this is possible by providing a value for mailNickname in AdditionalData (https://learn.microsoft.com/en-us/graph/teams-create-group-and-team).
So I implemented that. Unfortunately the mailaddress and the alias were still like TestGroup#domain.nl instead of TestMailNickname#domain.nl.
var graphApiServiceClient = new GraphServiceClient(this.authenticationProvider)
{
BaseUrl = "https://graph.microsoft.com/beta"
};
var owner = "valueForOwner";
var teamTemplate = teamTemplateType == TeamTemplateType.Staff
? "educationStaff"
: "educationClass";
var team = new Team
{
AdditionalData = new Dictionary<string, object>
{
{ "template#odata.bind", $"https://graph.microsoft.com/beta/teamsTemplates('{teamTemplate}')" },
{ "owners#odata.bind", new[]{$"https://graph.microsoft.com/beta/users('{owner}')"}},
{ "displayName", "TestGroup" },
{ "description", "This is a testgroup" },
{ "mailNickname", "TestMailNickname" }
}
};
await graphApiServiceClient.Teams.Request().AddAsync(team);
The MailNickname does change when I update the MailNickname property afterwards with an update request like await graphApiServiceClient.Groups[objectId].Request().UpdateAsync(new Group { MailNickname = mailNickname});.
This is confirmed with a graphApiServiceClient.Groups[objectId].Request().GetAsync()
Unfortunately it still shows TestGroup#domain.nl as the alias in the admin at https://admin.microsoft.com/AdminPortal/Home#/groups.
But, updating the value like this doesn't work for the Mail property because it states it's readonly in the update request.
Does anyone know what I am doing wrong in my original create/add request?
Plus does anyone know why the old alias value is still shown instead of the updated alias at https://admin.microsoft.com/AdminPortal/Home#/groups?
This is possible by creating a group via the Graph api first and then using the group ID to create a team for this group.
Create the group via "https://graph.microsoft.com/v1.0/groups".
{
"displayName": "TestGroup",
"mailNickname": "TestMailNickname",
"mailEnabled": true,
"securityEnabled": false,
"description": "This is a testgroup",
"groupTypes": [
"Unified"
],
"owners#odata.bind": [
"https://graph.microsoft.com/v1.0/users/OWNEDID"
]
}
Once the group has been made it'll output an ID which you'll then use to create a team via "https://graph.microsoft.com/v1.0/groups/YOURIDHERE/team"
Body would look something like;
{
"memberSettings": {
"allowCreateUpdateChannels": false,
"allowAddRemoveApps": false,
"allowCreateUpdateRemoveTabs": false,
"allowCreateUpdateRemoveConnectors": false
}
}
It looks like you are using an EDU tenant, since you are referencing the particular templates.
I have tested this previously, and the method suggested above will not work with the "EducationClass" template.
The way I got it to work was:
Create the Team with the template
$Teamdata =
'{
"displayName":"' + $newteamName + '",
"description":"' + $newteamName + '",
"template#odata.bind": "https://graph.microsoft.com/v1.0/teamsTemplates(\u0027educationClass\u0027)",
"hideFromOutlookClients": "true",
"hideFromAddressLists": "true",
"members":[
{
"#odata.type":"#microsoft.graph.aadUserConversationMember",
"roles":[
"owner"
],
"user#odata.bind":"'+ $DefaultOwnerURL + '"
}
]
}'
Wait for the group to be created (usually about 20 seconds)
patch the group mailnickname
$Body_SetGroupSDSSettings =
'{
"mailNickname": "' + $newteamMailNickname + '"
}'
It isn't perfect, but it is the best way I could find to do this.

Branch.io initSession returns empty BranchUniversalObject JSON and BranchLinkProperties JSON

I'm trying to add a sharing feature to my existing app using Unity Branch SDK.
I created the short URL using the following code.
BranchUniversalObject universalObject = new BranchUniversalObject();
BranchLinkProperties linkProperties = new BranchLinkProperties();
string deviceUniqueIdentifier = SystemInfo.deviceUniqueIdentifier;
universalObject.canonicalIdentifier = $"xLeague/{deviceUniqueIdentifier}";
universalObject.canonicalUrl = "http://xleague.games/";
universalObject.title = MetaDataManager.Title;
universalObject.contentDescription = MetaDataManager.Description;
universalObject.imageUrl = MetaDataManager.Image;
linkProperties.controlParams.Add("$desktop_url", "http://xleague.games/xleague-solitaire-share/");
linkProperties.controlParams.Add("$android_url", "http://xleague.games/android");
linkProperties.controlParams.Add("$ios_url", "https://apps.apple.com/us/app/xleague-solitaire/id1480117114");
linkProperties.controlParams.Add("$ipad_url", "https://apps.apple.com/us/app/xleague-solitaire/id1480117114");
linkProperties.controlParams.Add("$match_duration", "2000");
linkProperties.controlParams.Add("$og_title", MetaDataManager.Title);
linkProperties.controlParams.Add("$og_description", MetaDataManager.Description);
linkProperties.controlParams.Add("$og_image_url", MetaDataManager.Image);
linkProperties.controlParams.Add("$deviceID", deviceUniqueIdentifier);
Branch.getShortURL(universalObject, linkProperties, (param, error) =>
{
if (error != null)
{
Debug.LogError("Branch.getShortURL failed: " + error);
}
else if (param != null)
{
callback(param);
}
});
I can get the following short URLs from Unity Branch.
https://xleague.app.link/r7DxT2wkZ5
https://xleague.app.link/4WPwY7jm05
https://xleague.app.link/LFfAOZuo05
https://xleague.app.link/hVR3fr0n05
And I retrieve BranchUniversalObject and BranchLinkProperties using the following code.
void Start()
{
Branch.initSession(CallbackWithBranchUniversalObject);
}
void CallbackWithBranchUniversalObject(BranchUniversalObject buo, BranchLinkProperties linkProps, string error)
{
if (error != null)
Debug.Log($"Error : {error}");
else
{
Debug.Log(buo.ToJsonString());
Debug.Log(linkProps.ToJsonString());
if (linkProps.controlParams.ContainsKey("$deviceID"))
{
string senderDeviceID = linkProps.controlParams["$deviceID"];
...
}
}
}
If I click the shared link on iPhone or Android, it will redirect me to Appstore or Google Play Store. I can install the app on the Appstore and Google Play Store.
But when I opened the app, initSession returns empty BranchUniversalObject JSON and BranchLinkProperties JSON.
BranchUniversalObject JSON
{
"$canonical_identifier": "",
"$canonical_url": "",
"$og_title": "",
"$og_description": "",
"$og_image_url": "",
"$publicly_indexable": "0",
"$locally_indexable": "0",
"$exp_date": "69425078400000",
"$keywords": [],
"metadata": "{}"
}
BranchLinkProperties JSON
{
"~tags": [],
"~feature": "",
"~alias": "",
"~channel": "",
"~stage": "",
"~duration": "0",
"control_params": {}
}
What makes me more confused is that initSession sometimes returns correct BranchUniversalObject JSON and BranchLinkProperties JSON as expected. I guess the Branch Short URL that I created only works for the first click.
I'd be much appreciated you if you could help me.
A Branchster Here -
Please double check your integration as mentioned here. For further troubleshooting you can also follow as mentioned here. If this is happening when you click a link from background please make sure that you are following the intra-app linking guidelines and make sure onNewIntent is implemented as recommended.
Please note thaT when a link is clicked, it should only open the Activity which has the initSession() method. Also please don't share your Branch Keys publicly.

Programmatically adding SharePoint Library Teams Tab to Microsoft Teams Channel

I'm trying to add a SharePoint Library tab to a Microsoft Team Channel programmatically through the Microsoft Graph.
Here is the payload I'm sending through the Graph Explorer POST
{
"teamsAppId": "com.microsoft.teamspace.tab.files.sharepoint",
"name": "Documents3",
"sortOrderIndex": "10300",
"configuration": {
"siteUrl": "https://baywet.sharepoint.com/sites/customerhub",
"libraryServerRelativeUrl": "/sites/customerhub/Shared Documents",
"selectedDocumentLibraryTitle": "Documents",
"selectedSiteTitle": "customerhub",
"dateAdded": "2018-10-05T16:56:59.169Z"
}
}
I get a 201 status response, my tab is added to the channel. However whenever somebody tries to upload a file from the Teams UI, they get the following error message The File {filename} is missing. If they click on Open in SharePoint and then upload the file, it works.
If I compare with a tab created through the UI (which works properly) here is the description I get.
{
"id": "a68e34db-9d43-4821-953b-2dec938ce785",
"name": "Document%20Library",
"teamsAppId": "com.microsoft.teamspace.tab.files.sharepoint",
"sortOrderIndex": "10200",
"webUrl": "https://teams.microsoft.com/l/channel/19%3ab2e05a0aae42487485b13e088d5d2f0f%40thread.skype/tab%3a%3aa63916e6-f252-477d-9696-7934980e7e47?label=Document%2520Library&groupId=71ed6a2e-67ca-4930-a3c2-abb25ca29fbf&tenantId=bd4c6c31-c49c-4ab6-a0aa-742e07c20232",
"configuration": {
"entityId": null,
"contentUrl": null,
"removeUrl": null,
"websiteUrl": null,
"siteUrl": "https://baywet.sharepoint.com/sites/customerhub",
"libraryServerRelativeUrl": "/sites/customerhub/Shared Documents",
"libraryId": "706FAD5678484E7B93B0855E52A0BCD9",
"selectedDocumentLibraryTitle": "Documents",
"selectedSiteImageUrl": "https://baywet.sharepoint.com/sites/customerhub/_api/GroupService/GetGroupImage?id='f9d430ca-4de3-42f1-9474-1427bfdb16b0'&hash=636743460492415245",
"selectedSiteTitle": "customerhub",
"dateAdded": "2018-10-05T16:56:59.169Z"
}
}
The only difference being the libraryId configuration value. (you're not supposed to send in the webUrl and id).
This library id doesn't match the library id in SharePoint, or the drive item id in the Graph so my question is: what value am I supposed to set for the libraryId? Is there anything else I am missing?
The following code Creates the team for a known Group ID (365 that When created, created a team site) and adds 3 tabs on the existing channel.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(GraphClientId)
.WithTenantId(GraphTenantId)
.WithClientSecret(GraphClientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var team = new Team
{
MemberSettings = new TeamMemberSettings
{
AllowCreateUpdateChannels = true
},
MessagingSettings = new TeamMessagingSettings
{
AllowUserEditMessages = true,
AllowUserDeleteMessages = true
},
FunSettings = new TeamFunSettings
{
AllowGiphy = true,
GiphyContentRating = GiphyRatingType.Moderate
}
};
Team addedTeam = await graphClient.Groups[GroupID].Team
.Request()
.PutAsync(team);
ECGTeam ecgTeam = new ECGTeam {ProjectNumber= ProjectNumber ,GroupID = GroupID, TeamID = addedTeam.Id };
string channelID = string.Empty;
var channels = await graphClient.Teams[addedTeam.Id].Channels.Request().GetAsync();
channelID = channels[0].Id;
ecgTeam.ChannelID = channelID;
TeamsTab newTab = addTab(targetWebUrl, "WorkingPapers", "Working Papers");
var addedTab = await graphClient.Teams[addedTeam.Id].Channels[channelID].Tabs.Request().AddAsync(newTab);
ecgTeam.TabWorkingPapersID = addedTab.Id;
//DPC documents
newTab = addTab(targetWebUrl, "DPCdocuments", "DPC documents");
addedTab = await graphClient.Teams[addedTeam.Id].Channels[channelID].Tabs.Request().AddAsync(newTab);
ecgTeam.TabDPCdocumentsID=addedTab.Id;
//ContractDocuments //
newTab = addTab(targetWebUrl, "ContractDocuments", "Contract Documents");
addedTab = await graphClient.Teams[addedTeam.Id].Channels[channelID].Tabs.Request().AddAsync(newTab);
ecgTeam.TabContractDocumentsID = addedTab.Id;
//log.LogInformation(addedTab.Id);
Now, If you can help me create a Library for the said site that uses a custom content type, I will buy you coffee :-)
The wealth of good documentation for MS Graph in .NET Core makes me want to cry :-(
I found the solution a while after, sorry for not posting here earlier.
POST https://graph.microsoft.com/beta/teams/{groupId}/channels/{channelId}/tabs
{
"teamsApp#odata.bind": "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.files.sharepoint",
"name": "test",
"sortOrderIndex": "10400",
"configuration": {
"contentUrl": "https://baywet.sharepoint.com/sites/NYC/test"
}
}
Where contentUrl is the URL of the document library

Send email at a later time

I am wondering if the /v1.0/me/sendMail has the ability to delay sending an email. In the Outlook client, you can specify that you want your email sent at a later date and time. I've snooped around to see if there is a property that can be set on the message object to indicate this.
Did anyone find a way to get this working? Of course, I could implement something in my software to handle the delayed sending, but why re-create something if it is already there.
You can achieve delayed sending of emails using extended properties. These can be set on the Graph API request payload using the "singleValueExtendedProperties" attribute.
The property to use is PidTagDeferredSendTime which has the ID 0x3FEF and type SystemTime.
The id attribute of "singleValueExtendedProperties" takes different formats depending on the property you are setting.
For the deferred send time you would use SystemTime 0x3FEF.
Example using a HTTP JSON POST Payload:
{
"message": {
"subject": "Meet for lunch?",
"body": {
"contentType": "Text",
"content": "The new cafeteria is open."
},
"toRecipients": [
{
"emailAddress": {
"address": "bob#contoso.com"
}
}
],
"singleValueExtendedProperties":
[
{
"id":"SystemTime 0x3FEF",
"value":"2019-01-29T20:00:00"
}
]
}
}
Example using the Microsoft Graph API client library:
var client = /* Create and configure GraphServiceClient */;
var msg = new Message();
msg.ToRecipients = List<Recipient>();
msg.ToRecipients.Add(new Recipient() {
EmailAddress = new EmailAddress() { Address ="bob#contoso.com" }
};
msg.Subject = "Meet for lunch?";
msg.Body = new ItemBody()
{
Content = "The new cafeteria is open.",
ContentType = BodyType.Text,
};
msg.SingleValueExtendedProperties = new MessageSingleValueExtendedPropertiesCollectionPage();
msg.SingleValueExtendedProperties.Add(new SingleValueLegacyExtendedProperty()
{
Id = "SystemTime 0x3FEF",
Value = DateTime.UtcNow.AddMinutes(5).ToString("o")
});
await client.Me.SendMail(msg, true).Request().PostAsync();
https://gallery.technet.microsoft.com/office/Send-Emails-until-a-9cee20cf
You set the deferred send time extended prop when creating the item.

Resources