MS Dynamics CRM. Get users who current record shared with - entity

I have a entity record which is shared with or more users. I would like to unshare this record when Deactivate it. I want to do that in Plugin. But I can't understand how to get all users from sharing list who have access to this record. How to do that?
Here is my code snippet:
protected void ExecutePostPersonSetStateDynamicEntity(LocalPluginContext localContext)
{
if (localContext == null)
{
throw new ArgumentNullException("localContext");
}
var context = localContext.PluginExecutionContext;
var targetEntity = (Entity)context.InputParameters["EntityMoniker"];
var state = (OptionSetValue)context.InputParameters["State"];
var columns = new ColumnSet(new[] { "statecode" });
var retrivedEntity = localContext.OrganizationService.Retrieve(targetEntity.LogicalName, targetEntity.Id, columns);
if (state.Value == 1)
{
RevokeAccessRequest revokeRequest = new RevokeAccessRequest()
{
Target = new EntityReference(personEntity.LogicalName, personEntity.Id),
Revokee = new EntityReference(neededEntity.LogicalName, needed.Id)
};
// Execute the request.
}
}
As you can see, I need an entity "neededEntity", I don't know how to get it from "targetEntity" or "retrievedEntity".

You need to use a RetrieveSharedPrincipalsAndAccessRequest
http://msdn.microsoft.com/en-us/library/microsoft.crm.sdk.messages.retrievesharedprincipalsandaccessrequest.aspx
You can start from the included example, basically inside the foreach you call your RevokeAcessRequest

Related

Kentico 12 - How to set individual page to require authentication?

On Kentico 12, the property Security inside the Page doesn't have Access field like the previous version Kentico 11 - Interface Access.
I need to provide this feature, so I was thinking about using overriding the OnAuthentication method like this:
protected override void OnAuthentication(AuthenticationContext filterContext)
{
var isAuthenticated = filterContext.Principal.Identity.IsAuthenticated;
var routePath = filterContext.HttpContext.Request.Path;
var page = DocumentHelper.GetDocuments().Path(routePath).FirstOrDefault();
var allowAccess = (page.HasSecureProperty && isAuthenticated) || !page.HasSecureProperty;
if (allowAccess)
{
base.OnAuthentication(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "Signin" })
);
}
}
HasSecureProperty would be the property from the kentico page that admins or editors users can set on the administration panel. I was planning to create this property using custom table and make a interface on the page for the users.
The field IsSecureNode on CMS_Tree seems to be the property that I need and was been used on previous versions, but I couldn't find a way to set on the new admin panel.
Is there another solution to allow users to set authentication on pages? I was concerned about performance since this method will be called on every action. Thank you.
I have done something similar so maybe it will help point you in the right direction.
My entire MVC site requires authentication, so that might be where this differs. In the MVC when I want to get files and check permissions I do something like this:
var files = subcat.Children.WithAllData.WithPermissionsCheck;
On the CMS side, I have a field on the page type that allows a user to select roles and another one for selecting users. I then have a custom event on the document update or insert to update the settings.
Here is the code I use for updating the ACLs:
private void UpdateSettings(TreeNode node)
{
ObjectQuery<RoleInfo> roles = null;
ObjectQuery<UserInfo> users = null;
var columnRoles = node.GetStringValue("Roles", "");
if (columnRoles != "")
{
var rolesConcat = columnRoles.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var where = "RoleName IN " + "('" + string.Join("','", rolesConcat) + "')";
EventLogProvider.LogInformation("Document Event", "Roles", where);
roles = RoleInfoProvider.GetRoles()
.Where(where);
}
var columnUsers = node.GetStringValue("Users", "");
if (columnUsers != "")
{
var usersConcat = columnUsers.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
var where = "UserName IN " + "('" + string.Join("','", usersConcat) + "')";
EventLogProvider.LogInformation("Document Event", "Users", where);
users = UserInfoProvider.GetUsers()
.Where(where);
}
if (node != null)
{
// Gets the ID of the ACL item that stores the page's permission settings
int nodeACLID = ValidationHelper.GetInteger(node.GetValue("NodeACLID"), 0);
// Deletes the page's ACL item
// Removes the page's permission settings for all users and roles
AclItemInfoProvider.DeleteAclItems(nodeACLID);
node.IsSecuredNode = true;
int allowed = DocumentSecurityHelper.GetNodePermissionFlags(NodePermissionsEnum.Read);
// Prepares a value indicating that no page permissions are denied
int denied = 0;
if (users != null)
foreach (var user in users)
{
// Sets the page's permission for the user (allows the 'Modify' permission)
AclItemInfoProvider.SetUserPermissions(node, allowed, denied, user);
}
if (roles != null)
foreach (var role in roles)
{
// Sets the page's permission for the user (allows the 'Modify' permission)
AclItemInfoProvider.SetRolePermissions(node, allowed, denied, role);
}
}
}
You can use the approach mentioned in the documentation on authorizing live site actions.
I ended up using a custom table with an interface for the user to set if the page requires authentication or not. Since this is an override of OnAuthentication, every page calls this method. I hope there is a better solution using built-in Kentico features. Here is the final code:
protected override void OnAuthentication(AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
var routePath = filterContext.HttpContext.Request.Path;
var allowAccess = Authentication.CanEnterPage(filterContext.Principal.Identity.IsAuthenticated, routePath);
if (!allowAccess)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new { controller = "Account", action = "Signin", returnUrl = routePath })
);
}
}
The static method below contains the logic to access the page:
public static bool CanEnterPage(bool isAuthenticated, string routePath)
{
var page = DocumentHelper.GetDocuments().Path(routePath).FirstOrDefault();
if (page == null)
return false;
var pageAccess = PageAccessInfoProvider.GetPageAccesses()
.WhereEquals("PageAccessNodeID", page.NodeID).FirstOrDefault();
// Create a record if pageAccess is null
if (pageAccess == null)
{
pageAccess = CreateRecordsPageAccess(page);
}
var isSecure = pageAccess.PageAccessHasAuthentication;
var allowAccess = isSecure && isAuthenticated || !isSecure;
return allowAccess;
}

