Sitefinity - Revision history empty when publishing dynamic content item - sitefinity

I tried to use the following code to publish a dynamic content item. It works, but I thought it would create a new record in revision history of that item, but it doesn't:
public void PublishProduct()
{
// Set the provider name for the DynamicModuleManager here. All available providers are listed in
// Administration -> Settings -> Advanced -> DynamicModules -> Providers
var providerName = String.Empty;
// Set a transaction name and get the version manager
var transactionName = "someTransactionName";
var versionManager = VersionManager.GetManager(null, transactionName);
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName, transactionName);
Type productType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Products.Product");
DynamicContent productItem = dynamicModuleManager.CreateDataItem(productType);
// This is how values for the properties are set
productItem.SetValue("Title", "Some Title");
productItem.SetValue("Description", "Some Description");
productItem.SetString("UrlName", "SomeUrlName");
productItem.SetValue("Owner", SecurityManager.GetCurrentUserId());
productItem.SetValue("PublicationDate", DateTime.UtcNow);
productItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Draft");
// Create a version and commit the transaction in order changes to be persisted to data store
versionManager.CreateVersion(productItem, false);
// We can now call the following to publish the item
ILifecycleDataItem publishedproductItem = dynamicModuleManager.Lifecycle.Publish(productItem);
//You need to set appropriate workflow status
productItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Published");
// Create a version and commit the transaction in order changes to be persisted to data store
versionManager.CreateVersion(productItem, true);
// Commit the transaction in order for the items to be actually persisted to data store
TransactionManager.CommitTransaction(transactionName);
}
I used another method as well but doesn't work as well:
public void PublishProduct()
{
// Set the provider name for the DynamicModuleManager here. All available providers are listed in
// Administration -> Settings -> Advanced -> DynamicModules -> Providers
var providerName = String.Empty;
var versionManager = VersionManager.GetManager();
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName, transactionName);
Type productType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Products.Product");
DynamicContent productItem = dynamicModuleManager.CreateDataItem(productType);
// This is how values for the properties are set
productItem.SetValue("Title", "Some Title");
productItem.SetValue("Description", "Some Description");
productItem.SetString("UrlName", "SomeUrlName");
productItem.SetValue("Owner", SecurityManager.GetCurrentUserId());
productItem.SetValue("PublicationDate", DateTime.UtcNow);
productItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Draft");
// Create a version and commit the transaction in order changes to be persisted to data store
versionManager.CreateVersion(productItem, true);
versionManager.SaveChanges();
productItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Draft");
dynamicModuleManager.SaveChanges();
dynamicModuleManager.Lifecycle.Publish(productItem);
productItem.ApprovalWorkflowState.SetString("Published", true);
dynamicModuleManager.SaveChanges();
}
I tried to move versionManager.CreateVersion(productItem, true); to some another places in the code as well, for eg: after publishing the item, or the end of the method, but nothing created in revision history.
Could anyone help please? Thanks in advance!

I think revision history is for one "Item". But you are creating here a new item with:
DynamicContent productItem = dynamicModuleManager.CreateDataItem(productType);
I suggest you somehow find your existing item, edit, and commit/publish to add new record to revision history of that EXISTING item.
I would try something like this:
public void AddOrUpdateItem(string someIdentifier)
{
var providerName = string.Empty;
string txName = Guid.NewGuid().ToString();
try
{
var versionManager = VersionManager.GetManager(null, txName);
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(providerName, txName);
DynamicContent someItem = GetItemBySomeIdentifier(someIdentifier);
bool createNew = someItem == null;
if (createNew)
{
someItem = dynamicModuleManager.CreateDataItem(_bannerMetadataType);
someItem.SetValue("SomeIdColumn", someIdentifier);
someItem.SetValue("Owner", SecurityManager.GetCurrentUserId());
someItem.SetString("UrlName", someIdentifier);
}
someItem.SetValue("SomeFiled", "xyz");
someItem.SetValue("PublicationDate", DateTime.Now);
someItem.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Published",
CultureInfo.CreateSpecificCulture(SystemManager.CurrentContext.CurrentSite.DefaultCulture));
versionManager.CreateVersion(someItem, true);
TransactionManager.CommitTransaction(txName);
}
catch (Exception)
{
TransactionManager.RollbackTransaction(txName);
throw;
}
}

