How to use sharepoint online search api as app-only - sharepoint

I'm trying to use the sharepoint online search api (/_api/search) from an application. Adding it from the Azure portal I see the search is only in the "delegated permissions" section. When I ran it in testing with the user login and approve it it works well.
Since I don't want to need a user to login for this, I found this article
https://blogs.msdn.microsoft.com/vesku/2016/03/07/using-add-in-only-app-only-permissions-with-search-queries-in-sharepoint-online/
That made me believe it would be possible to use search as an app-only and not as a user. I followed all the steps, created the app through appregnew.aspx , I also added another permission via appinv.aspx so the permissions I asked for are the following :
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
<AppPermissionRequest Scope="http://sharepoint/search" Right="QueryAsUserIgnoreAppPrincipal" />
</AppPermissionRequests>
I'm testing using ADAL JAVA SDK as follows:
Future<AuthenticationResult> future = context.acquireToken(
resource, new ClientCredential(clientId,
clientSecret), null);
where resource is xxxxxx.sharepoint.com and I'm later using this token as the bearer token.
But when I'm trying to test this I get the following error:
2018-08-05 11:03:22 WARN ODataUtils:120 - Failed to get a successful response for uri [https://XXXXXX.sharepoint.com/_api/search], reason [{"error_description":"The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs."}];
Since this is sharepoint online I don't have a server and I'm not using the .NET framework so what other way do I have to debug this? or other idea what I'm doing wrong here?
Any help would be greatly appreciated.

Maybe you can use the Java to call SharePoint Search Api and show the result(But we need to spend many time to research on this, there are many uncertainties.)
The best choice for you is to use the VisualStudio to test the SharePoint Addin. Microsoft provide more support on it and you can use the ready-made template.
You can use the wide range of search-related APIs that SharePoint offers for search add-ins:
.NET client object model (CSOM)
Key libs:
Microsoft.SharePoint.Client.Search.dll ;
Silverlight CSOM
Key libs:
Microsoft.SharePoint.Client.Search.Silverlight.dll ;
ECMAScript (JavaScript, JScript) object model (JSOM)
Key libs:
SP.search.js ;
Search REST API
http://server/_api/search/query
Some demo code:
Client-side Object Model (CSOM)
C#
using (ClientContext clientContext = new ClientContext("http://localhost"))
{
KeywordQuery keywordQuery = new KeywordQuery(clientContext);
keywordQuery.QueryText = "*";
SearchExecutor searchExecutor = new SearchExecutor(clientContext);
ClientResult<ResultTableCollection> results =
searchExecutor.ExecuteQuery(keywordQuery);
clientContext.ExecuteQuery();
}
JavaScript Object Model (JSOM)
var keywordQuery = new
Microsoft.SharePoint.Client.Search.Query.KeywordQuery(context);
keywordQuery.set_queryText('SharePoint');
var searchExecutor = new Microsoft.SharePoint.Client.Search.Query.SearchExecutor(context);
results = searchExecutor.executeQuery(keywordQuery);
context.executeQueryAsync(onQuerySuccess, onQueryFail);
REST
HTTP GET request
HTML
http://mylocalhost/_api/search/query?querytext='SharePoint'
HTTP POST request
HTML
{
'__metadata' : {'type' : 'Microsoft.Office.Server.Search.REST.SearchRequest'},
'Querytext' : 'SharePoint'
}
Then set the permissions by VisualStudio and "Napa" Office 365 Development Tools
More information on Search add in:
https://learn.microsoft.com/en-us/sharepoint/dev/general-development/search-add-ins-in-sharepoint

Related

Unable to rename subsite URL via CSOM in SharePoint Online

I have set up a SharePoint Online site and I have created a provider hosted app. One of the features of the app is to create subsites and there are times when a subsite needs to be renamed, including renaming the subsite URL. I can use the CSOM to create the subsite without any problems but when I try to rename the URL I get an access denied error. If I only change the title and description of the subsite there is no problem. If I log into SharePoint Online via the browser (using the same user account!) and I use the UI to rename the URL then it works without any problem. The page in SharePoint I use to rename the URL is https://tenant.sharepoint.com/testproject/_layouts/15/prjsetng.aspx
I have tried this on both a Microsoft 365 Developer subscription (where I am doing most of my development and testing) and the main SharePoint Online site where the solution will eventually be deployed to. I don't know much of the details for the main SPO site, other people set it up and I was provided an account to test renaming the subsite. To be clear, I am able to rename the subsite URL via the UI in both the developer and main SharePoint Online sites.
Is there something I'm doing wrong? Is there a limitation to renaming a subsite URL via code in SharePoint Online? Is there a bug in SharePoint Online that prevents renaming a subsite URL using code?
The exception thrown includes ServerErrorTypeName = "Microsoft.SharePoint.SPException". I can get the correlation id but from what I understand that's of no use in SharePoint Online. The exception Message is literally "Access denied." There is no inner exception.
Here is the code I'm using to rename the subsite:
SharePointContext spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
ClientContext clientContext = new ClientContext(spContext.SPHostUrl)
{
Credentials = new SharePointOnlineCredentials("SPUserName", "SPPassword".ToSecureString())
};
var webUrl = request.OldProjectUrl;
var subweb = clientContext.Site.OpenWeb(webUrl);
clientContext.Load(subweb);
clientContext.ExecuteQuery();
subweb.Title = request.ProjectName;
subweb.Description = request.ProjectName;
subweb.ServerRelativeUrl = "/HardcodedForTesting"; // <-- if I skip this line there is no error
subweb.Update();
clientContext.ExecuteQuery();
I was trying to achieve the same result but encountered the same error.
I was able to solve this by disabling the NoScriptSite setting of the site collection.
Using the PnP.PowerShell module:
Set-PnPSite -NoScriptSite:$false
Also the value you give to the ServerRelativeUrl property must be correctly constructed. I found two allowed format:
/sites/site-collection-path/new-subsite-path
new-subsite-path
Just did a test on my environment, I could rename the subsite URL via CSOM code normally. I use the same code as yours.
For your issue, you'd better create a service request with Microsoft.

What is the Sharepoint Document Location endpoint really returning?

I'm trying to get the OneNote notebook information that is linked to my organization's CRM accounts. Each account has a OneNote book created for it that can be accessed inside of CRM.
From what I understand, I can use the SharePointDocumentLocation endpoint (found here: https://learn.microsoft.com/en-us/dynamics365/customer-engagement/web-api/sharepointdocumentlocation?view=dynamics-ce-odata-9) to get the location of the specific file if I ask for location type to be 1.
However, SharePointDocumentLocationId and SiteCollectionId don't seem to be pointing to anything on my company's sites. Should I be getting my data somewhere else?
I started searching through my company's SharePoint structure to see if I can get any hints as to where these documents may be located. My initial Postman request (getting the sites off of the root site) don't show the site that hosts our CRM documents (sites/crmdocs). I was able to find where this was stored eventually, but trying to get the OneNote notebooks stored there returns an error since we have more than 20,000 notebooks there, so I can't fetch them all. As far as I know, I'm able to get notebooks if I have the specific ID I want.
Once I fetch the CRM information, I try to send a request like this:
https://graph.microsoft.com/v1.0/sites/{myCompanyUrl},{siteCollectionId},{sharepointDocumentLocationId}/onenote/notebooks/
SiteCollectionId and SharePointDocumentLocationId are from my CRM SharePointDocumentLocation request
The error I receive is:
The requested site was not found. Please check that the site is still accessible.
Assuming your environment is using the out of the box sharepoint site and sharepoint document location hierarchy, you can access One Note files using the following link structure:
[SharePointAbsoluteUrl]/[EntityLogicalName]/[RelativeUrl]_[RegardingObjectId]/[RelativeUrl]
How to get [SharePointAbsoluteUrl] :
Querying for sharepointdocumentlocations is actually not enough because Dynamics 365 stores this information in another entity called sharepointsite. This is how you can obtain it:
var query = new QueryExpression("sharepointsite")
{
ColumnSet = new ColumnSet("absoluteurl")
};
query.Criteria.AddCondition("IsDefault", ConditionOperator.Equal, true);
var entityCollection = _service.RetrieveMultiple(query);
var absoluteUrl = entityCollection[0].Attributes["absoluteurl"];
In Web API it is equivalent to:
GET https://[Your Org]/api/data/v9.0/sharepointsites?$select=absoluteurl&$filter=isdefault%20eq%20true
There can only be a default sharepoint site so this query will return a single record.
How to get the remaining parts:
Fetch for sharepointdocumentlocations that have Location Type dedicated to One Note Integration:
var query = new QueryExpression("sharepointdocumentlocation")
{
ColumnSet = new ColumnSet("regardingobjectid", "relativeurl")
};
query.Criteria.AddCondition("locationtype", ConditionOperator.Equal, 1);
var entityCollection = _service.RetrieveMultiple(query);
In Web API it is equivalent to the following get request, don't forget to add add Prefer: odata.include-annotations="*" to your HTTP Request Headers so that it gets the lookup lookuplogicalname field:
GET https://[Your Org]/api/data/v9.0/sharepointdocumentlocations?$select=relativeurl,_regardingobjectid_value&$filter=locationtype%20eq%201
This query can return many records, I've only used the first one in the examples below for explanation purposes.
[EntityLogicalName] will be your ((EntityReference)entityCollection[0].Attributes["regardingobjectid"]).LogicalName;
In Web Api will be your value._regardingobjectid_value#Microsoft.Dynamics.CRM.lookuplogicalname value.
[RelativeUrl] will be your entityCollection[0].Attributes["relativeurl"];
In Web Api will be your value.relativeurl value.
[RegardingObjectId] can be obtained with this expression ((EntityReference)entityCollection[0].Attributes["regardingobjectid"]).Id.ToString().Replace("-", "").ToUpper();
In Web Api id will be your _regardingobjectid_value value and you have to remove dashes and convert it to upper case in whatever language you are doing the request.
You should end up with an URL like this https://mycompany.sharepoint.com/account/A Datum Fabrication_A56B3F4B1BE7E6118101E0071B6AF231/A Datum Fabrication

NULL Reference when trying to get NameIdentifier Claim

I modified the TodoSPA sample application on github to be a 1.0.0-rc1-update1 application since that is the platform we are developing with. I had to make changes to Startup but left the client code as is. The sample uses the OAuth2 implicit flow with the ADAL client library. When I click on the TodoList link, I authenticate against the authentication server and the client calls the Get method in the TodoListController class. So far, so good. The Get method does the following.
string owner = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
IEnumerable<Todo> currentUserToDos = db.Todoes.Where(a => a.Owner == owner);
FindFirst throws a null reference exception. Why don't I get a NameIdentifer claim? If I get all of the claims using ClaimsPrincipal.Current.Claims, I don’t see as many as I would expect. (Results from Visual Studio 2015 Watch Window.)
Results View Expanding the Results View will enumerate the IEnumerable
[0] {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name: } System.Security.Claims.Claim
[1] {http://schemas.microsoft.com/ws/2008/06/identity/claims/role: } System.Security.Claims.Claim

How do I "Follow Site" in Sharepoint 2013 with SSOM?

I haven't found that much information online about how to utilize the Social Part of SharePoint 2013 by Server Object Model. To understand and follow my question better i recommend you going to this site.
Lets say I have a Feature Receiver that is fired when i certain site is created and I would like to take advantage of the Follow Content Feature but instead of every time a site is created i would like to make the person that created the site automatically follow that site.
Does anyone got experience with working with the Social functionallity in SharePoint 2013? If so would be awesome with a summary how to use the different Social methods.
Social Actor
From what I've understood from reading about this you need to create a "Actor" to represent the item or in my case the site. SocialActorInfo which takes to properties ContentUri and ActorType.
SocialActorInfo actorInfo = new SocialActorInfo();
actorInfo.ContentUri = contentUrl;
actorInfo.ActorType = contentType;
Find and Check if that Actor is followed by the current user
Then you have to check if that SocialActor is Followed by the current user.
ClientResult<bool> isFollowed = followingManager.IsFollowed(actorInfo);
Follow/Unfollow the Site/Item
ClientResult<SocialFollowResult> result = followingManager.Follow(actorInfo);
clientContext.ExecuteQuery();
"followingManager.UnFollow(actorInfo);"
Questions,
If I want to follow a site, what kind of ActorTypes are there?
How do i do this with server-side code?
Additional Information
Microsoft says: When users follow documents, sites, or tags, status updates from documents, conversations on sites, and notifications of tag use show up in their newsfeed. The features related to following content can be seen on the Newsfeed and the Following content pages.
SharePoint Server 2013 provides the following APIs that you can use to programmatically follow content:
Client object models for managed code
NET client object model
Silverlight client object model
Mobile client object model
JavaScript object model
Representational State Transfer (REST) service
Server object model
Link to Follow Content in SharePoint 2013, I can just find how to do it with REST or CSOM.
Just wanted to Share, this Solved the task.
Just a Follow Method that takes a SPWeb object and a SPUser object.
SPServiceContext serverContext = SPServiceContext.GetContext(web.Site);
UserProfileManager profileManager = new UserProfileManager(serverContext);
string userString = user.LoginName.ToString();
UserProfile userProfile = profileManager.GetUserProfile(userString);
if (userProfile != null)
{
SPSocialFollowingManager manager = new
SPSocialFollowingManager(userProfile);
SPSocialActorInfo actorInfo = new SPSocialActorInfo();
actorInfo.ContentUri = new Uri(web.Url);
actorInfo.AccountName = user.LoginName;
actorInfo.ActorType = SPSocialActorType.Site;
manager.Follow(actorInfo);
}

Programmatically connect to ASP.NET WebAPI sitting behind ForeFront UAG

I have a WebAPI built using ASP.NET MVC4. It is a simple API for getting data (simple HTTP GET requests). The API is stable and has been working with our mobile (MonoTouch) app for quite some time. Now we're putting ForeFront UAG in front of the API (simply changed web.config to use windows auth. Testing the security and API through a browser e.g. Chrome, and the UAG login is presented (when hitting API first time). Enter your credentials and then you get the data back for the API GET request. All what you'd expect. Now, from .NET code (no browser) I want to do the same thing. I've seen examples accessing SharePoint programmatically and some windows phone stuff, but none of them seem to work for ASP.NET MVC4 WebApi calls from just regular old .NET code (which I'll eventually use in MonoTouch).
Anyone have an example of how to Authenticate and then make HTTP GET request successfully through UAG to an ASP.NET MVC4 WebApi?
I don't have the disposal over a ForeFront UAG so I can't test this. But in general you have a few options. The samples are snippets and some code is left out for readability.
WebClient / HttpWebRquest
CredentialCache credentials = new CredentialCache();
credentials.Add(new Uri(url), "NTLM", new NetworkCredential(userName, password, domain));
//WebClient
var webClient = new WebClient();
webClient.Credentials = credentials;
//HttpWebRequest
var request = HttpWebRequest.Create(url);
request.Credentials = credentials
HttpClient
WebRequestHandler clientHandler = new WebRequestHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.AllowPipelining = true;
clientHandler.ImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
ProgressMessageHandler progress = new ProgressMessageHandler();
httpClient = HttpClientFactory.Create(clientHandler, progress);
You have also the options of using third party libraries to get this job done, like RestSharp or Service Stack.
Personally I make use of RestSharp because of the ease of use and serializing/deserializing capabilities.

Resources