You cant add extra access rights. But you could by using PageAdaptors change the behaivor of the tree menu, and have it display none published pages
Got it. I found some articles and one of them is yours.
http://labs.episerver.com/en/Blogs/Anders-Hattestad/Dates/2009/5/Hiding-tabs-based-on-roles/
These are the basic idea on how to do it right? :)
Thanks a lot
btw sorry for misspelling your name on my post before :)
Hello again,
I've been using Reflector to examine PageExplorer, PageTreeView, PageDataSource and other classes related to what I want to do.
It seems that PageDataSource is the class that hides away the pages that I would like to see in the tree (which is a good thing in principle). However, the related methods are not overridable so I cannot just inherit from PageDataSource and change that functionality.
The same applies to the PageExplorer; the method CreateDataSource is private so I can't override it and create my own data source instead of the PageDataSource.
So, if I really want to implement this, I will have to re-implement (copy-pasting from Reflector) a lot of code without guaranteed results that it will work. I'm also a bit concerned about the javascript integration, EditTree.aspx defines a lot of javascript that I will need to be compatible with.
The PageAdaptor approach seems to be incorrect, because the data, if I'm not mistaken, are already filtered on the data source level. If I wanted to display less pages, maybe this would work because I could filter them out but now I need to display more data than what is contained in the data source by default.
Any comment on the above thoughts and any advice will be greatly appreciated and most welcome! It feels like I'm hacking into EPiServer in an inappropriate way and I don't want to continue before I get some feedback from the experts!
Thanks!
With pageAdaptors I think you only need to override PageExplorer's page load and change
if (this.treeView.DataSource == null)
{
this.treeView.DataSource = this.CreateDataSource(this.DisplayTreeMode);
}
if (this.Visible && !this.Page.IsCallback)
{
this.treeView.DataBind();
}
to point to your own DataSource method
Thanks, that really helped! I will post here the entire solution in case anyone else is interested and to discuss some more questions on that solution.
The solution consists of one adapter class, PageExplorerAdapter, one data source class, PageExplorerDataSource and one browser definition file to declare the adapter.
The browser file looks like:
<browsers>
<browser refID="Default">
<controlAdapters>
<adapter controlType="System.Web.UI.HtmlControls.HtmlHead" adapterType="EPiServer.UI.WebControls.ControlAdapters.HtmlHeadAdapter" />
<adapter controlType="System.Web.UI.HtmlControls.HtmlForm" adapterType="EPiServer.WebControls.ControlAdapters.HtmlFormAdapter" />
<adapter controlType="EPiServer.UI.Edit.PageExplorer" adapterType="MyApp.Web.Adapters.PageExplorerAdapter" />
</controlAdapters>
</browser>
</browsers>
The page adapter overrides the EPiServer PageExplorer OnLoad, as Anders said, and sets its own data source:
public class PageExplorerAdapter : PageAdapter
{
/// <summary>
/// Assigns a different data source to the tree view control contained in the PageExplorer control.
/// </summary>
/// <param name="e"></param>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
PageExplorer me = (PageExplorer)Control;
// only override for the standard Structure tree view, not the favorites tree.
if (me.DisplayTreeMode == PageExplorer.TreeMode.ExplorerTree)
{
// get the PageTreeView control
PageTreeView treeview = (PageTreeView)me.FindControl("treeView");
// override the data source
treeview.DataSource = new PageExplorerDataSource();
// Force databind
treeview.DataBind();
}
}
}
One point that I don't like is that this way I pay the penalty of EPiServer doing its own data binding first and then I do my own. I can try to copy paste the code of PageExplorer with Reflector but I don't like looking inside the black box.
Also, I had to always force a databind because in some cases (when switching to the Favorites tab and then back to the Structure tab) my data source would be 'forgotten' and the default will be used again.
The data source code looks like this:
class PageExplorerDataSource : Control, IDataSource, IHierarchicalDataSource, IHierarchicalPageDataSource, IPageSource
{
#region IDataSource Members
// Note that IDataSource needs to be implemented however it's not called in our use case
public event EventHandler DataSourceChanged;
public DataSourceView GetView(string viewName)
{
throw new NotImplementedException();
}
public System.Collections.ICollection GetViewNames()
{
throw new NotImplementedException();
}
#endregion
#region IHierarchicalDataSource Members
/// <summary>
/// Returns a hierarchical view for the given view path.
/// </summary>
/// <param name="viewPath">The view path for which the view is returned.</param>
/// <returns>A hierarchical view of pages.</returns>
public HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
return new PageHierarchicalView(this, viewPath);
}
#endregion
#region IHierarchicalPageDataSource Members
public EPiServer.Security.AccessLevel AccessLevel
{
get { return EPiServer.Security.AccessLevel.NoAccess; }
}
/// <summary>
/// Gets a value indicating whether tree nodes should be queried for the presence of child nodes.
/// </summary>
/// <remarks>
/// When this value is <c>false</c>, tree nodes will always have the plus icon regardless of whether they have children or not.
/// When this value is <c>true</c>, tree nodes will offer the plus icon only when they have in fact children nodes.
/// </remarks>
public bool EvaluateHasChildren
{
get { return true; }
}
/// <summary>
/// Gets a collection of pages that are children of the page designated by the given page reference.
/// </summary>
/// <param name="pageLink">A reference to the page whose children are to be returned</param>
/// <returns>A collection of pages</returns>
public PageDataCollection GetFilteredChildren(PageReference pageLink)
{
// The presence of the LanguageSelector is necessary to return sites and pages of all languages
return DataFactory.Instance.GetChildren(pageLink, LanguageSelector.AutoDetect(true));
}
/// <summary>
/// Returns an hierarchical enumerable of pages for the given view path.
/// </summary>
/// <param name="viewPath">The view path to use</param>
/// <returns>An hierarchical enumerable of pages</returns>
public PageHierarchicalEnumerable HierarchicalSelect(string viewPath)
{
PageReference start;
if (string.IsNullOrEmpty(viewPath))
{
// return a collection containing just the root page
start = PageReference.RootPage;
PageDataCollection pdc = new PageDataCollection();
PageData root = DataFactory.Instance.GetPage(start);
pdc.Add(root);
return new PageHierarchicalEnumerable(pdc, this);
}
else
{
// return a hierarchical enumerable that will fetch the children of the parsed view path
start = PageReference.Parse(viewPath);
return new PageHierarchicalEnumerable(start, this);
}
}
/// <summary>
/// Checks if the given page is root.
/// </summary>
/// <param name="page">The page to check</param>
/// <returns><c>true</c> if the given page represents a root page, <c>false</c> otherwise</returns>
public bool IsRoot(PageData page)
{
return PageReference.IsNullOrEmpty(page.ParentLink) || page.ParentLink == PageReference.RootPage;
}
#endregion
#region IPageSource Members
public PageData CurrentPage
{
get { throw new NotImplementedException(); }
}
public PageDataCollection GetChildren(PageReference pageLink)
{
throw new NotImplementedException();
}
/// <summary>
/// Gets the page that corresponds to the given page reference.
/// </summary>
/// <param name="pageLink">A page reference</param>
/// <returns>The page that corresponds to the given page reference</returns>
public PageData GetPage(PageReference pageLink)
{
return DataFactory.Instance.GetPage(pageLink);
}
#endregion
}
As you can see, several methods are not implemented but it turns out that they're not used in my particular case. So I only implemented enough for it to work. Luckily a lot of the plumbing code of EPiServer could be reused without troubles (such as PageHierarchicalEnumerable and PageHierarchicalView).
One thing I'm not sure about is the AccessLevel property. It seems that whatever I return from there, it doesn't make any difference.
Also, I'm using a LanguageSelector at GetFilteredChildren because by default content that didn't exist in the active language would be filtered out.
In the end, I think it's a solution that works pretty nice and it's very powerful because now the contents of the tree view are at my complete control (or so it seems).
Thanks a lot for the help!
If you could post the code on the code section it would be great. Know that I one day need it :), and to clip it from here is a bit hassel since we get line nr and all :)
And great solution btw
Done!
You can find it here:
http://world.episerver.com/Code/Cees-Gorter/PageExplorer-with-a-custom-data-source/
Btw I don't know why the code got messed up in a single line, I copied pasted the same way in the Code Section and there it looks fine.
strange. But can as you pointet out see it in the code section
The code shows how powerfull page adaptors can be..
Hi,
Got a question. I want to allow WebEditor with only read rights to view unpublished pages in edit interface without letting them to create or change or delete or any other modify rights to that page.
Is there a possibility to create extra Access Rights besides Read, Create, Change, Delete, Publish ETC? Can i modify the tree somehow to let other webeditor to see other webeditors unpublished pages in tree?
Thanks.