How to obtain information about the owner of a Dropbox shared file via the API?

I am writing an application where the users are sharing specific files via Dropbox. I am using the REST API. Lets say A shares a file to B. I know that A can share the file by adding B as a member to a file(add_file_member) and that B can check files shared with him by list_received_files. But I can't find a way that B can see who shared the file. It seems reasonable to have access to this info. Is there a way to obtain some account id or a display info for the user A from B perspective?
The SharedFileMetadata object returned by /sharing/list_received_files doesn't include this information, but I'll be sure to pass this along as a feature request.
However, you can use /sharing/list_file_members to list the members of the shared file, as well as their access level (e.g., owner, editor, etc.).
Example for C# Dropbox SDK.
public async Task<string> GetSharedFileOwnerID(Dropbox.Api.Files.Metadata data, Dropbox.Api.DropboxClient DropboxClient)
{
string owner = "";
var file = data.AsFile;
if ((file.HasExplicitSharedMembers ?? false) || (file.SharingInfo != null))
{
// search the real file owner
var list = await DropboxClient.Sharing.ListFileMembersAsync(file.Id);
var list_continue = false;
do
{
var item = list.Users.First(i => i.AccessType.IsOwner);
if (item != null)
{
owner = item.User.AccountId;
break;
}
list_continue = (list.Cursor != null);
if (list_continue)
{
list = await DropboxClient.Sharing.ListFileMembersContinueAsync(list.Cursor);
}
}
while (list_continue);
}
return owner;
}
public async Task<string> GetSharedFoldeOwnerID(Dropbox.Api.Files.Metadata data, Dropbox.Api.DropboxClient DropboxClient)
{
string owner = "";
var folder = data.AsFolder;
if (folder.SharedFolderId != null)
{
// search the real folder owner
var list = await DropboxClient.Sharing.ListFolderMembersAsync(folder.SharedFolderId);
var list_continue = false;
do
{
var item = list.Users.First(i => i.AccessType.IsOwner);
if (item != null)
{
owner = item.User.AccountId;
break;
}
list_continue = (list.Cursor != null);
if (list_continue)
{
list = await DropboxClient.Sharing.ListFolderMembersContinueAsync(list.Cursor);
}
}
while (list_continue);
}
return owner;
}
C# Dropbox SDK is NuGet package.
Use Install-Package Dropbox.Api for install it.