Related

Document permissions Content Engine API

I'm trying to remove/add the groups from security of a document in FileNet using CPE API. I am able to remove wihtout any issues. However, when I try to add the groups that are missing, by inheriting from document class, groups get added without full permissions. For example, I remove "author" group and when I try to add the same group back, it does not have all the permissions.
Remove groups:
AccessPermissionList apl = doc.get_Permissions();
Iterator iter = apl.iterator();
while (iter.hasNext())
{
AccessPermission ap = (AccessPermission)iter.next();
if(ap.get_GranteeName().contains("group name")){
iter.remove();
}
}
doc.set_Permissions(apl);
doc.save(RefreshMode.NO_REFRESH);
Add groups:
DocumentClassDefinition docClassDef = Factory.DocumentClassDefinition.fetchInstance(os, classID, null);
AccessPermissionList docClassApl = docClassDef.get_Permissions();
Iterator docClassApliter = docClassApl.iterator();
for(Object obj : docClassApl)
{
AccessPermission ap = (AccessPermission)obj;
if(!apl.contains(ap)){
apl.add(ap);
}
}
doc.set_Permissions(apl);
doc.save(RefreshMode.NO_REFRESH);
RESOLVED:
Had to use DefaultInstanceSecurity rather than regular security as the permissions in both instances were different. So just updated the following line of code:
AccessPermissionList docClassApl = docClassDef.get_DefaultInstancePermissions();
You need to set AccessMask too. Like below:
AccessPermission ap;
ap.set_AccessMark ( new Integer (AccessLevel.FULL_CONTROL_DOCUMENT_AS_INT));
//AccessLevel.WRITE_DOCUMENT_AS_INT
//AccessLevel.MAJOR_VERSION_DOCUMENT_AS_INT
Version 5.2.0 onwards, AccessLevel is deprecated but you can give it a try. AccessRight is the replacement now. Refer this.
Update
public static void setPermissions(Document doc) throws IOException {
//In cpetarget.properties file
//cpetarget.security=Administrator:FULL_CONTROL,p8admin:MODIFY_PROPERTIES
InputStream input = new FileInputStream("cpetarget.properties");
java.util.Properties prop = new java.util.Properties();
prop.load(input);
List<String> strList = new ArrayList<String>(Arrays.asList(prop.getProperty("cpetarget.security").split(",")));
AccessPermissionList apl = doc.get_Permissions();
Iterator<AccessPermission> itr = apl.iterator();
List<AccessPermissionList> oldPermissionsList = new ArrayList<AccessPermissionList>();
oldPermissionsList.addAll(apl);
// Remove all your old permissions here
apl.removeAll(oldPermissionsList);
// Add all your new permissions here
try {
for (String str : strList) {
String[] strArray = str.split(":");
AccessPermission permission = Factory.AccessPermission.createInstance();
permission.set_GranteeName(strArray[0]);
permission.set_AccessType(AccessType.ALLOW);
permission.set_InheritableDepth(new Integer(0));
//permission.set_InheritableDepth(new Integer(0)); // this object only
//permission.set_InheritableDepth(new Integer(-1));this object and all children
//permission.set_InheritableDepth(new Integer(1)); this object and immediate children
if (strArray[1].equalsIgnoreCase("FULL_CONTROL")) {
permission.set_AccessMask(new Integer(AccessLevel.FULL_CONTROL_DOCUMENT_AS_INT));
//permission.set_AccessMask(AccessRight.MAJOR_VERSION_AS_INT);
}
if (strArray[1].equalsIgnoreCase("READ_ONLY")) {
permission.set_AccessMask(new Integer(AccessLevel.VIEW_AS_INT));
}
if (strArray[1].equalsIgnoreCase("MODIFY_PROPERTIES")) {
permission.set_AccessMask(new Integer(AccessLevel.WRITE_DOCUMENT_AS_INT));
}
if (strArray[1].equalsIgnoreCase("MAJOR_VERSIONING")) {
permission.set_AccessMask(new Integer(AccessLevel.MAJOR_VERSION_DOCUMENT_AS_INT));
}
AccessPermissionList permissions = doc.get_Permissions();
permissions.add(permission);
doc.set_Permissions(permissions);
doc.save(RefreshMode.REFRESH);
System.out.println("Done");
}
} catch (Exception e) {
e.printStackTrace();
}
}

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;
}

