I need to bind the LinkURL of the Blog Page with a link button on the Start Page. What I did was actually found that Page ID and get a Page Reference using it.
PageReference BlogPageReference = new PageReference(21);
PageData BlogPage = GetPage(BlogPageReference);
var url = BlogPage.LinkURL;
This is pretty straight forward, but I'm not happy that the Page ID is hard coded.
Is there a better way of doing this, like getting the Page by Page name? or any other way?
Thanks in advance :)
I would create a property on the start page of type "Page", which means the property will have the type PageReference. Then it's no longer hardcoded.
It's also common to move such "settings" properties to a separate Settings page type which is itself linked via a property from the root or startpage (which are constants).
Im writing from memory so excuse any mistakes in the code.
var startPage = DataFactory.Instance.Get<StartPage>(PageReference.StartPage);
var settingsPage = DataFactory.Instance.Get<SettingsPage>(startPage.SettingsPage);
var blogPageRef = settingsPage.BlogPage;
Where SettingsPage and BlogPage are defined
public virtual PageReference xxxPage {get; set; }
in your page type class.
Related
I'm using EPiServer version 11 and I have requirement that when property of type linkItemCollection is rendered using PropertyFor() method, I need to add some custom attribute ( based on condition if target is blank ) to generated hyperlink.
#Html.PropertyFor(x => x.Layout.LinksCollection)
I have idea of creating a custom view under DisplayTemplates in view and adding new view. My query is how can i get default template for linkItemCollection to get it started ?
The easy option would be to o it yourself and not worry about the Property for, the only slight issue is that you may not get inline editing to work.
https://www.jondjones.com/learn-episerver-cms/episerver-developers-tutorials/episerver-properties/how-to-display-a-list-of-links-in-episerver/
To go with your route
[UIHint("MyView")]
[Display(
GroupName = SystemTabNames.Settings,
Order = 100)]
public virtual LinkItemCollection MyProperty{ get; set; }
In Views/Shared/DisplayTemplates add a template MyView.cshtml
Instead of using the PropertyFor you could take full control of the rending yourself.
// FullRefreshPropertiesMetaData asks on-page edit to reload the page
// to run the following custom rendering again after the value has changed.
#Html.FullRefreshPropertiesMetaData(new []{ "RelatedContentLinks" })
// EditAttributes enables on page-edit when you have custom rendering.
<p #Html.EditAttributes(m => m.CurrentPage.RelatedContentLinks) >
#if (Model.CurrentPage.RelatedContentLinks != null)
{
<span>See also:</span>
foreach (LinkItem item in Model.CurrentPage.RelatedContentLinks)
{
#item.Text }
}
</p>
Taken from the EPi documentation
Thanks for your input on this.
I managed to resolve this as below.
public static MvcHtmlString LinkItemCollectionFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
MvcHtmlString result = PropertyExtensions.PropertyFor(html, expression);
return MvcHtmlString.Create(result.ToString().Replace("target=\"_blank\"", "target=\"_blank\" rel=\"noopener noreferrer\""));
}
Hope, it helps someone.
Hi there I created a visual force page to with a standard lead controller as the controller and extend it. I want to do this so i can still utilize the apex:detail control and not reinvent the wheel in dealing with the standard lead info, related list etc.
I added an apex:commandbutton and make it call save. When I click on this button I can clearly see that my function is being called. However, all changes that is done to the lead info via inline editing is not captured.
For example, If I edited LastName using the inline editing and i click on the apex:commandbutton the new LastName value is not being saved. It's almost like the save function that is being called by apex:commandbutton is not aware of the data changes.
the following is code to my visual force page.
<apex:page standardController="Lead" extensions="LeadTestExtensionController">
<apex:form >
<apex:commandButton action="{!save}" value="Save" id="btnSave"/>
<apex:detail subject="{!Lead.Id}" relatedList="true" showchatter="true" inlineEdit="true" />
</apex:form>
</apex:page>
the following is code to my controller
public with sharing class LeadTestExtensionController {
private Apexpages.StandardController controller;
private PageReference page;
private string id;
private final Lead myLead;
public String positions {get; set;}
public LeadTestExtensionController(ApexPages.StandardController stdController) {
this.controller = stdController;
this.myLead = (Lead)stdController.getrecord();
this.page = ApexPages.currentPage();
this.id = page.getParameters().get('id');
List<Lead> myLeads = [select Opportunity_Stage__c from lead where id = :id];
if(myLeads.size() > 0)
{
positions = myLeads[0].Opportunity_Stage__c;
}
}
public PageReference save() {
this.controller.save();
PageReference newPage = New PageReference('/apex/RCS');
newPage.getParameters().put('id',ApexPages.currentPage().getParameters().get('id'));
newPage.setRedirect(true);
return newPage;
}
}
Once I click on the apex:command button, the page is being redirected to apex/RCS so i know its being called. However, if i return to the same lead, the last name doesn't change. I was under the impression that the following line would've called the standard controller's save function that should've taken care of the updating of the Last Name.
this.controller.save();
What am I doing wrong and how can I accomplish this. The above code is heavily simplified version of my actual code. What I am trying to do in my actual code is to check the value of certain field and if it meets certain criteria I want it to do something. However, I can't seems to see the new value entered.
Thank you.
Calling save() on the standard controller does not commit inline edits in a detail section. I've just reproduced the problem using only the standard controller, so the way you are overriding it is not the issue.
I think the reason for this is that apex:detail gets a record out of the database in its own right, rather than using a reference to the one in the standard controller.
Inline edit should provide you with its own save button, and I think your best bet is to try and incorporate that into your design.
We are using PageTypeBuilder to define our PageTypes, on one page we have a property which represents a Link item collection as below:
[PageTypeProperty(Type = typeof(PropertyLinkCollection), HelpText = "Test links.", EditCaption = "Test links", SortOrder = 11)]
public virtual LinkItemCollection PageLinks { get; set; }
We can populate this in CMS editor mode with links, save and publish without any errors. We then have a user control that inherits from EPiServer.UserControlBase and grabs the LinkItemCollection property using the below code and binds it to a repeater:
var links = currentPage.Property["PageLinks"].Value as LinkItemCollection;
if (links != null)
{
linkRepeater.DataSource = links;
linkRepeater.DataBind();
}
If I view the page when logged in as a CMS editor this page works fine and the links parameter is populated correctly, however if I view the page as a normal user and not logged in the links variable is always null (although when I'm debugging I can see the currentPage.Property["PageLinks"] is present, and the type is LinkCollection, its just that the Value is null
Is there something I need to configure here, permissions on a specific page type?
The problem is most likely one of the pages in the LinkItemCollection not being accessible by outside visitors. Try accessing the links in the collection as an outside visitor and remove any of the links that are in fact locked from outside view.
I have a page where you basically select a set of options (configuration), and then you go to a next page, where you do some stuff
Using the MVVM Light toolkit, I have a viewmodel that binds to the view of the first page. when the user hits a button, it redirects to another view, which would be the 2nd page
i.e.:
Page2Command = new DelegateCommand((obj) =>
Messenger.Default.Send<Uri>(new Uri("/DoStuffView.xaml", UriKind.Relative),
Common.CommonResources.GoToDoStuffRequest)) });
The problem is, the viewmodel for the 2nd view (the way that I see it) has a couple of parameters in the constructor, which are basically the dependencies on the configuration that was set on the first page.
i.e. :
public DoStuffViewModel(ICollection<Note> availableNotes, SoundMappers soundType)
{
}
The problem lies here.. How can I instantiate the viewmodel with this data that was dynamically selected by the user on the 1st page?.
I can't use the ViewModelLocator pattern that MVVM light provides, since those viewmodels don't have any dependencies, they are just by themselves (or they can retrieve data from a db, file or whatever, but they don't have any dynamic input data). I could do it through the view's constructor, instantiate there the viewmodel, and assign to the view's DataSource the newly created viewmodel, but I think that's not very nice to do.
suggestions?
As I see you send messsage using Messenger class so you are familiar with messaging in MVVM light. You have to define your own message type that should accept your parameters from page 1:
public class Page2ViewModelCreateMessage : MessageBase
{
public ICollection<Note> AvailableNotes{get;set;}
public SoundMappers SoundType{get;set;}
public Page2ViewModelCreateMessage ()
{
}
public Page2ViewModelCreateMessage(ICollection<Note> availableNotes, SoundMappers soundType)
{
this.AvailableNotes = availableNotes;
this.SoundType = soundType;
}
}
You have to send an Page2ViewModelCreateMessage instance with you parameters and send it on navigating:
var message = new Page2ViewModelCreateMessage(myAvailableNotes, mySoundType)
Messenger.Default.Send(message);
On Page2 you have to register for recieving message of type Page2ViewModelCreateMessage:
Messenger.Default.Register<Page2ViewModelCreateMessage>(this, OnPage2ViewModelCreateMessage);
..
public void OnPage2ViewModelCreateMessage(Page2ViewModelCreateMessage message)
{
var page2ViewModel = new Page2ViewModel(messsage.AvailableNotes, message.SoundType);
}
As you can see I have replace your DoStuffViewModel with Page2ViewModel to be more clear.
I hope this will help you.
NOTE:I dont guarantee that code will work as its written in notepad.
The way I do this is to have a central controller class that the ViewModels all know about, via an interface. I then set state into this before having the phone perform the navigation for me. Each ViewModel then interrogates this central class for the state it needs.
There are a number of benefits to this for me:
It allows me to have non-static ViewModels.
I can use Ninject to inject the concrete implementation of the controller class and have it scoped as a singleton.
Most importantly, when tombstoning, I only need to grab the current ViewModel and the controller class.
I ran into a problem with messaging where my ViewModel was the registered listener, because I was View First and not ViewModel First, I was forced to use static ViewModel references. Otherwise the ViewModel wasn't created in time to receive the message.
I use the controller class in conjunction with messages (it is basically the recipient of all messages around the UI) so in future if I refactor, I don't need to change much, just the recipients of the messages.
Come to think of it, the controller class is also my navigation sink - as I have some custom navigation code that skips back paging on certain pages etc.
Here's an example of my current set up:
public interface IController
{
Foo SelectedFoo { get; }
}
public class ViewModel
{
private IController _controller;
public ViewModel(IController controller)
{
_controller = controller;
}
private void LoadData()
{
// Using selected foo, we load the bars.
var bars = LoadBars(_controller.SelectedFoo);
}
}
You could use PhoneApplicationService dictionary to save data you need when navigation from first event, and parse it when you navigateTo second page. you can also use that data in your ViewModels.
Something like this:
PhoneApplicationService.Current.State["DatatFromFirstPage"] = data;
and when navigating to second page:
if (PhoneApplicationService.Current.State.ContainsKey("DatatFromFirstPage"))
{
var dataUsedOnSeconPage= PhoneApplicationService.Current.State["DatatFromFirstPage"];
}
you can use this data globally in entire app
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;
}