Get all sharepoint sites in a site collection to which user has access using Javascript client object model

I want to display all sites in a site collection using JSOM to which user has access to. In other words I only need to find collection of sites to which user has access in a site collection. I am able to get all webs but it doesnt work if user doesnt have permissions to some of web sites.
SP.Web.getSubwebsForCurrentUser Method returns a security trimmed (user has access) collection of sub sites (only one level beneath)
Example
var ctx = SP.ClientContext.get_current();
var webs = ctx.get_web().getSubwebsForCurrentUser(null);
ctx.load(webs);
ctx.executeQueryAsync(
function() {
for(var i=0;i< webs.get_count();i++) {
var web = webs.getItemAtIndex(i);
console.log(web.get_title());
}
},
function(sender,args){
console.log(args.get_message());
}
);
If you are interested in all sub webs within site collection, you could consider the following approach.
function getAllSubwebsForCurrentUser(success,error)
{
var ctx = SP.ClientContext.get_current();
var web = ctx.get_site().get_rootWeb();
var result = [];
var level = 0;
var getAllSubwebsForCurrentUserInner = function(web,result,success,error)
{
level++;
var ctx = web.get_context();
var webs = web.getSubwebsForCurrentUser(null);
ctx.load(webs,'Include(Title,Webs)');
ctx.executeQueryAsync(
function(){
for(var i = 0; i < webs.get_count();i++){
var web = webs.getItemAtIndex(i);
result.push(web);
if(web.get_webs().get_count() > 0) {
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
}
level--;
if (level == 0 && success)
success(result);
},
error);
};
getAllSubwebsForCurrentUserInner(web,result,success,error);
}
Usage
getAllSubwebsForCurrentUser(
function(allwebs){
for(var i = 0; i < allwebs.length;i++){
console.log(allwebs[i].get_title());
}
},
function(sendera,args){
console.log(args.get_message());
});
Hi the following code snippet may help you.
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
ctx.load(web);
var webCollection = web.getSubwebsForCurrentUser(null);
ctx.load(webCollection);
ctx.executeQueryAsync(
Function.createDelegate(this,this.onSuccess),
Function.createDelegate(this,this.onError)
);
getSubwebsForCurrentUser - uses a parameter of type SP.SubwebQuery which you may leave as null.
The web collection you get using this code is just of one level. You will not get the subsites of the subsites. For that you need to execute the same statements on every SP.Web object you get - recursively - starting from the root web.
If you can use the API instead then I would suggest you do the following to return all the sub webs for the current user.
using(SPSite site = new SPSite("http://example/site/"))
{
using (SPWeb web = site.OpenWeb())
{
SPWebCollection webCollection = web.GetSubwebsForCurrentUser();
}
}
Note: As pointed out by Helm Sterk in comment below, GetSubwebsForCurrentUser() would not return result which the user is seeking. So above code would not work.

Programmatically create index

How do I create an index programmatically in RavenDB?
I tried to follow this example.
This is my index creator:
public class MyIndex : Raven.Client.Indexes.AbstractIndexCreationTask<MyEntity>
{
public MyIndex()
{
Map = col => col.Select(c => new
{
code = c.Code,
len = c.Code.Length,
sub = c.Code.Substring(0, 1)
});
}
}
And here is the caller:
var store = new Raven.Client.Document.DocumentStore
{
Url = "http://localhost:8080"
};
store.Initialize();
try
{
using (var session = store.OpenSession("MyDB"))
{
Raven.Client.Indexes.IndexCreation.CreateIndexes(
typeof(MyIndex).Assembly, store);
}
}
finally
{
store.Dispose();
}
The index was created but not in MyDB but in system database.
How to create the index in MyDB? Is the way I create index correct?
Try this:
specify the database name in your store object
var store = new Raven.Client.Document.DocumentStore
{
Url = "http://localhost:8080",
DefaultDatabase = "MyDB"
};
As MED pointed out, you can provide a default database when attaching to the document store. When doing so, you no longer pass the database name to the OpenSession method. This is the easiest way, and if you're working with a single database then it is the best answer (and should be given the credit as the answer to this question).
But if you need to work with multiple databases, and thus can't use that technique, then you can use this helper method.
public static void CreateIndexes(Assembly assembly, IDocumentStore store,
string databaseName)
{
var catalog = new AssemblyCatalog(assembly);
var provider = new CompositionContainer(catalog);
var commands = store.DatabaseCommands.ForDatabase(databaseName);
IndexCreation.CreateIndexes(provider, commands, store.Conventions);
}
Call it the same way you would call the other method, but now you can pass the database name as a parameter.

Copying sitecore rendering to new template programmatically using renderingDefinition.ItemId?

I have a custom sitecore button which changes the template of the current item, simple enough.
However as part of this I'm trying to also migrate the renderings of the old layout to a new layout if it's of a certain sublayout type by ItemId. However the ItemId that is returned is always null, the only value I get back from the RenderingDefinition is the UniqueId.
What am I doing wrong?
I have used this blog post as a guide.
The Code
public class ConvertToNewTemplateCommand : Command
{
protected void Run(ClientPipelineArgs args)
{
if (!SheerResponse.CheckModified())
return;
Item item = Context.ContentDatabase.Items[args.Parameters["id"]];
if (args.IsPostBack)
{
if (args.Result == "yes")
{
//Get current layout details
var originalLayoutXml = item[FieldIDs.LayoutField];
//Get new template
TemplateItem hubTemplate = Context.ContentDatabase.GetTemplate("some guid...");
//Change template
item.ChangeTemplate(hubTemplate);
//Reset laytout
ResetLayout(item);
//Get reset layout
var newLayoutXml = item[FieldIDs.LayoutField];
//Add all the module containers to the new layout in the central column
MoveModuleContainers(item, originalLayoutXml, newLayoutXml);
}
}
}
private void MoveModuleContainers(Item item, string oldXml, string newXml)
{
var oldLayout = LayoutDefinition.Parse(oldXml);
var newLayout = LayoutDefinition.Parse(newXml);
bool updated = false;
var oldRenderings = (oldLayout.Devices[0] as DeviceDefinition).Renderings;
var newRenderings = (newLayout.Devices[0] as DeviceDefinition).Renderings;
foreach (RenderingDefinition rendering in oldRenderings)
{
// Here is where the rendering.ItemID is always null
if (rendering != null && !String.IsNullOrEmpty(rendering.ItemID) && new Guid(rendering.ItemID) == new Guid("matching guid..."))
{
rendering.Placeholder = "middlecolumn";
newRenderings.Add(rendering);
updated = true;
}
}
if (updated)
{
// Save item...
}
}
}
I got onto Sitecore support in the end which informed me that I should use:
Sitecore.Data.Fields.LayoutField.GetFieldValue(item.Fields[Sitecore.FieldIDs.LayoutField])
instead of:
item[FieldIDs.LayoutField]
to get the items layoutField correctly. This results in the rendering values being parsed correctly and as they say the rest is history.