List all external users SharePoint Online - sharepoint

Is there a way other than powershell (CSOM or JSOM) to list all external users?
I want to list all external users and "their permissions".

I ended upp doing a console application that exports all external users and their permissions on all sites, lists, files and folders in a sitecollection.
It was done quickly, so code modification could most certainly be done to fit your requirements better.
This solution exports a .csv file with the content.
Just copy paste from the "Program.class"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security;
using Microsoft.SharePoint.Client;
using System.Net;
namespace ListExternalUsersCSOM
{
class Program
{
// Output to filesystem
private static string filePath = #"C:\Users\User\Desktop\output.csv";
// Builds the content to export to the csv file
private static StringBuilder csv = new StringBuilder();
// Groupcollection of all sitegrups and their members
// Only want one trip to the server for this
private static GroupCollection groups;
static void Main(string[] args)
{
// Sitecollection url, username and password of admin account
var webUri = new Uri("https://tenant.sharepoint.com/sites/intranet");
const string userName = "admin#tenant.com";
const string password = "Password";
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
// Create credentials and context
var credentials = new SharePointOnlineCredentials(userName, securePassword);
var ctx = new Microsoft.SharePoint.Client.ClientContext(webUri);
ctx.Credentials = credentials;
// Get rootweb and the groups for the sitecollection
var rootWeb = ctx.Web;
groups = rootWeb.SiteGroups;
// Load groupcollection and load certain properties in every group right away
ctx.Load(groups, groups => groups.Include(g => g.Title, g => g.Users, g => g.Id));
// Load rootweb, subsites, lists, relative url, title, uniqueroleassingments
// Inlcude uniqueroleassingments and title in lists, spares us a trip to the server
ctx.Load(rootWeb, w => w.Webs, w => w.ServerRelativeUrl, w => w.Title, w => w.Lists.Include(l => l.HasUniqueRoleAssignments, l => l.Title), w => w.HasUniqueRoleAssignments);
ctx.ExecuteQuery();
// First we do these checks for rootweb only, then we recursively check all subsites...
// If uniquepermissions on web, look for external users
if (rootWeb.HasUniqueRoleAssignments)
{
getExternalUsersAndPermissionsWeb(rootWeb);
}
foreach (var list in rootWeb.Lists)
{
if (list.Title != "MicroFeed" | list.Title != "Delningslänkar")
{
ListHasUniqueRoleAssignment(rootWeb, list, ctx);
}
}
foreach (var subWeb in rootWeb.Webs)
{
WebHasUniqueRoleAssignmentRecursive(subWeb, ctx);
}
System.IO.File.WriteAllText(filePath, csv.ToString(), Encoding.UTF8);
}
private static void WebHasUniqueRoleAssignmentRecursive(Web spWeb, ClientContext ctx)
{
ctx.Load(spWeb, w => w.Webs, w => w.ServerRelativeUrl, w => w.Title, w => w.Lists.Include(l => l.HasUniqueRoleAssignments, l => l.Title), w => w.HasUniqueRoleAssignments);
ctx.ExecuteQuery();
if (spWeb.HasUniqueRoleAssignments)
{
getExternalUsersAndPermissionsWeb(spWeb);
}
foreach (var list in spWeb.Lists)
{
if (list.Title != "MicroFeed" | list.Title != "Delningslänkar")
{
ListHasUniqueRoleAssignment(spWeb, list, ctx);
}
}
foreach (var subWeb in spWeb.Webs)
{
WebHasUniqueRoleAssignmentRecursive(subWeb, ctx);
}
}
private static void ListHasUniqueRoleAssignment(Web spWeb, List list, ClientContext ctx)
{
var listsFolders = new List<Folder>();
var listsFiles = new List<File>();
var listsItems = new List<ListItem>();
CamlQuery query = CamlQuery.CreateAllItemsQuery();
ListItemCollection items = list.GetItems(query);
ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.HasUniqueRoleAssignments, i => i.File, i => i.Folder, i => i.File.ListItemAllFields.HasUniqueRoleAssignments, i => i.Folder.ListItemAllFields.HasUniqueRoleAssignments));
ctx.ExecuteQuery();
listsItems.AddRange(items);
if (list.HasUniqueRoleAssignments)
{
getExternalUsersAndPermissionsList(spWeb, list);
}
foreach (var listItem in listsItems)
{
if (listItem.FileSystemObjectType == FileSystemObjectType.File && listItem.HasUniqueRoleAssignments)
{
listsFiles.Add(listItem.File);
}
else if (listItem.FileSystemObjectType == FileSystemObjectType.Folder && listItem.HasUniqueRoleAssignments)
{
listsFolders.Add(listItem.Folder);
}
}
foreach (File file in listsFiles)
{
if (file.ListItemAllFields.HasUniqueRoleAssignments)
{
getExternalUsersAndPermissionsFile(spWeb, file);
}
}
foreach (Folder folder in listsFolders)
{
if (folder.ListItemAllFields.HasUniqueRoleAssignments)
{
getExternalUsersAndPermissionsFolder(spWeb, folder);
}
}
}
private static void getExternalUsersAndPermissionsWeb(Web spWeb)
{
var ctx = spWeb.Context;
var assignments = spWeb.RoleAssignments;
// Load roleassingment for web, include users and groups and their permissionslevels on this web
ctx.Load(assignments, assignment => assignment.Include(role => role.Member, role => role.RoleDefinitionBindings));
ctx.ExecuteQuery();
// Iterate trough all assingments
foreach (var roleAssingment in assignments)
{
// If a user loginname contains #ext# it is an external user, so print the user and the permission level
if (roleAssingment.Member.LoginName.ToString().Contains("#ext") & roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.User))
{
var output = String.Format("Site \"{0}\": {1} har rättighet {2} ", spWeb.Title, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output);
csv.AppendLine(output);
}
// If a group
else if (roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.SharePointGroup))
{
// Go to groupcollection we got earlier, get the corresonding groups users
var users = groups.Where(g => g.Title == roleAssingment.Member.Title).First().Users;
// Iterate trough users
foreach (var user in users)
{
// If a user loginname contains #ext# it is an external user, so print the user and the permission level
if (user.LoginName.ToString().Contains("#ext#"))
{
var output2 = String.Format("Site \"{0}\": {1} har rättighet {2} ", spWeb.Title, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output2);
csv.AppendLine(output2);
}
}
}
}
}
private static void getExternalUsersAndPermissionsList(Web spWeb, List list)
{
var ctx = spWeb.Context;
var assignments = list.RoleAssignments;
ctx.Load(assignments, assignment => assignment.Include(role => role.Member, role => role.RoleDefinitionBindings));
ctx.ExecuteQuery();
foreach (var roleAssingment in assignments)
{
if (roleAssingment.Member.LoginName.ToString().Contains("#ext") & roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.User))
{
var output = String.Format("Lista \"{0}\": {1} har rättighet {2} ", list.Title, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output);
csv.AppendLine(output);
}
else if (roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.SharePointGroup))
{
var users = groups.Where(g => g.Title == roleAssingment.Member.Title).First().Users;
foreach (var user in users)
{
if (user.LoginName.ToString().Contains("#ext#"))
{
var output2 = String.Format("Lista \"{0}\": {1} har rättighet {2} ", list.Title, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output2);
csv.AppendLine(output2);
}
}
}
}
}
private static void getExternalUsersAndPermissionsFile(Web spWeb, File item)
{
var ctx = spWeb.Context;
var assignments = item.ListItemAllFields.RoleAssignments;
ctx.Load(assignments, assignment => assignment.Include(role => role.Member, role => role.RoleDefinitionBindings));
ctx.ExecuteQuery();
foreach (var roleAssingment in assignments)
{
if (roleAssingment.Member.LoginName.ToString().Contains("#ext") & roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.User))
{
var output = String.Format("Fil \"{0}\": {1} har rättighet {2} ", item.Name, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output);
csv.AppendLine(output);
}
else if (roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.SharePointGroup))
{
var users = groups.Where(g => g.Title == roleAssingment.Member.Title).First().Users;
foreach (var user in users)
{
if (user.LoginName.ToString().Contains("#ext#"))
{
var output2 = String.Format("Fil \"{0}\": {1} har rättighet {2} ", item.Name, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output2);
csv.AppendLine(output2);
}
}
}
}
}
private static void getExternalUsersAndPermissionsFolder(Web spWeb, Folder folder)
{
var ctx = spWeb.Context;
var assignments = folder.ListItemAllFields.RoleAssignments;
ctx.Load(assignments, assignment => assignment.Include(role => role.Member, role => role.RoleDefinitionBindings));
ctx.ExecuteQuery();
foreach (var roleAssingment in assignments)
{
if (roleAssingment.Member.LoginName.ToString().Contains("#ext") & roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.User))
{
var output = String.Format("Mapp\\Dokumentgrupp \"{0}\": {1} har rättighet {2} ", folder.Name, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output);
csv.AppendLine(output);
}
else if (roleAssingment.Member.PrincipalType.Equals(Microsoft.SharePoint.Client.Utilities.PrincipalType.SharePointGroup))
{
var users = groups.Where(g => g.Title == roleAssingment.Member.Title).First().Users;
foreach (var user in users)
{
if (user.LoginName.ToString().Contains("#ext#"))
{
var output2 = String.Format("Mapp\\Dokumentgrupp \"{0}\": {1} har rättighet {2} ", folder.Name, roleAssingment.Member.Title, roleAssingment.RoleDefinitionBindings.First().Name);
Console.WriteLine(output2);
csv.AppendLine(output2);
}
}
}
}
}
}
}

