Is there a way in episerver to find a block hierarchy.
The structure that I have is
Carousal Block includes ContentArea property for Carousal Items
Carousal Item1
string
iselection factory
Carousal Item2
string
iselection factory
In Carousal Item 1,I have a selection factory .I need to fetch Carousal Block properties within this selection factory.
Any help is appreciated.
Thanks in advance.
Short answer, no, there isn't a way since blocks doesn't inherit structure that way. They can be used anywhere. Thus if you ask for the parent of a block it will always return the SysContentAssetFolder.
To solve your problem (I'm certain there are other ways as well) I'd go for a usage approach. E.g. where is the block used and find the owner, block instances will refer to parents as owners since it is an instance that can have multiple parents/owners.
Now that we know that here's some code
public class ImageBlockController : BlockController<ImageBlock>
{
private readonly IContentRepository contentRepository;
private readonly IContentTypeRepository contentTypeRepository;
public ImageBlockController(IContentRepository contentRepository, IContentTypeRepository contentTypeRepository)
{
this.contentRepository = contentRepository;
this.contentTypeRepository = contentTypeRepository;
}
public override ActionResult Index(ImageBlock currentBlock)
{
var blockContent = (IContent)currentBlock;
// Get block references
var usageReferences = contentRepository.GetReferencesToContent(blockContent.ContentLink, false);
if(usageReferences.Count() == 1)
{
// easy peasy, only one usage
var owner = contentRepository.Get<IContent>(usageReferences.First().OwnerID);
// example
var ownerType = contentTypeRepository.Load(owner.ContentTypeID);
if (ownerType.ModelType == typeof(ImageBlock))
{
// do stuff
}
}
else
{
// handle if the block is used at multiple locations
// ...
}
return PartialView(currentBlock);
}
}
The trick in this example is to get all references contentRepository.GetReferencesToContent to the loaded block, if we are satisfied with the result do something and move on. In the case you have multiple usageReferences you may have to search the loaded page for the correct owner block. In that case load the IPageRouterHelper, fetch the current page using something like var parentPage = contentRepository.Get<IContent>(pageRouteHelper.Page.ContentLink); and match the usageReferences to the items in the contentarea.
Related
Is it possible to programmatically determine if the content of a block uses personalization?
I need to know in order to handle caching correctly when the content of a block can be personalized for different visitor groups.
In order to make decision about personalization you will need to get access to ContentAreaItem instance itself.
The most easiest way to get access to ContentAreaItem is to hook into rendering pipeline. You may need to swap out ContentAreaRenderer (see AlloyTech sample site for code snippet) with your own implementation.
Then you can implement BeforeRenderContentAreaItemStartTag:
public class MyCustomContentAreaRenderer : ContentAreaRenderer
{
protected override void BeforeRenderContentAreaItemStartTag(TagBuilder tagBuilder, ContentAreaItem contentAreaItem)
{
var isPersonalizationApplied = contentAreaItem.AllowedRoles.Any();
}
}
Next question of course is how you will deliver this decision down to block type instance, as overridden ContentAreaRenderer method is far enough from the block instance inside content area.
#keithl8041, you can use VisitorGroupHelper to check whether current user matches particular role:
using EPiServer.Personalization.VisitorGroups;
using EPiServer.Security;
protected override void BeforeRenderContentAreaItemStartTag(TagBuilder tagBuilder, ContentAreaItem contentAreaItem)
{
var groupHelper = new VisitorGroupHelper();
var isPersonalizationApplied = contentAreaItem.AllowedRoles.Any();
var isCurrentUserInAnyOfGroups = contentAreaItem.AllowedRoles.Any(r => groupHelper.IsPrincipalInGroup(PrincipalInfo.CurrentPrincipal, r);
}
In page edit mode I want to show a read-only text that is based on a page property value. The text could for example be "A content review reminder email will be sent 2015-10-10", where the date is based on the page published date + six months (a value that will be configurable and therefore can change anytime). So far I've tried to accomplish something like this by adding another property on the page.
I've added the property CurrentReviewReminderDate to an InformationPage class we use. In page edit mode the property name is shown, but it doesn't have a value. How do I do to show the value in page edit mode (preferably as a label)?
[CultureSpecific]
[Display(
Name = "Review reminder date",
Description = "On this date a reminder will be sent to the selected mail to remember to verify page content",
Order = 110)]
[Editable(false)]
public virtual string CurrentReviewReminderDate
{
get
{
var daysUntilFirstLevelReminder =
int.Parse(WebConfigurationManager.AppSettings["PageReviewReminder_DaysUntilFirstLevelReminder"]);
if (CheckPublishedStatus(PagePublishedStatus.Published))
{
return StartPublish.AddDays(daysUntilFirstLevelReminder).ToString();
}
return "";
}
set
{
this.SetPropertyValue(p => p.CurrentReviewReminderDate, value);
}
}
EPiServer internally uses the GetPropertyValue method (i.e. the opposite of SetPropertyValue) when retrieving content for the UI.
This makes sense, otherwise your "made-up" value would be stored as the real value whenever the content is saved. This would make fall-back values etc impossible to implement.
So, this is by-design (and quite wisely so) in EPiServer. :)
However, you can customize how properties work by:
Using custom editors by applying UI hints
Modifying property metadata (for example, to display a generated value as a watermark in a textbox without interfering with the actual value being saved)
I could be misunderstanding what you're trying to do, but off the top of my head it looks like a custom editor could be a viable option for your use case?
Another solution would be to hook into the LoadedPage-event and add the value from there. This might not be the best way performance-wise since you need to do a CreateWritableClone, but depending on the site it might not matter.
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class EventInitialization : IInitializableModule
{
public void Initialize(InitializationEngine context)
{
ServiceLocator.Current.GetInstance<IContentEvents>().LoadedContent += eventRegistry_LoadedContent;
}
void eventRegistry_LoadedContent(object sender, ContentEventArgs e)
{
var p = e.Content as EventPage;
if (p != null)
{
p = p.CreateWritableClone() as EventPage;
p.EventDate = p.StartPublish.AddDays(10);
e.Content = p;
}
}
}
I have a View / ViewModel where a ProductList is loaded. This list is not visible on the screen.
What I need to do is show a new View/ViewModel (e.g. SelectProductView / SelectProductViewModel), pass the ProductList to them, and after a user selects a particular Product, close this view, and make use of selected product.
What is the best way to achieve this?
I am using MVVMLight, but I guess the ideas should not be restricted just to it.
The easiest way is to create a view, and pass collection to it, but that doesn't sound MVVM friendly. I was thinking of creating a SelectProductViewModel from the first ViewModel and pass the collection to it, but I don't know how would I automatically create SelectProductView and bind it to created SelectProductViewModel.
Edit: in my application view structure is a bit complex. I have a main view, which basically needs to host a SelectProductView, since this view must cover whole screen. MainView contains lots of child and grandchild views (through tabs), so there could be 3 different child views or grand childViews that could issue a request for a product to be selected. Also, some view will not have products preloaded, so this task should probably be propagated to a SelectProductViewModel.
Example of Structure:
MainView
/ \
ChildViewA ChildViewB
/ \ / \
GrandChildViewA1 GrandChildViewA2 GrandChildViewB1 GrandChildViewB2
So, GrandChildViewA1, ChildViewB and GrandChildViewB2 could issue a request for a product to be selected. Only the view that issued a request should get the selected product, others should not bother with it. GrandChildViewA1 will have loaded products in it, but GrandChildViewB2 will not have ProductList loaded in it. This means, for performance sake, that GrandChildViewA1 should pass product list to SelectProductViewModel, while GrandCHildViewB2 will not have Product list in it, so SelectProductViewModel should fetch data from database.
I would create a generic viewModel which defines a contract for receiving data.
public abstract class PassDataViewModel<T> : ObservableObject
{
public T Data { get; }
}
I would then create a more general ViewModel for your product list like so:
public class SelectProductViewModel : PassDataViewModel<Product>
{
private Product _selectedProduct;
private ObservableCollection<Product> _products = new ObservableCollection<Product>();
public SelectProductViewModel(IList<Product> products)
{
_selectedProduct = _products.First();
}
public IEnumerable<Product> Products
{
get { return _products; }
}
public Product SelectedProduct
{
get { return _selectedProduct; }
set
{
_selectedProduct = value;
OnPropertyChanged("SelectedProduct");
OnPropertyChanged("Data");
}
}
public Product Data
{
get { return _selectedProduct; }
}
}
You would use this in the following way:
Your first viewModel can create an instance of the SelectProductViewModel (when a command is invoked, for example)
You pass your products list to the new SelectProductViewModel instance.
Use a DataTemplate to change the view on your screen (this post will show you how to do this).
Have a property in the parent viewModel that returns the product returned from the data property of the SelectProductViewModel (you will need to propagate the PropertyChanged event to your parent viewModel).
the most easy way is to go the viewmodel first approach and use a dialogservice to show the selection view.
your viewmodel with ProductionList simply call the dialogservice and put a ProductSelectionViewmodel with ProductionList as parameter. because this is viewmodel first you have to create a datatemplate so WPF knows how to render your ProductSelectionViewmodel.
here is a link for a simple dialogservice.
btw: in my opinion viewmodel first approach is much easier when doing mvvm.
EDIT:
in your ProductionListViewModel in your SelectProductCommand
var selectProductViewModel = new SelectProductViewModel(this.ProductionList);
var result = this.uiDialogService.ShowDialog("Select Product", selectProductViewModel );
//if result true, simple get the selected product
this.SelectedProduct = selectProductViewModel.MySelectedProduct;
thats all - simple and easy
I am not able to call the navigation service from the user control.
even when I create one event handler on the main page to call the navigation service that is laos not working.
Can you please help me?
I think I see the problem, but like Austin indicated, there isn't much to go on in your initial description. It sounds like you are trying to access the NavgationService (which is a PhoneApplicationPage property) from within a UserControl that you are placing on that page.
As with many things in these APIs, you have a couple of options. First, you can access the PhoneApplicationFrame (which contains your pages and manages navigation) and use it for navigation:
var frame = App.Current.RootVisual as PhoneApplicationFrame;
frame.Navigate(new Uri("/TargetPage.xaml", UriKind.Relative));
Alternatively, you can walk the control's Visual Tree using the VisualTreeHelper until you get to the containing page:
var page = GetParentOfType<PhoneApplicationPage>(this); // this is your user control
private static T GetParentOfType<T>(DependencyObject item) where T : DependencyObject
{
if (item == null) throw new ArgumentNullException("item");
T result;
var parent = VisualTreeHelper.GetParent(item);
if (parent == null) return null;
else if (parent.GetType().IsSubclassOf(typeof(T))
{
result = (T)parent;
}
else result = GetParameterOfType<T>(parent);
return result;
}
As you see, the VisualTree approach involves more code, but gets you the containing page object, where you have more access to things like NavigationContext, etc.
Hope that was your question (and your answer.)
Some background on what I'm doing
I usually like to have my pages return the url needed to access it. So i will normally have a method like so
public partial class ProductDetails : Page
{
public static string GetUrl(Guid productId)
{
return "fully qualified url";
}
}
on my other pages/controls that need to access this page i'll simply set the link as
hl.NavigateUrl = ProductDetails.GetUrl(id);
I'm toying around with the new UrlRouting stuff in 4.0 and ran into something I'm not sure will work. I'm trying to use the Page.GetRouteUrl in my static method, and obviously it's blowing up due to Page not being static.
Does anyone know if it's possible replicate what i'm doing with GetRouteUrl?
thx
You can do something like:
var url = ((Page)HttpContext.Current.Handler).GetRouteUrl(id);
Note: If you called this method from another page, you may not get the desired result if it's relative-specific in some way...but it's as good as you can get with static I believe.
I got GetRouteUrl to work using Nicks suggestion above.
I also found an alternative way to do it w/o using the GetRouteUrl. You are basically generating it manually using GetVirtualPath
public static string GetUrl(int productId)
{
var parameters = new RouteValueDictionary { { "productId", productId } };
var vpd = RouteTable.Routes.GetVirtualPath(null, "product-details", parameters);
return vpd.VirtualPath;
}