0

I'm building an Add-In that is being used to display to a user their followed documents in a responsive format. For the most part, the add-in works fine. However, for certain items, in particular "Link to Document" content types, I would like to be able to query the item directly to get further information from its other fields.

Getting the documents followed is easy enough using the SP.Social.SocialFollowingManager. However, I am tripping up on getting the item back from the document.

It is my suspicion that because the followed documents aren't necessarily in the same site collection as the app, they'll still show as followed document from the social following manager, but would be otherwise non-accessible to the add-in.

Below is mockup of the add-in (in Typescript and using Knockout):

//App.ts

const SPAppWebUrl_key = "SPAppWebUrl";
const SPHostUrl_key = "SPHostUrl";
class App {
    docs: KnockoutObservableArray<Document> = ko.observableArray([]);

    // ...

    SPHostUrl: string = "";
    AppHostUrl: string = "";
    clientContext: SP.ClientContext = null;
    HostSite: SP.AppContextSite = null;

    constructor() {
        let self = this;

        JSRequest.EnsureSetup();

        try {
            self.SPHostUrl = decodeURIComponent(JSRequest.QueryString[SPHostUrl_key]);
            self.AppHostUrl = decodeURIComponent(JSRequest.QueryString[SPAppWebUrl_key]);

            self.clientContext = SP.ClientContext.get_current();
            self.HostSite = new SP.AppContextSite(self.clientContext, self.SPHostUrl);
        }
        catch (error) {
            alert(error);
        }

        let FollowingManager: SP.Social.SocialFollowingManager = new SP.Social.SocialFollowingManager(self.clientContext);
        let actors: SP.Social.SocialActor[] = FollowingManager.getFollowed(SP.Social.SocialActorTypes.documents);
         self.clientContext.executeQueryAsync(
            (sender: any, args: SP.ClientRequestSucceededEventArgs) => {
                self.docs.push(...actors.map(actor => new Document(
                    {
                        ActorType: actor.get_actorType(),
                        Name: actor.get_name(),
                        Uri: actor.get_contentUri()
                    }, self.clientContext, self.HostSite)
                ));
            },
            (sender: any, args: SP.ClientRequestFailedEventArgs) => {
                alert('error: ' + args.get_message());
            }
        );
    };
};

For each document, the context and AppContextSite are passed in.

//Document.ts

const domainRegex: RegExp = /((?:https?:\/\/)?(?:[^\/]+))(\/.*)/i;

export default class Document {
    ActorType: string = "";
    Name: string = "";
    Uri: string = "";
    LinkToDocument: string = "none";

    constructor(followed: FollowedResult_JSON, SP_Context: SP.ClientContext, HostSite: SP.AppContextSite) {
        let self = this;

        // Invert non-TypeScript-compiled enum
        self.ActorType = getKeyByValue(<any>SP.Social.SocialActorType, followed.ActorType);
        self.Name = followed.Name;
        self.Uri = followed.Uri;

        let extension = self.Name.substr(self.Name.lastIndexOf(".") + 1);
        if(extension === "aspx") {
            let docUriRelative: string = self.Uri.match(domainRegex)[2];
            let spfile: SP.File = HostSite.get_web().getFileByServerRelativeUrl(docUriRelative);
            SP_Context.load(spfile);
            SP_Context.executeQueryAsync(
                () => {
                    self.LinkToDocument = spfile.get_listItemAllFields()["URL"];
                },
                (sender: any, args: SP.ClientRequestFailedEventArgs) => {
                    alert('error in Document: ' + args.get_message());
                }
            );
        }
    };
}

interface FollowedResult_JSON {
    ActorType: SP.Social.SocialActorType,
    Name: string,
    Uri: string
}

I have managed to get something like this to work before in other add-ins, albeit they were strictly accessing an item that was in the site collection the app was installed in. In this case, this is not guaranteed, and in my test cases, these "Link To Document" documents are hitting the 'error in Document' with 'File not found'.

Is my suspicion correct in this case and am I out of luck with getting this information purely within a SharePoint Add-In, or is there something else I can do to get this information?

1 Answer 1

1

From my experience it's possible to read data across site collections from SharePoint hosted addin, but you need to adjust a few things in your deployment model.

  1. First of all you need to deploy the app into the add-catalog site and use "Deployments" option to deploy to any other particular site collection. More info here - App Deployment through "App Stapling". Please note that in that way your app will use one single app web - app web located at App Catalog site collection.
  2. You need to give your app Tenant Read permission (or other permissions depending on your requirements). By doing that your app becomes "admin-approved" and only admins can install it in the app catalog.
  3. Finally you can write something similar in your app code (JSOM also works):
function CrossDomainAccess(){
var executor = new SP.RequestExecutor(SPAppWebUrl);
var newHostWeb = "http://server/sites/your_other_sitecollection/";
var url = SPAppWebUrl + "/_api/SP.AppContextSite(@target)/web?" + "@target='" + newHostWeb + "'";

executor.executeAsync({
    url: url,
    method: "GET",
    crossDomain: true,
    headers: { "Accept": "application/json; odata=verbose" },
    success: function (data)
    {
        alert(data.body);
        // var jsonObject = JSON.parse(data.body);
    },
    error: function (data, errorCode, errorMessage)
    {
        alert(errorMessage);
    }
});}  
1
  • Thank's Sergei. I'm finally back on this project after a hiatus, and this put me on the right track. The bit I was missing was SP.RequestExecutor (and that side-loading apps with tenancy permissions really doesn't work..!)
    – BM-
    Aug 10, 2017 at 0:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.