Related

Create user inside a transaction

When creating a user like so:
using var trans = Db.BeginTransaction();
AuthRepository.CreateUserAuth(newUser, request.Password);
AuthRepository.AssignRoles(created, new List<string> { request.role });
//.. do other stuff
throw new Exception("other code may throw this");
trans.Commit();
The Auth repo has it's own connection so it's not part of the transaction. This means, if my code bails out, I end up with an unwanted user.
Is there any way to use a transaction with AuthRepository or is only way to manually write to user and role tables? I couldn't find an example of creating the password hash in docs when manually saving, is there any example?
You can't use the existing Auth Repository APIs within a transaction because each uses its own DB connection.
If you want you can take the implementation of those OrmLite Auth Respository APIs in OrmLiteAuthRepository.cs and move them into your method so they're all using the same DB Connection + Transaction.
Also note that to use Transactions in OrmLite you should use OpenTransaction() instead, e.g:
using (var dbTrans = db.OpenTransaction())
{
}
Adding class I used here in case it helps anyone as needed a little refactoring:
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using OutReachPete.ServiceModel.User;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;
namespace OutReachPete.ServiceInterface.User
{
public class UserHelper
{
public static bool ForceCaseInsensitiveUserNameSearch { get; set; } = true;
public static bool UseDistinctRoleTables { get; set; } = true;
public static UserAuthCustom CreateUserAuth(UserAuthCustom newUser, string password, IDbConnection db)
{
newUser.ValidateNewUser(password);
AssertNoExistingUser(db, newUser);
newUser.PopulatePasswordHashes(password);
newUser.CreatedDate = DateTime.UtcNow;
newUser.ModifiedDate = newUser.CreatedDate;
db.Save(newUser);
newUser = db.SingleById<UserAuthCustom>(newUser.Id);
return newUser;
}
public static void AssertNoExistingUser(IDbConnection db, IUserAuth newUser,
IUserAuth exceptForExistingUser = null)
{
if (newUser.UserName != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.UserName);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.UserAlreadyExistsTemplate1,
newUser.UserName.SafeInput()));
}
if (newUser.Email != null)
{
var existingUser = GetUserAuthByUserName(db, newUser.Email);
if (existingUser != null
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
throw new ArgumentException(string.Format(ErrorMessages.EmailAlreadyExistsTemplate1,
newUser.Email.SafeInput()));
}
}
public static UserAuthCustom GetUserAuthByUserName(IDbConnection db, string userNameOrEmail)
{
var isEmail = userNameOrEmail.Contains("#");
var lowerUserName = userNameOrEmail.ToLower();
UserAuthCustom userAuth = null;
// Usernames/Emails are saved in Lower Case so we can do an exact search using lowerUserName
if (HostContext.GetPlugin<AuthFeature>()?.SaveUserNamesInLowerCase == true)
{
return isEmail
? db.Select<UserAuthCustom>(q => q.Email == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == lowerUserName).FirstOrDefault();
}
// Try an exact search using index first
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email == userNameOrEmail).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName == userNameOrEmail).FirstOrDefault();
if (userAuth != null)
return userAuth;
// Fallback to a non-index search if no exact match is found
if (ForceCaseInsensitiveUserNameSearch)
{
userAuth = isEmail
? db.Select<UserAuthCustom>(q => q.Email.ToLower() == lowerUserName).FirstOrDefault()
: db.Select<UserAuthCustom>(q => q.UserName.ToLower() == lowerUserName).FirstOrDefault();
}
return userAuth;
}
public static void AssignRoles(IAuthRepository userAuthRepo, IUserAuth userAuth, IDbConnection db,
ICollection<string> roles = null, ICollection<string> permissions = null)
{
if (userAuthRepo is IManageRoles managesRoles)
{
AssignRoles(userAuth.Id.ToString(), db, roles, permissions);
}
else
{
AssignRolesInternal(userAuth, roles, permissions);
SaveUserAuth(userAuth, db);
}
}
public static IUserAuth GetUserAuth(string userAuthId, IDbConnection db)
{
if (string.IsNullOrEmpty(userAuthId))
throw new ArgumentNullException(nameof(userAuthId));
return db.SingleById<UserAuthCustom>(int.Parse(userAuthId));
}
public static void AssignRoles(string userAuthId, IDbConnection db, ICollection<string> roles = null, ICollection<string> permissions = null)
{
var userAuth = GetUserAuth(userAuthId, db);
if (!UseDistinctRoleTables)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x => userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
SaveUserAuth(userAuth, db);
}
else
{
var now = DateTime.UtcNow;
var userRoles = db.Select<UserAuthRole>(q => q.UserAuthId == userAuth.Id);
if (!roles.IsEmpty())
{
var roleSet = userRoles.Where(x => x.Role != null).Select(x => x.Role).ToHashSet();
foreach (var role in roles)
{
if (!roleSet.Contains(role))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Role = role,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
if (!permissions.IsEmpty())
{
var permissionSet = userRoles.Where(x => x.Permission != null).Select(x => x.Permission).ToHashSet();
foreach (var permission in permissions)
{
if (!permissionSet.Contains(permission))
{
db.Insert(new UserAuthRole
{
UserAuthId = userAuth.Id,
Permission = permission,
CreatedDate = now,
ModifiedDate = now,
});
}
}
}
}
}
private static void AssignRolesInternal(IUserAuth userAuth, ICollection<string> roles, ICollection<string> permissions)
{
if (!roles.IsEmpty())
{
foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
{
if (userAuth.Roles == null)
userAuth.Roles = new List<string>();
userAuth.Roles.Add(missingRole);
}
}
if (!permissions.IsEmpty())
{
foreach (var missingPermission in permissions.Where(x =>
userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
{
if (userAuth.Permissions == null)
userAuth.Permissions = new List<string>();
userAuth.Permissions.Add(missingPermission);
}
}
}
public static void SaveUserAuth(IUserAuth userAuth, IDbConnection db)
{
if (userAuth == null)
throw new ArgumentNullException(nameof(userAuth));
userAuth.ModifiedDate = DateTime.UtcNow;
if (userAuth.CreatedDate == default(DateTime))
userAuth.CreatedDate = userAuth.ModifiedDate;
db.Save((UserAuthCustom)userAuth);
}
}
}
Then just call it like:
var created = UserHelper.CreateUserAuth(newUser, request.Password, Db);
UserHelper.AssignRoles(AuthRepository, created, Db, new List<string> { request.CreateUserType.ToString() });
It will only use the connection passed. Just change UserAuthCustom to whatever you user class is.

SharePoint Microsoft.SharePoint.Client.CamlQuery recursively return folders only (including subfolders)

I am attempting to pull back all the Folders and SubFolders (there can be any number) from a SharePoint site. I don't want the files (there could be thousands), so I am basically trying to just build a folder hierarchy. Additionally I only want the User created folders and the main "Documents" folders, not all the system ones.
That said, I found the following example that I though should have worked, but when I reduce it to just the folders I only get the top level folders:
https://stackoverflow.com/questions/16652288/sharepoint-client-get-all-folders-recursively
Here is the state of the current code. I am probably just missing something on the load (like an expresssion?):
public static void LoadContent(Microsoft.SharePoint.Client.Web web, out Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.Folder>> listsFolders)
{
listsFolders = new Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.Folder>>();
var listsItems = new Dictionary<string, IEnumerable<Microsoft.SharePoint.Client.ListItem>>();
var ctx = web.Context;
var lists = ctx.LoadQuery(web.Lists.Where(l => l.BaseType == Microsoft.SharePoint.Client.BaseType.DocumentLibrary));
ctx.ExecuteQuery();
foreach (var list in lists)
{
var items = list.GetItems(Microsoft.SharePoint.Client.CamlQuery.CreateAllFoldersQuery());
ctx.Load(items);
listsItems[list.Title] = items;
}
ctx.ExecuteQuery();
foreach (var listItems in listsItems)
{
listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == Microsoft.SharePoint.Client.FileSystemObjectType.Folder).Select(i => i.Folder);
}
}
UPDATE
Just to help out anyone else who might just want the main folders and subfolders as a list of urls, here is the final code. I suspect it could be simplified but it is working. The trick after the help below was to get the "root" folder paths, which required a separate query. I think that is where it could prove easier to just get Folders -> Subfolders, but I have Folders -> Subfolders -> Subfolders and this solution gets that last subfolder, along with the root folders.
public static void LoadContent(Microsoft.SharePoint.Client.Web web, List<String> foldersList)
{
Dictionary<string, IEnumerable<Folder>> listsFolders = new Dictionary<string, IEnumerable<Folder>>();
var listsItems = new Dictionary<string, IEnumerable<ListItem>>();
var ctx = web.Context;
var lists = ctx.LoadQuery(web.Lists.Include(l => l.Title).Where(l => l.BaseType == BaseType.DocumentLibrary && !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));
ctx.ExecuteQuery();
foreach (var list in lists)
{
ctx.Load(list.RootFolder);
ctx.ExecuteQuery();
}
foreach (var list in lists)
{
if (list.Title != "Form Templates" && list.Title != "MicroFeed" && list.Title != "Site Assets" && list.Title != "Site Pages")
{
foldersList.Add(list.RootFolder.ServerRelativeUrl);
var items = list.GetItems(CamlQuery.CreateAllFoldersQuery());
ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));
listsItems[list.Title] = items;
}
}
ctx.ExecuteQuery();
foreach (var listItems in listsItems)
{
listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == FileSystemObjectType.Folder).Select(i => i.Folder);
}
foreach (var item in listsFolders)
{
IEnumerable<Folder> folders = item.Value;
foreach (Folder folder in folders)
{
foldersList.Add(folder.ServerRelativeUrl);
}
}
}
An example of what this returns:
1) In the provided example, to return Folder object, it needs to be explicitly included otherwise the exception occur, so replace the line:
ctx.Load(items);
with:
ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));
2) "system" libraries could be excluded like this:
var lists = ctx.LoadQuery(web.Lists.Where(l => !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));
Modified example
public static void LoadContent(Web web, out Dictionary<string, IEnumerable<Folder>> listsFolders)
{
listsFolders = new Dictionary<string, IEnumerable<Folder>>();
var listsItems = new Dictionary<string, IEnumerable<ListItem>>();
var ctx = web.Context;
var lists = ctx.LoadQuery(web.Lists.Include(l =>l.Title).Where(l => l.BaseType == BaseType.DocumentLibrary && !l.Hidden && !l.IsCatalog && !l.IsSiteAssetsLibrary));
ctx.ExecuteQuery();
foreach (var list in lists)
{
var items = list.GetItems(CamlQuery.CreateAllFoldersQuery());
ctx.Load(items, icol => icol.Include(i => i.FileSystemObjectType, i => i.Folder));
listsItems[list.Title] = items;
}
ctx.ExecuteQuery();
foreach (var listItems in listsItems)
{
listsFolders[listItems.Key] = listItems.Value.Where(i => i.FileSystemObjectType == FileSystemObjectType.Folder).Select(i => i.Folder);
}
}
Try this.
var lists = ctx.LoadQuery(ctx.Web.Lists.Where(l => l.BaseType == BaseType.DocumentLibrary));
ctx.ExecuteQuery();
foreach (var list in lists)
{
Console.WriteLine(list.Title);
ListItemCollection listitems = list.GetItems(CamlQuery.CreateAllFoldersQuery());
ctx.Load(listitems, items => items.Include(item => item.Id,item=>item.Folder));
ctx.ExecuteQuery();
foreach (var item in listitems)
{
Console.WriteLine(item.Folder.ServerRelativeUrl);
}
}

