Orchard CMS: Adding default data to fields and then querying them - c#

I have added a LinkField called Website to a content type using a part with the same name as the content type.
ContentDefinitionManager.AlterTypeDefinition("MyContentType", a => a
.WithPart("CommonPart")
.WithPart("MyContentType")
.Creatable());
ContentDefinitionManager.AlterPartDefinition("MyContentType", cft => cft
.WithField("Website", a => a.OfType("LinkField").WithDisplayName("Website")
.WithSetting("FieldIndexing.Included", "True"))
.Attachable());
I then create some default content items during the migration.
I'm creating the item before adding the field data because I have had problems with fields not being updated when their values are set before the item is created. (Feel free to shine some light on that, but that isn't my question though)
var myItem = _orchardServices.ContentManager.New("MyContentType");
_orchardServices.ContentManager.Create(myItem);
var websitePart = myItem.Parts.FirstOrDefault(x => x.Fields.Any(y => y.Name == "Website"));
var websiteLinkField = websitePart .Fields.FirstOrDefault(x => x.Name == "Website") as LinkField;
websiteLinkField.Value = "http://www.google.com";
websiteLinkField.Text = "Link to google";
_orchardServices.ContentManager.Publish(myItem);
I realize there are more dynamic ways to access the field, but this seems to work too.
The data shows up when I view the items, but then I move on to making a Query.
I use the UI to build a simple query looking for the word "google" in the text of the LinkField, then I hit preview.
No results are found.
I then open up one of the items created from the migration and simply hit the "Save" button.
Then I try the preview again and the item I saved now shows up.
So as far as I can tell something is happening when I save a content item that I'm not doing from the migration. But I have been stepping through the code going over all angles, and I just can't find it.
I suspect maybe some handler is supposed to get triggered in order to create the FieldIndex'es ?
(I know how to trigger an update for the Lucene index, but as one would expect it does not affect querying fields using the Projections module and I'm really lost at this point.)
By now I'm just stabbing blindly in the dark.
Would really appreciate any help I can get, even if it's just something pointing me back in the right direction. Thank you.

You should change
_orchardServices.ContentManager.Create(myItem);
to
_orchardServices.ContentManager.Create(myItem, Orchard.ContentManagement.VersionOptions.Draft);
For understanding look at CreatePOST method of Orchard.Core.Contents.Controllers.AdminController class and Publish method of Orchard.ContentManagement.DefaultContentManager class
In your case when you call a Create(myItem) then created published content item and all handlers are invoked normally (but has not yet set up a desired data). When you call Publish(myItem) nothing happens (no handlers are invoked) because your content is already published.

I've raised this as a bug, vote for it if you think it needs fixed.
#Alexander Petryakov is correct in his description of what is happening and his work around is probably the correct approach, however the behaviour doesn't make sense, which is why I have raised the bug. The code in your question manages to create an inconsistency between the content view of the data, stored in the Orchard_Framework_ContentItemVersionRecord table and the Projections view of the data stored in the Orchard_Projections_StringFieldIndexRecord table. Essentially, the Orchard_Projections_StringFieldIndexRecord contains null because it hasn't processed the publish event after you updated the field.
The code you have essentially does the following things:
Create a content item + publish it's creation
Update one of the content items fields this update doesn't change the state of the content
Try to publish the content item which doesn't do anything because it thinks it is already published.
To me, if you update a field on the content item, then the state of the item you are working on should no longer be published (it's changed since you published it). The Fields provide hooks that allow you to be notified when they are updated, so an alternate way of solving the problem would be to create a class that implements the interface IFieldStorageEvents that updates the published state of the content when a field is updated.
public class FieldUpdateEventHandler : IFieldStorageEvents {
public void SetCalled(FieldStorageEventContext context) {
context.Content.ContentItem.VersionRecord.Published = false;
}
}
This would allow your original code to run as it was written.

Related

Is there a way to bind properties together?

I've recently needed to synchronize values from a higher up view model (bound to a custom tab content control) to the base one (bound to the host view) for the CanExecute delegate in the host view to use. The base model has the instance of the top one, among others.
The only thing that I can think of for it to know when a value changes higher up is to subscribe to the PropertyChanged event. But, that seems excessive considering how many times that event would get fired for all of the other properties. It also doesn't feel right for MVVM (but I may be wrong).
Right now, I'm setting it all in a central method in the manager class where the magic happens to make sure the values match up:
If setDealable Then multilegViewModel.IsDealable = isDealable
multilegViewModel.IsIndicative = (Not isDealable)
' [...]
tktViewModel.IsCommandOtherEnabled = (Not isDealable)
tktViewModel.IsCommandBuyEnabled = multilegViewModel.IsBuyButtonEnabled
tktViewModel.IsCommandSellEnabled = multilegViewModel.IsSellButtonEnabled
tktViewModel.IsDealable = isDealable
tktViewModel.IsIndicative = (Not isDealable)
' [...]
But, smaller sets of the "multilegViewModel" properties are being set elsewhere, so I have to find them all and add the copy over. The risk is having another developer leave out such a pairing somewhere.
So, does anyone have ideas other than one view model subscribing to the other to ensure that the values always get set?
Note: The dual language tags are on purpose. It's a mixed language solution, such as the manager being VB.NET, but the models are C#, so I accept suggestions in either one.
UPDATE: I've changed my approach and greatly simplified it, so my initial reason for needing this is no longer valid. But, I may have an edge case or two that could still benefit from this.

Link Xamarin properties from different ViewModels

Is it possible to just "link" two properties of different ViewModels in a way such that when one property changes, the other one changes too. So in essence, I want two properties in different ViewModels behave as if they were one.
It would be nice if I could just do something like the following in my ViewModels:
WhenPropertyChanges(() => SettingX).CopyValueTo(() => ModelView2.SettingX);
Example: On my settings page, when I change a setting, I want the new value to be available in the ViewModel of another page.
I know I can achieve that with the third Layer (Model), but it feels a bit clunky and the way I solved it for now doesn't feel right (Firing events).
I've implemented following MVVM-Pattern from this page: http://www.wintellect.com/devcenter/krome/linking-property-change-notifications-in-xamarin-forms-or-wpfsilverlight. As far as I understood, the author says his code makes it possible, but unfortunately he doesn't show how.
I also found this article (http://blog.alectucker.com/post/2014/07/26/using-messageingcenter-in-xamarin-forms-for-viewmodel-to-viewmodel-navigation.aspx), but I don't necessarily want to navigate to the other View. Still, maybe I can use this MessagingCenter somehow to achieve my goal?
This is addressed in the final paragraph of my article that you reference. I use a simple extension method to do it. An example of this can be seen in the source code example from the article, in this file: https://github.com/Wintellect/XamarinSamples/blob/master/PropertyDependencyDemo%2FPropertyDependencyDemo%2FMvvm%2FObservableExtensions.cs
For your specific example, it would look something like this:
// using PropertyDependencyDemo.Mvvm;
// ... use the namespace above that contains the ObservableExtensions class
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => ModelView2.SettingX = ModelView1.SettingX);
All this does is hook into the PropertyChanged event of the source viewmodel for you in a name-safe way.
One word of caution though: you need to be careful to not create a situation where you inadvertently prevent an object from being garbage collected. The reason this can happen is that in this example, ViewModel1 will now have a PropertyChanged handler that references a PropertyDependency object that in turn references both ModelView1 and ModelView2 due to the captured references in the Action lambda expression.
IF you know for sure that this won't be a problem (perhaps both go out of scope together), then there is nothing to worry about. But if you find yourself facing a situation where you need to prevent ModelView1 from keeping ModelView2 pinned, then you can do so using a WeakReference. Again, this is unlikely to be a concern, but if you find it leaking memory then you can change the above to this:
// assuming "TModelView" is the class name of the viewmodels
var wr = new WeakReference<TModelView>(ModelView2);
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => {
TModelView mv;
if (wr.TryGetTarget(out mv))
mv.SettingX = ModelView1.SettingX;
});
I think if the navigation is lineare ( you only need the information of page 1 in page 2 ) you can use the MessagingCenter to do what you want.
If the user is able to change the data in the page 2 ( and be updated in the page 1 ) the messagingCenter is a little to tricky to use. I think you can use the first link you provide. Just use an abstract class with all shared data in your ViewModels.
If you want more help put an more completed code example.

Storing viewmodel data in Session creates problems with validation using FluentValidation

I'm currently working on a large project involving Sitecore CMS (7.2). For viewmodel validation we are using FluentValidations. Because of the combination of Sitecore and FluentValidations I seem to be running in some kind of technical deadlock. I sort-of found a solution myself, but I'm not sure whether this is the right approach or not. Here's the problem:
Situation
There is a Sitecore component which contains a HTML form. Via the modelbinder each value of this form is binded to it's corresponding field in the (complex) viewmodel. This is standard .NET MVC approach.
However, some values in the viewmodel are NOT part of the form. For instance, a date at which the mutation will be applied is calculated by the application. The user can only see this date-value as plain text, and thus can not edit it. It's still part of the viewmodel though. To make sure this value is being posted back to the model in code, one would normally use a hidden field. But if I use a hidden field, it means that users are able to spoof that date and because some validations depend on this value, they are able to spoof the entire validity of the form.
Moreover, in the same viewmodel I have a list of complex objects that I can't simply put in a hidden field (or I should serialize it to JSON, which I don't want).
The conclusion is that I need to store this data somewhere else. Somewhere the user can't spoof it, but I'm still able to validate user input with FluentValidations. I therefore decided to put the entire viewmodel in the users Session, and delete it directly after a succesful mutation.
Problem
By using session data I run into problems. Let's first see these steps:
(GET) Viewmodel is created. Calculated date is set and list of complex types is retrieved once from a (slow) webservice.
Entire viewmodel is stored as session data.
Form is shown to the user, who fills the form. Some data is only shown as readonly, like the date and list of complex types.
User submits form, FluentValidations kicks in to validate the data (POST).
That's where I run into problems. The validation done by FluentValidations kicks in before it reaches the POST controller method. That's exactly the way we want it, because that means validation errors are automatically added to the ModelState. However, because of security reasons I don't want to add this data as hidden fields to the cshtml file, which means they are empty at the time FluentValidations is going to validate the form.
This is creating problems because some of the form validations rely on the missing data. What I basically want is to merge the viewmodel that is stored in the session with the viewmodel that was posted to the controller method. But I have to do that before FluentValidations is going to do it's work.
My current solution
Gladly, I learned about FluentValidation's IValidatorInterceptor: an interface that can be used to 'do stuff' before or after the validations process kicks in. I used the BeforeMvcValidation method to do my merging process. The code is as follows:
public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
{
if (controllerContext.HttpContext.Session == null)
return validationContext;
var sessionData = controllerContext.HttpContext.Session["some_identifier"];
if (sessionData == null)
return validationContext;
var mergedObjectToValidate = Utils.MergeViewModelData(sessionData, validationContext.InstanceToValidate);
// Unfortunately, we have to do this..
var privateSetterProperty = validationContext.GetType().GetProperty(ValidationContextInstancePropertyName);
if (privateSetterProperty == null)
return validationContext;
privateSetterProperty.SetValue(validationContext, mergedObjectToValidate);
return validationContext;
}
Basically this interceptor method allows me to do my merging-process before validation. So I thought I had the solution here, but as you can see I am using reflection to set a property. That is because the property InstanceToValidate in the ValidationContext object has a private setter. I simply can not set it without using reflection. Which is, obviously, a bit dirty.
It does work exactly as I want though! :) I do not need any hidden fields that can be spoofed (which is horrible for straight-trough-processing) and I can still use FluentValidations exactly as I always did before. Also, the MVC modelbinding-process is left untouched, which I prefer.
The actual question
So the above solution works exactly as you want so what are your questions?! Well, simple:
I'm using reflection to set a private property in a 3rd party library (FluentValidations). The obvious answer is: don't go that way. But in this case it works flawlessly. If the InstanceToValidate-property had a public setter, I wouldn't even be posting this question at all: I would feel like I nailed it. But unfortunately it is private, so are there any real reasons why I shouldn't do this, maybe someone being an expert in FluentValidations behaviour?
Let's say there is a genuine reason why I shouldn't go this way; is there another approach which has the same effect? Can I hook in even earlier, so before FluentValidations kicks in, perhaps some kind of 'hook' just after the MVC model-binding process but before validation kicks in?
Is this entire approach simply wrong and should I tackle it in a completely different way?
Thanks!

