1

So I'm trying to access files from a Sharepoint document library in C#. My app is a provider-hosted Sharepoint app. I seem to be able to access the library, but not the library's items.

Here is how I get my context in the controller:

var spContext = SharePointContextProvider.Current.GetSharePointContext(System.Web.HttpContext.Current);
using (var clientContext = spContext?.CreateUserClientContextForSPHost())
{
    if (clientContext != null)
    {
        template.SetMergefields(clientContext);
    }
}

And how I try to access the files:

Web web = clientContext.Web;

List templateList = web.Lists.GetByTitle(libraryName);
clientContext.Load(templateList);
clientContext.ExecuteQuery();

var templateFiles = templateList.RootFolder.Files;
clientContext.Load(templateFiles);
clientContext.ExecuteQuery();

var templateListItems = templateList.GetItems(CamlQuery.CreateAllItemsQuery());
clientContext.Load(templateListItems);
clientContext.ExecuteQuery();

At this point, the templateList has the property ItemCount = 8 which does match the number of the files in the library. Yet, both templateFiles and templateListItems, have Count = 0 so I don't seem to be able to access these 8 items.

I have also tried to access a single item by it's id which I looked up on Sharepoint:

var itemWithId1 = templateList.GetItemById(1);
clientContext.Load(itemWithId1);
clientContext.ExecuteQuery();

Yet this leads to the error:

Microsoft.SharePoint.Client.ServerException: 'Item does not exist. It may have been deleted by another user.'

Another approach I tried was using the GetFileByServerRelativeUrl to get a specific file:

File file = clientContext.Web.GetFileByServerRelativeUrl(serverRelativeUrl);
clientContext.Load(file);
clientContext.ExecuteQuery();

This gives me the following error:

Microsoft.SharePoint.Client.ServerUnauthorizedAccessException: 'Access denied. You do not have permission to perform this action or access this resource.'

And yes I've checked - I do have full permissions for this library and it's on default settings so item-level permissions do not vary from library's permissions.

Does anyone have an idea what I'm doing wrong or how to do it properly? The actual goal is to access a specific file from the library by the file's name, a file list would also work.