How to find object type from URL SharePoint online (o365)?

I found out how to determine the object type from URL for SharePoint on prem:
https://blogs.msdn.microsoft.com/sanjaynarang/2009/04/06/find-sharepoint-object-type-from-url/
But I didn't find anything for SharePoint Online (CSOM).
Is it possible for SharePoint online?
For the most scenarios such as:
folder url, e.g. https://contoso.sharepoint.com//Documents/Forms/AllItems.aspx?RootFolder=%2FDocuments%2FArchive
list item url, e.g. https://contoso.sharepoint.com/Lists/ShoppingCart/DispForm.aspx?ID=9
list/library url, e.g. https://contoso.sharepoint.com/Lists/Announcements
page url, e.g. https://contoso.sharepoint.com/Lists/Announcements/Newsletter.aspx
the following example demonstrates how to determine client object type:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Net;
using System.Web;
using Microsoft.SharePoint.Client;
namespace O365Console
{
static class ClientObjectExtensions
{
public static ClientObject ResolveClientObjectFromUrl(string resourceUrl, ICredentials credentials)
{
ClientObject targetObject = null;
var resourceUri = new Uri(resourceUrl);
using (var rootCtx = new ClientContext(resourceUri.Scheme + Uri.SchemeDelimiter + resourceUri.Host))
{
rootCtx.Credentials = credentials;
var webUrl = Web.WebUrlFromPageUrlDirect(rootCtx, resourceUri);
using (var ctx = new ClientContext(webUrl.ToString()))
{
ctx.Credentials = credentials;
var queryBag = System.Web.HttpUtility.ParseQueryString(resourceUri.Query);
if (queryBag["Id"] != null)
{
var listUrl = string.Join(string.Empty,
resourceUri.Segments.Take(resourceUri.Segments.Length - 1));
var list = ctx.Web.GetList(listUrl);
targetObject = TryRetrieve(() => list.GetItemById(Convert.ToInt32(queryBag["Id"])));
}
else if (queryBag["RootFolder"] != null)
{
var folderUrl = HttpUtility.UrlDecode(queryBag["RootFolder"]);
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(folderUrl));
}
else if (queryBag.Count > 0)
{
throw new Exception("Unsupported query string parameter found");
}
else
{
targetObject = TryRetrieve(() => ctx.Web.GetFileByServerRelativeUrl(resourceUri.AbsolutePath));
if (targetObject == null)
{
targetObject = TryRetrieve(() => ctx.Web.GetList(resourceUri.AbsolutePath),list => list.RootFolder);
if (targetObject == null || ((List)targetObject).RootFolder.ServerRelativeUrl != resourceUri.AbsolutePath)
targetObject = TryRetrieve(() => ctx.Web.GetFolderByServerRelativeUrl(resourceUri.AbsolutePath));
}
}
}
}
return targetObject;
}
private static T TryRetrieve<T>(Func<T> loadMethod, params Expression<Func<T,object>>[] retrievals) where T : ClientObject
{
try
{
var targetObject = loadMethod();
targetObject.Context.Load(targetObject, retrievals);
targetObject.Context.ExecuteQuery();
return targetObject;
}
catch
{
}
return default(T);
}
}
}
Usage
var credentials = GetCredentials(userName, password);
var clientObj = ClientObjectExtensions.ResolveClientObjectFromUrl("https://contoso.sharepoint.com/Lists/Announcements", credentials);
Console.WriteLine(clientObj.GetType().Name);
where
static ICredentials GetCredentials(string userName,string password)
{
var securePassword = new SecureString();
foreach (var c in password)
{
securePassword.AppendChar(c);
}
return new SharePointOnlineCredentials(userName, securePassword);
}