LightSwitch - "Reference properties cannot be set to deleted or discarded entities."

I am experiencing this exception when trying to define a data member contained within another piece of data.
Example:
Container newRecord = this.DataWorkspace.ApplicationData.Containers.AddNew();
newRecord.SubContainer = this.DataWorkspace.ApplicationData.SubContainers.AddNew();
The exception, "Reference properties cannot be set to deleted or discarded entities.", is encountered with the second line.
I don't understand what entity it's talking about with regard to it being discarded or deleted, so any help with this issue would be most appreciated.
The code lines are in an interface function defined in LightSwitch, which is called from a Silverlight project, passing data from that project to the LightSwitch project.
I eventually managed to do this after working out that I needed to be on the 'Logic' thread, which I was not. I spent a little while messing around trying to find a this.DataContext but could not (my Silverlight project had this but not the LightSwitch project).
Eventually though I found out what I needed to do:
this.Details.Dispatcher.BeginInvoke(() =>
{
Container newRecord = this.DataWorkspace.ApplicationData.Containers.AddNew();
newRecord.SubContainer = this.DataWorkspace.ApplicationData.SubContainers.AddNew();
newRecord.exampleIntProperty=2;
newRecord.SubContainer.innerString="Example";
});
I can then assign data to the properties of newRecord and the properties of the objects it contains (such as the example SubContainer's properties), although obviously the new record is not saved until LightSwitch is instructed to save its data.
Your code needs to be changed slightly:
Container newRecord = this.DataWorkspace.ApplicationData.Containers.AddNew();
SubContainer newSub = newRecord.SubContainers.AddNew();
If the navigation property isn't called SubContainers, just replace that with the correct name.

Many-to-Many Databinding in LINQ

I've looked all over and haven't been able to find a clear answer to a seemingly common question: How can I do two-way databinding over a many-to-many relationship in ASP.net?
I have the following database structure:
I am currently writing a page for editing or adding a User record. Databinding things such as name and password is simple, but what I really need it to be able to display a list of all PhoneGroups and choose one or more from the list. How do I do this?
I tried a CheckBoxList, but while I can display the list of PhoneGroups, How do I bind the Checked state of each box based on whether the user has access? Some solutions use a loop in the OnDataBound event of the CheckBoxList. If I do this, how do I update the database when the checked state changes? I could go the brute force approach and write code to do this, but isn't there something that can make this simpler? It seems like such a common scenario.
Update #1
I am currently using Devart's LinqConnect, but I am open to change. The backend database is MySQL.
Yeah it is a common scenario and binding to that event is the solution i see used.
It is fairly simple when you consdier what the code is doing int he background. You could write your own custom server control, but thats a lot more difficult.
MVC may offer you an alternative ...
really why not redesign and only return the objects that they ahve permission for?
as for updating items in the database you need to say more about the architecture. But ultimatley to update an item you have to take the new item ... you have to do womthing like this
public void StoreTheUpdatedData(YourBusinessObject theBusinessObject)
{
var yourDataContext = new DataContext()
var oldObject = (from i in yourDataContext.YourbusinessObjects
where (blah equals blah to select your item and only your item)
select i).First();
//repeat for all properties in the object
oldObject.Property = theBusinessObject.Property;
yourDataContext.SaveChanges();
}
code liek that is what you need to do the update.
the save method varies depending on which ORM you are using ... I think linq2SSql uses commitChanges for instance. Been a while since i used that one.

Categories