NHibernate Dynamic Component Default Value Issue

All of my entities (that are mapped to a database table) inherit from an entity class with a dynamic component on it called Attributes e.g.:
public abstract class Entity<T> {
public virtual T Id { get; set; }
private IDictionary _attributes;
public virtual IDictionary Attributes {
get { return _attributes ?? (_attributes = new Hashtable()); }
set { _attributes = value; }
}
}
The Attributes collection allows me to add extra fields to each entity without directly changing the entity itself. This allows me to make my application more modular.
For example say I have the following entity:
public class User : Entity<int> {
public virtual string Name { get; set; }
}
Now say I have a Forum module which needs a NumPosts property against the User. I would add the field against the Users table in the database. This field is non nullable and has a default value of 0. I then map the field using the dynamic component against the User entity.
However when I try inserting the user by saying:
session.Save(new User() { Name = "Test" });
It throws an error as it's expecting me to set a value for NumPosts and the generated SQL would be something like:
INSERT INTO Users (Name, NumPosts) VALUES ('Test', NULL)
However NumPosts does not allow nulls and hence the error. Ideally I'd like it to say the following if the Attributes collection does not contain an entry for NumPosts:
INSERT INTO Users (Name) VALUES ('Test')
An alternative is to say the following which would work fine:
session.Save(new User() { Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } } });
The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this.
For reference here's a bare bones version of session factory method which maps the NumPosts field:
return Fluently.Configure()
...
.ExposeConfiguration(c => {
// Get the persistent class
var persistentClass = c.GetClassMapping("User");
// Create the attributes component
var component = new Component(persistentClass);
// Create a simple value
var simpleValue = new SimpleValue(persistentClass.Table);
// Set the type name
simpleValue.TypeName = "Int32";
// Create a new db column specification
var column = new Column("NumPosts");
column.Value = simpleValue;
column.Length = 10;
column.IsNullable = false;
column.DefaultValue = "0";
// Add the column to the value
simpleValue.AddColumn(column);
// Ad the value to the component
component.AddProperty(new Property() { Name = column.Name, Value = simpleValue });
// Add the component property
persistentClass.AddProperty(new Property() { Name = "Attributes", Value = component });
})
.BuildConfiguration();
I'd appreciate if someone could let me know if this is possible. Thanks
You know how to make it working as described above:
... An alternative is to say the following which would work fine:
session.Save(new User()
{
Name = "Test", Attributes = new Hashtable() { { "NumPosts", 0 } }
});
... The problem I have is that I don't want the modules to have a dependency on each other and I can't really say this...
In case, that the biggest issue is the explicit Attributes initialization ("...I don't want the modules to have a dependency...") we can use:
12.2. Event system
So, with Listener like this:
[Serializable]
public class MyPersistListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(SaveOrUpdateEvent #event)
{
var entity = #event.Entity as Entity<int>; // some interface IHaveAttributes
if (entity == null) // would be more appropriate
{
return;
}
var numPosts = entity.Attributes["NumPosts"] as int?;
if (numPosts.HasValue)
{
return;
}
entity.Attributes["NumPosts"] = 0;
}
}
Based on this doc snippet:
Configuration cfg = new Configuration();
ILoadEventListener[] stack = new ILoadEventListener[] { new MyLoadListener(), new DefaultLoadEventListener() };
cfg.EventListeners.LoadEventListeners = stack;
This should be the init in our case:
.ExposeConfiguration(c => {
var stack = new ISaveOrUpdateEventListener [] { new MyPersistListener() };
c.EventListeners.SaveEventListeners= stack;

MS Dynamics CRM. Get users who current record shared with

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

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.