Need help to deal with Photo for Xamarin forms

I am using xamarin forms. I want to pick photo from gallery for my iphone app and want to save it in Azure DB. Is there any solution available for xamarin forms. Or Is there any plugin available to deal with Photo, Document, or Audio. Any help is appreciated.
Using dependency service you can take or pick photos from Android / iPhone :-
Please refer to code below and try to implement the similar code:-
This is the interface in PCL:-
public interface IGalleryProvider
{
Task<List<AttachmentMediaFile>> PickPhotoAsync();
Task<List<AttachmentMediaFile>> PickAudioAsync();
Task<List<AttachmentMediaFile>> PickDocumentAsync();
Task<AttachmentMediaFile> PickProfilePhotoAsync();
Task SaveToGalleryAsync(AttachmentMediaFile file);
}
Below is the code using which you can pick or take photos from iPhone only:-
using AssetsLibrary;
using AVFoundation;
using ELCImagePicker;
using Foundation;
using MediaPlayer;
using MobileCoreServices;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UIKit;
[assembly: Xamarin.Forms.Dependency(typeof(GalleryProvider))]
namespace SanketSample.MobileApp.Sample.iOS.Common
{
public class GalleryProvider : IGalleryProvider
{
private TaskCompletionSource<List<AttachmentMediaFile>> _audioPickedTask;
public async Task<List<AttachmentMediaFile>> PickAudioAsync()
{
_audioPickedTask = new TaskCompletionSource<List<AttachmentMediaFile>>();
var picker = new MPMediaPickerController();
ShowViewController(picker);
picker.ItemsPicked += OnAudioPicked;
picker.DidCancel += OnCancel;
var media = await _audioPickedTask.Task;
return media;
}
private void OnCancel(object sender, EventArgs e)
{
var picker = sender as MPMediaPickerController;
picker.DidCancel -= OnCancel;
picker.DismissViewController(true, null);
_audioPickedTask.TrySetResult(new List<AttachmentMediaFile>());
}
private void OnAudioPicked(object sender, ItemsPickedEventArgs e)
{
var media = new List<AttachmentMediaFile>();
var picker = sender as MPMediaPickerController;
picker.ItemsPicked -= OnAudioPicked;
picker.DismissViewController(true, null);
if (e.MediaItemCollection.Items != null)
{
foreach (var item in e.MediaItemCollection.Items)
{
//var vm1 = (new ViewModelLocator()).AttachmentsVM.SelectedAttachments.Add();
if (!item.IsCloudItem)
{
try
{
//var error = new NSError();
//var asset = new AVUrlAsset(item.AssetURL);
//var exporter = new AVAssetExportSession(asset, item.Title);
//exporter.OutputFileType = "com.apple.m4a-audio";
//AVAssetExportSession session = new AVAssetExportSession(asset, "");
//var reader = new AVAssetReader(asset, out error);
//var settings = new NSDictionary();
//Func<byte[]> bytesGetter = e.MediaItemCollection
//TODO item.Title, item.Title SSSanket,
//var _asset = AVAsset.FromUrl(NSUrl.FromFilename(item.AssetURL.ToString()));
//var _exportSession = new AVAssetExportSession(_asset, AVAssetExportSession.PresetPassthrough);
//_exportSession.OutputFileType = AVFileType.Aiff;
// media.Add(new AttachmentMediaFile(item.AssetURL.AbsoluteString, AttachmentMediaFileType.Audio, null , item.Title));
}
catch (Exception ex)
{
// throw ;
}
}
}
}
_audioPickedTask.TrySetResult(media);
}
public async Task<List<AttachmentMediaFile>> PickDocumentAsync()
{
var task = new TaskCompletionSource<List<AttachmentMediaFile>>();
var allowedUTIs = new string[]
{
UTType.UTF8PlainText,
UTType.PlainText,
UTType.RTF,
UTType.Text,
UTType.PDF,
"com.microsoft.word.doc",
"com.microsoft.excel.xls"
};
var pickerMenu = new UIDocumentMenuViewController(allowedUTIs, UIDocumentPickerMode.Open);
pickerMenu.DidPickDocumentPicker += (sender, args) =>
{
args.DocumentPicker.DidPickDocument += (sndr, pArgs) =>
{
var securityEnabled = pArgs.Url.StartAccessingSecurityScopedResource();
NSError err;
var fileCoordinator = new NSFileCoordinator();
var docs = new List<AttachmentMediaFile>();
// Read bytes.
fileCoordinator.CoordinateRead(pArgs.Url, 0, out err, (NSUrl newUrl) =>
{
NSData data = NSData.FromUrl(newUrl);
docs.Add(new AttachmentMediaFile(pArgs.Url.AbsoluteString, AttachmentMediaFileType.Doc, data.ToArray(),null));
task.TrySetResult(docs);
});
};
ShowViewController(args.DocumentPicker);
};
ShowViewController(pickerMenu);
return await task.Task;
}
public async Task<List<AttachmentMediaFile>> PickPhotoAsync()
{
var media = new List<AttachmentMediaFile>();
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 15;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes= imageEditor.ResizeImage(item.Image, 1024, 1024);
media.Add(new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name));
}
}
});
});
return media;
}
public async Task<AttachmentMediaFile> PickProfilePhotoAsync()
{
AttachmentMediaFile selectMediaFile = null;
var picker = ELCImagePickerViewController.Instance;
picker.MaximumImagesCount = 1;
ShowViewController(picker);
await picker.Completion.ContinueWith(result =>
{
picker.BeginInvokeOnMainThread(() =>
{
picker.DismissViewController(true, null);
if (!result.IsCanceled && result.Exception == null)
{
var imageEditor = new ImageEditor();
var items = result.Result as List<AssetResult>;
foreach (var item in items)
{
var bbytes = imageEditor.ResizeImage(item.Image, 1024, 1024);
selectMediaFile = new AttachmentMediaFile(item.Path, AttachmentMediaFileType.Photo, bbytes, item.Name);
}
}
});
});
return selectMediaFile;
}
public async Task SaveToGalleryAsync(AttachmentMediaFile file)
{
var bytes = file.GetBytes();
var originalImage = ImageEditor.ImageFromByteArray(bytes);
var library = new ALAssetsLibrary();
var orientation = (ALAssetOrientation)originalImage.Orientation;
var nsUrl = await library.WriteImageToSavedPhotosAlbumAsync(originalImage.CGImage, orientation);
}
private void ShowViewController(UIViewController controller)
{
var topController = UIApplication.SharedApplication.KeyWindow.RootViewController;
while (topController.PresentedViewController != null)
{
topController = topController.PresentedViewController;
}
topController.PresentViewController(controller, true, null);
}
}
}
Below are useful classes :-
public class AttachmentMediaFile
{
private readonly Func<byte[]> _bytesGetter;
public string LocalPath { get; private set; }
public string Name { get; private set; }
public AttachmentMediaFileType Type { get; private set; }
public AttachmentMediaFile(string localPath, AttachmentMediaFileType type, byte[] bytesGetter, string name = null)
{
LocalPath = localPath;
Type = type;
_bytesGetter = () =>
{
return bytesGetter;
};
if (string.IsNullOrEmpty(name))
{
Name = FileNameHelper.PrepareName(localPath);
}
else
{
Name = name;
}
}
public byte[] GetBytes()
{
return _bytesGetter();
}
}
public enum AttachmentMediaFileType
{
Photo = 0,
Audio = 1,
Doc = 2,
Video = 3,
}
public static class FileNameHelper
{
private const string Prefix = "IMG";
public static string PrepareName(string localPath)
{
var name = string.Empty;
if (!string.IsNullOrEmpty(localPath))
{
name = localPath.Split('/').Last();
}
return name;
}
public static string GenerateUniqueFileName(Extension extension)
{
var format = ".jpg";
var fileName = string.Concat(Prefix, '_', DateTime.UtcNow.Ticks, format);
return fileName;
}
public enum Extension
{
JPG
}
}
Now if you want to store your data to Azure Server Table so you are already using Azure mobile service client SDK similarly you need Blob nuget from Azure using which you can save your photos by making blob objects to Azure server :-
use blob helper nuget from manage nuget package install Microsoft.WindowsAzure.Storage.Auth;
Microsoft.WindowsAzure.Storage.Blob;
this and try to implement the code similarly I given bellow:-
using Acr.UserDialogs;
using Microsoft.WindowsAzure.Storage.Auth;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using SanketSample.MobileApp.sample.Business.Azure;
using SanketSample.MobileApp.sample.Business.Interfaces;
using SanketSample.MobileApp.sample.Models;
using SanketSample.MobileApp.sample.Models.AzureTables;
using SanketSample.MobileApp.sample.Models.Media;
using SanketSample.MobileApp.sample.Utils;
using Xamarin.Forms;
namespace SanketSample.MobileApp.Sanket.Common.Media
{
public class BlobHelper
{
private const string ContainerName = "attachments";
private Dictionary<string, TaskCompletionSource<bool>> _tasks;
private IHttpService _httpservice { get; set; }
#region Singleton Implementation
private static readonly Lazy<BlobHelper> lazyInstance = new Lazy<BlobHelper>(() => new BlobHelper(), true);
private BlobHelper()
{
_tasks = new Dictionary<string, TaskCompletionSource<bool>>();
}
public static BlobHelper Instance
{
get { return lazyInstance.Value; }`enter code here`
}
#endregion Singleton Implementation
public async Task UploadAttachments(IList<AttachmentFile> attachments, long associatedRecordId, string category)
{
foreach (var attachment in attachments)
{
await UploadAttachment(attachment, associatedRecordId, category);
}
}
public async Task UploadAttachment(AttachmentFile attachment, long associatedRecordId, string category)
{
try
{
CommonHelper commonHelper = new CommonHelper();
attachment.ContainerName = ContainerName;
attachment.AssociatedRecordId = associatedRecordId;
//attachment.RecordId = commonHelper.GenerateRecordId();
if (attachment.FileExtension == null)
{
attachment.FileExtension = ConvertType(attachment.MediaFile);
}
attachment.Category = category;
var taskCompletionSource = new TaskCompletionSource<bool>();
if (!_tasks.ContainsKey(attachment.Name))
{ _tasks.Add(attachment.Name, taskCompletionSource); }
else
{
_tasks[attachment.Name] = taskCompletionSource;
}
// _tasks.Add(attachment.Name, taskCompletionSource);
var attachmentsTableOnline = AzureServiceProvider.Instance.GetRemoteTable<AttachmentFile>();
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.InsertAsync(attachment);
}
var attachmentsTableOffline = AzureServiceProvider.Instance.GetLocalTable<AttachmentFile>();
await attachmentsTableOffline.InsertAsync(attachment);
if (!string.IsNullOrEmpty(attachment.SasQueryString))
{
var credentials = new StorageCredentials(attachment.SasQueryString);
var imageUri = new Uri(attachment.Uri);
var container = new CloudBlobContainer(new Uri(string.Format("https://{0}/{1}",
imageUri.Host, attachment.ContainerName)), credentials);
var blobFromSASCredential = container.GetBlockBlobReference(attachment.Name);
try
{
var bytes = attachment.MediaFile.GetBytes();
await blobFromSASCredential.UploadFromByteArrayAsync(bytes, 0, bytes.Length);
if (CheckInternetConnection.IsConnected())
{
await attachmentsTableOnline.UpdateAsync(attachment);
}
await attachmentsTableOffline.UpdateAsync(attachment);
taskCompletionSource.TrySetResult(true);
}
catch (Microsoft.WindowsAzure.Storage.StorageException ex)
{
// Throws from UploadFromByteArrayAsync, but image uploaded.
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(true);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"BlobHelper: {ex}");
taskCompletionSource.TrySetResult(false);
}
}
}
catch (Exception ca)
{
//throw ca;
}
}
/// <summary>
/// Downloads Blob Data boject and returns the Byts[] data
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachment(AttachmentFile file)
{
byte[] bytes = null;
var fileContainer = file.Uri.Replace(file.Name, string.Empty);
var container = new CloudBlobContainer(new Uri(fileContainer));
var blob = container.GetBlockBlobReference(file.Name);
using (var stream = new MemoryStream())
{
var isExist = await blob.ExistsAsync();
if (isExist)
{
await blob.DownloadToStreamAsync(stream);
bytes = stream.ToArray();
}
}
return bytes;
}
/// <summary>
/// Updates the Attachments Byts in the Azure Local Tables.
/// </summary>
/// <param name="AttachmentFileRecordId">Attachments Byte[] Data.</param>
/// <returns></returns>
public async Task<byte[]> DownloadAttachmentFileDetails(long? AttachmentFileRecordId, IHttpService service)
{
_httpservice = service;
try
{
ResponseWrapper<AttachmentFileDetail> result = new ResponseWrapper<AttachmentFileDetail>();
if (AttachmentFileRecordId != null)
{
var request = Constants.API_BASE_URL + string.Format(Constants.API_ATTACHMENTS_PARAMETERS, AttachmentFileRecordId);
var response = await _httpservice.SendRequestAsync(HttpMethod.Get, request);
result.Status = response.Status;
if (response.IsSuccess)
{
result.Result = JsonConvert.DeserializeObject<AttachmentFileDetail>(response.Result);
if (result.Result == null)
{
result.Status = System.Net.HttpStatusCode.InternalServerError;
}
else
{
var output = result.Result;
var data = new List<AttachmentFileDetail>() { output };
await AzureServiceProvider.Instance.DatabaseService.InsertDataToLocalDB<AttachmentFileDetail>(data);
return result.Result.FileByteArray;
}
}
}
}
catch (Exception ex)
{
////throw ex;
}
finally
{
}
return null;
}
private string ConvertType(AttachmentMediaFile file)
{
switch (file.Type)
{
case AttachmentMediaFileType.Doc:
return "doc";
case AttachmentMediaFileType.Audio:
return "mp3";
}
return "jpeg";
}
}
}
media plugin on github
works pretty well for me.

