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?