themaksmw
  • 25
  • 6
  • Can you post the part of your `AppManifest.xml` that shows the `AppPermissionRequests` section? From what you've said the app permissions are wrong and it doesn't have access to the list items even if you do. – Equalsk Jan 30 '18 at 16:09
  • @Equalsk My `AppManifest.xml` does not contain any `AppPermissionRequests`section. I only have `Properties` with `Title` and `StartPage`, and `AppPrincipal` with `RemoteWebApplication`. – themaksmw Jan 31 '18 at 08:00
  • Is the app meant to run in the context of the current user or is it meant to run with app only permissions? – Equalsk Jan 31 '18 at 08:47
  • @Equalsk Now that you mention it.. It would probably be even better to run with app only permissions. I changed it now to `CreateAppOnlyClientContextForSPHost()` and set `` but the code behaves exactly the same :( – themaksmw Jan 31 '18 at 09:53
  • What scope did you request? There should be a line that looks like `` (yours may differ depending on what's needed) – Equalsk Jan 31 '18 at 10:48
  • @Equalsk For the testing now I gave it full control `` – themaksmw Jan 31 '18 at 11:42
  • If you browse to `http://YourSPSite/_layouts/15/appinv.aspx` and lookup your app by ID, what can you see? – Equalsk Jan 31 '18 at 14:05
  • @Equalsk I can't find any app by its id. Is the id of format `XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX`? I should maybe meantion that it is a dev-Sharepoint and the app is therefore registered as an "App in Testing". – themaksmw Jan 31 '18 at 14:21

5 Answers5

0

Did you try to start Visual Studio as Admin? If that solves your problem, you might be needing a manifest file and change inside that admin required. It normally happens when you're trying to access files that are not part of the Windows User, which means the root folder(C:).

  • Unfortunately I do not have admin rights on my PC and therefore can't run VS as admin. But the permission part should be covered by the context. I mean the whole SP is not part of the Windows User, yet I can read the amount of items in the document library. – themaksmw Jan 30 '18 at 12:18
0

I worked on this type of things before, but I didn't use C#. If you want to try another way, there is a PowerShell Method called PnP PowerShell that accesses easily the SharePoint app and return things (It is officially recognised by Microsoft). You could try with :

Get-PnPListItem -List <ListPipeBind>
            [-Fields <String[]>]
            [-PageSize <Int>]
            [-ScriptBlock <ScriptBlock>]
            [-Web <WebPipeBind>]
            [-Connection <SPOnlineConnection>]

which returns all items from a list (remember that a document library is just a special list, like a task list or other), there are versions of the cmdlet that only return 1 item.

For example:

PS:> Get-PnPListItem -List Tasks -PageSize 1000

returns the 1000 first elements of the Tasks list.

Here is the main github: https://github.com/SharePoint/PnP-PowerShell/tree/master/Documentation

here is the cmdlet documentaion: https://github.com/SharePoint/PnP-PowerShell/blob/master/Documentation/Get-PnPListItem.md

raiskader
  • 89
  • 4
  • 16
  • Thanks for showing this alternative way, but I'd prefer to stick with C#. – themaksmw Jan 30 '18 at 12:16
  • As you wish but I never used C# to access a SharePoint Site as PnP PowerShell is far easier and better. – raiskader Jan 30 '18 at 12:42
  • So as nothing else seems to be working, I'm trying your approach with `powerShell.AddCommand("Get-PnPFile -Url " + serverRelativeUrl + " -AsFile");`. I have managed to include PS code in C# and probably have the code to catch the result afterwards. My issue is - how do I include the whole PnP library? The command is not being recognized. I probably need some `Import-Module`, but where do I import from? The documentation only offers .msi, but wouldn't this install the module only on my PC instead of the Visual Studio solution? – themaksmw Feb 01 '18 at 08:51
  • To use PnP PowerShell, you need to download and install the SharePoint Online Management shell. Then install the PnP package via the command given at the main page of the PnP github. But I think they are only usable in the management shell you need to download. I know making third party programs for a SharePoint website are a pain, contact me "Erwan Tassin" on linkedin if you can't manage to do it. – raiskader Feb 01 '18 at 20:48
0

Another answer I can try to give, do you run your code directly from the server location? I sometimes needed to do that to make some of my programs work.

raiskader
  • 89
  • 4
  • 16
  • I'm not sure how to answer this one. Well the app currently runs on my localhost but it's connected to a Sharepoint site which is obviously online. – themaksmw Jan 30 '18 at 12:31
  • Do you have the full admin rights of the SharePoint site ? – raiskader Jan 30 '18 at 12:44
  • I am in the Owners group and have the "Full Control" permission level. – themaksmw Jan 30 '18 at 13:00
  • I can't figure out the problem then. Try using the powershell answer, MS SP is full of bugs and things even MS does not understand and support is very thin. And with the PnP PowerShell, it is 5 lines of code, you can pipe the results in a .csv file and boom, a good job quickly done. – raiskader Jan 30 '18 at 14:03
  • The issue is, that I need to access a specific file _within_ my C# code. I therefore actually need the file's stream. I once use it to convert a docx into a pdf (using word automation services) and a few times to open the docx as a `WordprocessingDocument` and read or modify the content. – themaksmw Jan 30 '18 at 14:25
  • there is a cmdlet for that : `Get-PnPFile -Url [-Web ] [-Connection ]` it downloads the file – raiskader Jan 30 '18 at 14:49
0

To access the file of a list item, you need to access the list item itself, and load its File property within the context:

var docs = clientContext.Web.Lists.GetByTitle(Library);
var listItem = docs.GetItemById(listItemId);
clientContext.Load(docs);
clientContext.Load(listItem, i => i.File);
clientContext.ExecuteQuery();

var fileRef = listItem.File.ServerRelativeUrl;

Are you absolutely sure, that you have a file with ID = 1? Use your code:

var templateListItems = templateList.GetItems(CamlQuery.CreateAllItemsQuery());

clientContext.Load(templateListItems);

clientContext.ExecuteQuery();

get all the items, and check their ids.

EDIT

Based on your comment - Did you try with a different Query (writing your own) that will retrieve some items based on a rule. Because the property templateList.ItemCount giving you some value, still doesn't mean that you have loaded the items properly. It means that you have loaded the list.

There is something with the Query. From what I see in all the examples they are creating a query like this:

var query = CamlQuery.CreateAllItemsQuery();
var templateListItems = templateList.GetItems(query);

I know that it doesn't seem like a deal breaker, but you know - SharePoint... It's worth the try.

Community
  • 1
  • 1
m3n7alsnak3
  • 3,026
  • 1
  • 15
  • 24
  • My issue is not accessing the `File` property of an item but accessing the items of a list at all. As already stated in the question my code for `templateListItems` gives me `templateListItems.Count = 0`, I therefore cannot use it to _"get all the items and check their ids"_. The _"get all the items"_ IS the actual issue. And yes, I am sure about the ids. I made Sharepoint show me the ids in the library and because the library has not been touched yet, the ids of the 8 files actualy are from 1 to 8. – themaksmw Jan 31 '18 at 07:51
  • I tried it that way too, with the query defined seperatly, but this works same as the one before - `templateListItems.Count = 0`. – themaksmw Feb 01 '18 at 07:21
0

I finally got it to work! Turns out, that the permissions on SharePoint where wrong.

On SharePoint, in the "Apps in Testing" Tab, you can click the "..." next to your app, go to "MANAGE PERMISSIONS" and you will be forwarded to a page that asks you "Do you trust XXX?". I was so focused on the question and the "Trust It" and "Cancel" Buttons that I didn't see the dropdown on the left saying "Let it have full control of the list:" with "App Packages" pre-selected. When I changed it to the list I was trying to access, one of the approaches in my code suddenly worked:

List templateList = web.Lists.GetByTitle(libraryName);
clientContext.Load(templateList);
clientContext.ExecuteQuery();

ListItemCollection templateListItems = templateList.GetItems(CamlQuery.CreateAllItemsQuery());
clientContext.Load(templateListItems);
clientContext.ExecuteQuery();

Now templateListItems actually contains objects from my library!

One can of course "optimize" the code and delete the first ExecuteQuery(), or define the CamlQuery.CreateAllItemsQuery() as a separate variable and pass it to GetItems(), or use vars instead of explicit types - all of those work, it's up to your preferences.

Appreciate all the help, I hope I've got it from here on :)

themaksmw
  • 25
  • 6