Read All users related to RoleDefinition of ProjectSite using CSOM

I need to loop through all RoleDefinitiona of ProjectSite and get all users related to each one and add those users to a RoleDefinition of a ProjectSite in another SiteCollection
I can loop through RoleDefinitions like :
using (var src_ctx = new ClientContext(Root))
{
RoleDefinitionCollection role_definition_collection = src_ctx.Web.RoleDefinitions;
src_ctx.Load(role_definition_collection);
src_ctx.ExecuteQuery();
foreach (RoleDefinition role_definition in role_definition_collection)
{
}
}
also I can loop through all groups and read users of each group then get RoleAssignment of each user:
GroupCollection group_collection = src_ctx.Web.SiteGroups;
src_ctx.Load(group_collection);
src_ctx.ExecuteQuery();
foreach (Group group in group_collection)
{
UserCollection user_collection = group.Users;
foreach (User user in user_collection)
{
RoleAssignment role_assignment = src_ctx.Web.RoleAssignments.GetByPrincipal(user);
RoleDefinitionBindingCollection role_definition_binding_collection = role_assignment.RoleDefinitionBindings;
}
}
but how can I link between RoleDefinition and users ?
the following class used to copy users with its permissions, may help someone
public static class UserHelper
{
// static members
private static Dictionary<string, string> Levels = new Dictionary<string, string>();
private static void Prepare_Dictionary()
{
Levels["Full Control"] = "Full Control";
Levels["Design"] = "Design";
Levels["Contribute"] = "Contribute";
Levels["Read"] = "Read";
Levels["Limited Access"] = "Limited Access";
}
public static void Start_Moving_Users(string Root2010,string Root2013)
{
Prepare_Dictionary();
using (ClientContext src_ctx = new ClientContext(Root2010))
{
using (ClientContext dest_ctx = new ClientContext(Root2013))
{
dest_ctx.Web.BreakRoleInheritance(true, false);
RoleAssignmentCollection src_rac = src_ctx.Web.RoleAssignments;
src_ctx.Load(src_rac);
src_ctx.ExecuteQuery();
foreach (RoleAssignment src_ra in src_rac)
{
try
{
src_ctx.Load(src_ra.Member);
src_ctx.ExecuteQuery();
RoleDefinitionBindingCollection src_rdb = src_ra.RoleDefinitionBindings;
src_ctx.Load(src_rdb);
src_ctx.ExecuteQuery();
RoleDefinition src_rd = src_rdb[0];
src_ctx.Load(src_rd);
src_ctx.ExecuteQuery();
Principal dest_user = dest_ctx.Web.EnsureUser(src_ra.Member.LoginName);
RoleDefinition dest_rd = dest_ctx.Web.RoleDefinitions.GetByName(Levels[src_rd.Name]);
RoleDefinitionBindingCollection dest_rdb = new RoleDefinitionBindingCollection(dest_ctx);
dest_rdb.Add(dest_rd);
dest_ctx.Web.RoleAssignments.Add(dest_user, dest_rdb);
dest_ctx.ExecuteQuery();
}
catch { continue; }
}
}
}
}
}

Resources