/// <summary> /// Removes an app from the app catalog /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> RemoveAsync(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { if (id == Guid.Empty) { throw new ArgumentException(nameof(id)); } await new SynchronizationContextRemover(); return(await BaseRequest(id, AppManagerAction.Remove, true, null, scope)); }
#pragma warning restore CA2000 private static async Task <AppMetadata> GetAppMetaData(AppCatalogScope scope, ClientContext context, string accessToken, PnPHttpProvider httpClient, string requestDigest, string id) { AppMetadata returnValue = null; int retryCount = 0; int waitTime = 10; // seconds var metadataRequestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; using (var metadataRequest = new HttpRequestMessage(HttpMethod.Get, metadataRequestUrl)) { metadataRequest.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { metadataRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } if (!string.IsNullOrEmpty(requestDigest)) { metadataRequest.Headers.Add("X-RequestDigest", requestDigest); } while (returnValue == null && retryCount < 5) { // Perform actual post operation HttpResponseMessage metadataResponse = await httpClient.SendAsync(metadataRequest, new System.Threading.CancellationToken()); if (metadataResponse.IsSuccessStatusCode) { // If value empty, URL is taken var metadataResponseString = await metadataResponse.Content.ReadAsStringAsync(); if (metadataResponseString != null) { returnValue = JsonSerializer.Deserialize <AppMetadata>(metadataResponseString); } } else if (metadataResponse.StatusCode != HttpStatusCode.NotFound) { // Something went wrong... throw new Exception(await metadataResponse.Content.ReadAsStringAsync()); } if (returnValue == null) { // try again retryCount++; Thread.Sleep(waitTime * 1000); // wait 10 seconds } } return(returnValue); } }
public AppMetadata GetAppMetadata(ClientContext context, AppCatalogScope scope) { var appmanager = new AppManager(context); if (_id != Guid.Empty) { return(appmanager.GetAvailable(_id, scope)); } if (!string.IsNullOrEmpty(_title)) { return(appmanager.GetAvailable(_title, scope)); } return(_metadata); }
/// <summary> /// Retracts an app in the app catalog. Notice that this will not remove the app from the app catalog. /// </summary> /// <param name="appMetadata">The app metadata object of the app to retract.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> RetractAsync(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { if (appMetadata == null) { throw new ArgumentException(nameof(appMetadata)); } if (appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata.Id)); } await new SynchronizationContextRemover(); return(await BaseRequest(appMetadata.Id, AppManagerAction.Retract, true, null, scope)); }
/// <summary> /// Upgrades an app in a site /// </summary> /// <param name="appMetadata">The app metadata object of the app to upgrade.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> UpgradeAsync(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { if (appMetadata == null) { throw new ArgumentException(nameof(appMetadata)); } if (appMetadata.Id == Guid.Empty) { throw new ArgumentException(nameof(appMetadata.Id)); } await new SynchronizationContextRemover(); return(await UpgradeAsync(appMetadata.Id, scope)); }
private static async Task <AppMetadata> GetAppMetaData(AppCatalogScope scope, ClientContext context, string id) { AppMetadata returnValue = null; int retryCount = 0; int waitTime = 10; // seconds #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(context); #pragma warning restore CA2000 // Dispose objects before losing scope var metadataRequestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; using (var metadataRequest = new HttpRequestMessage(HttpMethod.Get, metadataRequestUrl)) { metadataRequest.Headers.Add("accept", "application/json;odata=nometadata"); await PnPHttpClient.AuthenticateRequestAsync(metadataRequest, context).ConfigureAwait(false); while (returnValue == null && retryCount < 5) { // Perform actual post operation HttpResponseMessage metadataResponse = await httpClient.SendAsync(metadataRequest, new System.Threading.CancellationToken()); if (metadataResponse.IsSuccessStatusCode) { // If value empty, URL is taken var metadataResponseString = await metadataResponse.Content.ReadAsStringAsync(); if (metadataResponseString != null) { returnValue = JsonSerializer.Deserialize <AppMetadata>(metadataResponseString); } } else if (metadataResponse.StatusCode != HttpStatusCode.NotFound) { // Something went wrong... throw new Exception(await metadataResponse.Content.ReadAsStringAsync()); } if (returnValue == null) { // try again retryCount++; await Task.Delay(waitTime * 1000); // wait 10 seconds } } return(returnValue); } }
/// <summary> /// Returns an available app /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public AppMetadata GetAvailable(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => BaseGetAvailableAsync(scope, id)).GetAwaiter().GetResult()); }
/// <summary> /// Returns all available apps. /// </summary> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public List <AppMetadata> GetAvailable(AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => BaseGetAvailableAsync(scope, Guid.Empty)).GetAwaiter().GetResult()); }
private async Task <bool> BaseRequest(Guid id, AppManagerAction action, bool switchToAppCatalogContext, Dictionary <string, object> postObject, AppCatalogScope scope) { var context = _context; if (switchToAppCatalogContext == true && scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); context = context.Clone(appcatalogUri); } var returnValue = false; var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(w => w.Url); // we're not in app-only or user + app context, so let's fall back to cookie based auth if (String.IsNullOrEmpty(accessToken)) { handler.SetAuthenticationCookies(context); } using (var httpClient = new PnPHttpProvider(handler)) { var method = action.ToString(); string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetByID('{id}')/{method}"; HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl); request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } request.Headers.Add("X-RequestDigest", await context.GetRequestDigest()); if (postObject != null) { var jsonBody = JsonConvert.SerializeObject(postObject); var requestBody = new StringContent(jsonBody); MediaTypeHeaderValue sharePointJsonMediaType; MediaTypeHeaderValue.TryParse("application/json;odata=nometadata;charset=utf-8", out sharePointJsonMediaType); requestBody.Headers.ContentType = sharePointJsonMediaType; request.Content = requestBody; } // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { var responseJson = JObject.Parse(responseString); returnValue = true; } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } return(await Task.Run(() => returnValue)); }
/// <summary> /// Returns an available app /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="title">The title of the app.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> private async Task <dynamic> BaseGetAvailableAsync(AppCatalogScope scope, Guid id = default(Guid), string title = "") { dynamic addins = null; var accessToken = _context.GetAccessToken(); using (var handler = new HttpClientHandler()) { _context.Web.EnsureProperty(w => w.Url); // we're not in app-only or user + app context, so let's fall back to cookie based auth if (String.IsNullOrEmpty(accessToken)) { handler.SetAuthenticationCookies(_context); } using (var httpClient = new PnPHttpProvider(handler)) { string requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps"; if (Guid.Empty != id) { requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; } HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl); request.Headers.Add("accept", "application/json;odata=verbose"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } else { if (_context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } } request.Headers.Add("X-RequestDigest", await _context.GetRequestDigest()); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { if (Guid.Empty == id && string.IsNullOrEmpty(title)) { var responseJson = JObject.Parse(responseString); var returnedAddins = responseJson["d"]["results"] as JArray; addins = JsonConvert.DeserializeObject <List <AppMetadata> >(returnedAddins.ToString()); } else if (!String.IsNullOrEmpty(title)) { var responseJson = JObject.Parse(responseString); var returnedAddins = responseJson["d"]["results"] as JArray; var listAddins = JsonConvert.DeserializeObject <List <AppMetadata> >(returnedAddins.ToString()); addins = listAddins.Where(a => a.Title == title).FirstOrDefault(); } else { var responseJson = JObject.Parse(responseString); var returnedAddins = responseJson["d"]; addins = JsonConvert.DeserializeObject <AppMetadata>(returnedAddins.ToString()); } } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } return(await Task.Run(() => addins)); }
/// <summary> /// Uploads a file to the Tenant App Catalog /// </summary> /// <param name="file">A byte array containing the file</param> /// <param name="filename">The filename (e.g. myapp.sppkg) of the file to upload</param> /// <param name="overwrite">If true will overwrite an existing entry</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <param name="timeoutSeconds">If specified will set the timeout on the request. Defaults to 200 seconds.</param> /// <returns></returns> public async Task <AppMetadata> AddAsync(byte[] file, string filename, bool overwrite = false, AppCatalogScope scope = AppCatalogScope.Tenant, int timeoutSeconds = 200) { if (file == null && file.Length == 0) { throw new ArgumentException(nameof(file)); } if (string.IsNullOrEmpty(filename)) { throw new ArgumentException(nameof(filename)); } await new SynchronizationContextRemover(); return(await BaseAddRequest(file, filename, overwrite, timeoutSeconds, scope)); }
/// <summary> /// Uploads an app file to the Tenant App Catalog /// </summary> /// <param name="path"></param> /// <param name="overwrite">If true will overwrite an existing entry</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <param name="timeoutSeconds">If specified will set the timeout on the request. Defaults to 200 seconds.</param> /// <returns></returns> public AppMetadata Add(string path, bool overwrite = false, AppCatalogScope scope = AppCatalogScope.Tenant, int timeoutSeconds = 200) { return(Task.Run(() => AddAsync(path, overwrite, scope, timeoutSeconds)).GetAwaiter().GetResult()); }
/// <summary> /// Returns an avialable app /// </summary> /// <param name="title">The title of the app.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <AppMetadata> GetAvailableAsync(string title, AppCatalogScope scope = AppCatalogScope.Tenant) { await new SynchronizationContextRemover(); return(await BaseGetAvailableAsync(scope, Guid.Empty, title)); }
/// <summary> /// Deploys/trusts an app in the app catalog /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="skipFeatureDeployment">If set to true will skip the feature deployed for tenant scoped apps.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Deploy(Guid id, bool skipFeatureDeployment = true, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => DeployAsync(id, skipFeatureDeployment, scope)).GetAwaiter().GetResult()); }
/// <summary> /// Uninstalls an app from a site. /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Uninstall(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => UninstallAsync(id, scope)).GetAwaiter().GetResult()); }
/// <summary> /// Uploads an app file to the Tenant App Catalog /// </summary> /// <param name="path"></param> /// <param name="overwrite">If true will overwrite an existing entry</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <param name="timeoutSeconds">If specified will set the timeout on the request. Defaults to 200 seconds.</param> /// <returns></returns> public async Task <AppMetadata> AddAsync(string path, bool overwrite = false, AppCatalogScope scope = AppCatalogScope.Tenant, int timeoutSeconds = 200) { if (string.IsNullOrEmpty(path)) { throw new ArgumentException(nameof(path)); } if (!System.IO.File.Exists(path)) { throw new IOException("File does not exist"); } var bytes = System.IO.File.ReadAllBytes(path); var fileInfo = new FileInfo(path); await new SynchronizationContextRemover(); return(await BaseAddRequest(bytes, fileInfo.Name, overwrite, timeoutSeconds, scope)); }
#pragma warning disable CA2000 private async Task <AppMetadata> BaseAddRequest(byte[] file, string filename, bool overwrite, int timeoutSeconds, AppCatalogScope scope) { AppMetadata returnValue = null; var isCloned = false; var context = _context; if (scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); context = context.Clone(appcatalogUri); isCloned = true; } var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(w => w.Url); if (string.IsNullOrEmpty(accessToken)) { context.SetAuthenticationCookiesForHandler(handler); } using (var httpClient = new PnPHttpProvider(handler)) { string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/Add(overwrite={(overwrite.ToString().ToLower())}, url='{filename}')"; var requestDigest = string.Empty; if (!string.IsNullOrEmpty(accessToken)) { requestDigest = await context.GetRequestDigestAsync().ConfigureAwait(false); } else { requestDigest = await context.GetRequestDigestAsync(handler.CookieContainer).ConfigureAwait(false); } using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } else { if (context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } } if (!string.IsNullOrEmpty(requestDigest)) { request.Headers.Add("X-RequestDigest", requestDigest); } request.Headers.Add("binaryStringRequestBody", "true"); request.Content = new ByteArrayContent(file); httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { using (var jsonDocument = JsonDocument.Parse(responseString)) { if (jsonDocument.RootElement.TryGetProperty("UniqueId", out JsonElement uniqueIdElement)) { var id = uniqueIdElement.GetString(); returnValue = await GetAppMetaData(scope, context, accessToken, httpClient, requestDigest, id); } } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }
#pragma warning disable CA2000 private async Task <bool> BaseRequest(Guid id, AppManagerAction action, bool switchToAppCatalogContext, Dictionary <string, object> postObject, AppCatalogScope scope, int timeoutSeconds = 200) { var isCloned = false; var context = _context; if (switchToAppCatalogContext == true && scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); context = context.Clone(appcatalogUri); isCloned = true; } var returnValue = false; var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(w => w.Url); if (string.IsNullOrEmpty(accessToken)) { context.SetAuthenticationCookiesForHandler(handler); } using (var httpClient = new PnPHttpProvider(handler)) { httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); var method = action.ToString(); var requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetByID('{id}')/{method}"; using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("X-RequestDigest", await context.GetRequestDigestAsync().ConfigureAwait(false)); } else { if (context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } request.Headers.Add("X-RequestDigest", await context.GetRequestDigestAsync(handler.CookieContainer).ConfigureAwait(false)); } if (postObject != null) { var jsonBody = JsonSerializer.Serialize(postObject); var requestBody = new StringContent(jsonBody); if (MediaTypeHeaderValue.TryParse("application/json;odata=nometadata;charset=utf-8", out MediaTypeHeaderValue sharePointJsonMediaType)) { requestBody.Headers.ContentType = sharePointJsonMediaType; } request.Content = requestBody; } // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { returnValue = true; } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }
/// <summary> /// Returns an available app /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="title">The title of the app.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> private async Task <dynamic> BaseGetAvailableAsync(AppCatalogScope scope, Guid id = default(Guid), string title = "") { dynamic addins = null; var accessToken = _context.GetAccessToken(); using (var handler = new HttpClientHandler()) { if (string.IsNullOrEmpty(accessToken)) { _context.SetAuthenticationCookiesForHandler(handler); } _context.Web.EnsureProperty(w => w.Url); using (var httpClient = new PnPHttpProvider(handler)) { string requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps"; if (Guid.Empty != id) { requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; } using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); request.Headers.Add("X-RequestDigest", await _context.GetRequestDigestAsync().ConfigureAwait(false)); } else { if (_context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } request.Headers.Add("X-RequestDigest", await _context.GetRequestDigestAsync(handler.CookieContainer).ConfigureAwait(false)); } // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { if (Guid.Empty == id && string.IsNullOrEmpty(title)) { var resultCollection = JsonSerializer.Deserialize <ResultCollection <AppMetadata> >(responseString, new JsonSerializerOptions() { IgnoreNullValues = true }); if (resultCollection.Items != null && resultCollection.Items.Any()) { addins = resultCollection.Items; } } else if (!String.IsNullOrEmpty(title)) { var resultCollection = JsonSerializer.Deserialize <ResultCollection <AppMetadata> >(responseString, new JsonSerializerOptions() { IgnoreNullValues = true }); if (resultCollection.Items != null && resultCollection.Items.Any()) { addins = resultCollection.Items.FirstOrDefault(a => a.Title.Equals(title)); } } else { addins = JsonSerializer.Deserialize <AppMetadata>(responseString); } } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } } return(await Task.Run(() => addins)); }
/// <summary> /// Returns an available app /// </summary> /// <param name="id">The id of the app</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <AppMetadata> GetAvailableAsync(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { await new SynchronizationContextRemover(); return(await BaseGetAvailableAsync(scope, id)); }
/// <summary> /// Returns an available app /// </summary> /// <param name="title">The title of the app.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public AppMetadata GetAvailable(string title, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => BaseGetAvailableAsync(scope, Guid.Empty, title)).GetAwaiter().GetResult()); }
/// <summary> /// Deploys/trusts an app in the app catalog /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="skipFeatureDeployment">If set to true will skip the feature deployed for tenant scoped apps.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public async Task <bool> DeployAsync(Guid id, bool skipFeatureDeployment = true, AppCatalogScope scope = AppCatalogScope.Tenant) { if (id == Guid.Empty) { throw new ArgumentException(nameof(id)); } var postObj = new Dictionary <string, object> { { "skipFeatureDeployment", skipFeatureDeployment } }; await new SynchronizationContextRemover(); return(await BaseRequest(id, AppManagerAction.Deploy, true, postObj, scope)); }
/// <summary> /// Returns an available app /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="title">The title of the app.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> private async Task <dynamic> BaseGetAvailableAsync(AppCatalogScope scope, Guid id = default(Guid), string title = "") { dynamic addins = null; _context.Web.EnsureProperty(w => w.Url); #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(_context); #pragma warning restore CA2000 // Dispose objects before losing scope string requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps"; if (Guid.Empty != id) { requestUrl = $"{_context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; } using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); PnPHttpClient.AuthenticateRequestAsync(request, _context).GetAwaiter().GetResult(); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { if (Guid.Empty == id && string.IsNullOrEmpty(title)) { var resultCollection = JsonSerializer.Deserialize <ResultCollection <AppMetadata> >(responseString, new JsonSerializerOptions() { IgnoreNullValues = true }); if (resultCollection.Items != null && resultCollection.Items.Any()) { addins = resultCollection.Items; } } else if (!String.IsNullOrEmpty(title)) { var resultCollection = JsonSerializer.Deserialize <ResultCollection <AppMetadata> >(responseString, new JsonSerializerOptions() { IgnoreNullValues = true }); if (resultCollection.Items != null && resultCollection.Items.Any()) { addins = resultCollection.Items.FirstOrDefault(a => a.Title.Equals(title)); } } else { addins = JsonSerializer.Deserialize <AppMetadata>(responseString); } } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } return(await Task.Run(() => addins)); }
/// <summary> /// Uploads a file to the Tenant App Catalog /// </summary> /// <param name="file">A byte array containing the file</param> /// <param name="filename">The filename (e.g. myapp.sppkg) of the file to upload</param> /// <param name="overwrite">If true will overwrite an existing entry</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public AppMetadata Add(byte[] file, string filename, bool overwrite = false, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => AddAsync(file, filename, overwrite, scope)).GetAwaiter().GetResult()); }
private async Task <bool> BaseRequest(Guid id, AppManagerAction action, bool switchToAppCatalogContext, Dictionary <string, object> postObject, AppCatalogScope scope, int timeoutSeconds = 200) { var isCloned = false; var context = _context; if (switchToAppCatalogContext == true && scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); #pragma warning disable CA2000 // Dispose objects before losing scope context = context.Clone(appcatalogUri); #pragma warning restore CA2000 // Dispose objects before losing scope isCloned = true; } var returnValue = false; context.Web.EnsureProperty(w => w.Url); #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(context); #pragma warning restore CA2000 // Dispose objects before losing scope httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); var method = action.ToString(); var requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetByID('{id}')/{method}"; using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); await PnPHttpClient.AuthenticateRequestAsync(request, context).ConfigureAwait(false); if (postObject != null) { var jsonBody = JsonSerializer.Serialize(postObject); var requestBody = new StringContent(jsonBody); if (MediaTypeHeaderValue.TryParse("application/json;odata=nometadata;charset=utf-8", out MediaTypeHeaderValue sharePointJsonMediaType)) { requestBody.Headers.ContentType = sharePointJsonMediaType; } request.Content = requestBody; } // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { try { returnValue = true; } catch { } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }
/// <summary> /// Removes an app from the app catalog /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Remove(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => RemoveAsync(id, scope)).GetAwaiter().GetResult()); }
private async Task <AppMetadata> BaseAddRequest(byte[] file, string filename, bool overwrite, int timeoutSeconds, AppCatalogScope scope) { AppMetadata returnValue = null; var isCloned = false; var context = _context; if (scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); #pragma warning disable CA2000 // Dispose objects before losing scope context = context.Clone(appcatalogUri); #pragma warning restore CA2000 // Dispose objects before losing scope isCloned = true; } context.Web.EnsureProperty(w => w.Url); #pragma warning disable CA2000 // Dispose objects before losing scope var httpClient = PnPHttpClient.Instance.GetHttpClient(context); #pragma warning restore CA2000 // Dispose objects before losing scope string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/Add(overwrite={(overwrite.ToString().ToLower())}, url='{filename}')"; var requestDigest = string.Empty; using (var request = new HttpRequestMessage(HttpMethod.Post, requestUrl)) { request.Headers.Add("accept", "application/json;odata=nometadata"); await PnPHttpClient.AuthenticateRequestAsync(request, context).ConfigureAwait(false); request.Headers.Add("binaryStringRequestBody", "true"); request.Content = new ByteArrayContent(file); httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { using (var jsonDocument = JsonDocument.Parse(responseString)) { if (jsonDocument.RootElement.TryGetProperty("UniqueId", out JsonElement uniqueIdElement)) { var id = uniqueIdElement.GetString(); returnValue = await GetAppMetaData(scope, context, id); } } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } if (isCloned) { context.Dispose(); } return(await Task.Run(() => returnValue)); }
private async Task <AppMetadata> BaseAddRequest(byte[] file, string filename, bool overwrite, int timeoutSeconds, AppCatalogScope scope) { AppMetadata returnValue = null; var context = _context; if (scope == AppCatalogScope.Tenant) { // switch context to appcatalog var appcatalogUri = _context.Web.GetAppCatalog(); context = context.Clone(appcatalogUri); } var accessToken = context.GetAccessToken(); using (var handler = new HttpClientHandler()) { context.Web.EnsureProperty(w => w.Url); // we're not in app-only or user + app context, so let's fall back to cookie based auth if (String.IsNullOrEmpty(accessToken)) { handler.SetAuthenticationCookies(context); } using (var httpClient = new PnPHttpProvider(handler)) { string requestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/Add(overwrite={(overwrite.ToString().ToLower())}, url='{filename}')"; var requestDigest = await context.GetRequestDigest(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl); request.Headers.Add("accept", "application/json;odata=verbose"); if (!string.IsNullOrEmpty(accessToken)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } else { if (context.Credentials is NetworkCredential networkCredential) { handler.Credentials = networkCredential; } } request.Headers.Add("X-RequestDigest", requestDigest); request.Headers.Add("binaryStringRequestBody", "true"); request.Content = new ByteArrayContent(file); httpClient.Timeout = new TimeSpan(0, 0, timeoutSeconds); // Perform actual post operation HttpResponseMessage response = await httpClient.SendAsync(request, new System.Threading.CancellationToken()); if (response.IsSuccessStatusCode) { // If value empty, URL is taken var responseString = await response.Content.ReadAsStringAsync(); if (responseString != null) { var responseJson = JObject.Parse(responseString); var id = responseJson["d"]["UniqueId"].ToString(); var metadataRequestUrl = $"{context.Web.Url}/_api/web/{(scope == AppCatalogScope.Tenant ? "tenant" : "sitecollection")}appcatalog/AvailableApps/GetById('{id}')"; HttpRequestMessage metadataRequest = new HttpRequestMessage(HttpMethod.Post, metadataRequestUrl); metadataRequest.Headers.Add("accept", "application/json;odata=verbose"); if (!string.IsNullOrEmpty(accessToken)) { metadataRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken); } metadataRequest.Headers.Add("X-RequestDigest", requestDigest); // Perform actual post operation HttpResponseMessage metadataResponse = await httpClient.SendAsync(metadataRequest, new System.Threading.CancellationToken()); if (metadataResponse.IsSuccessStatusCode) { // If value empty, URL is taken var metadataResponseString = await metadataResponse.Content.ReadAsStringAsync(); if (metadataResponseString != null) { var metadataResponseJson = JObject.Parse(metadataResponseString); var returnedAddins = metadataResponseJson["d"]; returnValue = JsonConvert.DeserializeObject <AppMetadata>(returnedAddins.ToString()); } } else { // Something went wrong... throw new Exception(await metadataResponse.Content.ReadAsStringAsync()); } } } else { // Something went wrong... throw new Exception(await response.Content.ReadAsStringAsync()); } } } return(await Task.Run(() => returnValue)); }
/// <summary> /// Removes an app from the app catalog /// </summary> /// <param name="appMetadata">The app metadata object of the app to remove.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Remove(AppMetadata appMetadata, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => RemoveAsync(appMetadata, scope)).GetAwaiter().GetResult()); }
/// <summary> /// Upgrades an app in a site /// </summary> /// <param name="id">The unique id of the app. Notice that this is not the product id as listed in the app catalog.</param> /// <param name="scope">Specifies the app catalog to work with. Defaults to Tenant</param> /// <returns></returns> public bool Upgrade(Guid id, AppCatalogScope scope = AppCatalogScope.Tenant) { return(Task.Run(() => UpgradeAsync(id)).GetAwaiter().GetResult()); }