Allow user view unpublished pages in edit mode without modify rights

Vote:
 

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.

 

#46681
Dec 24, 2010 11:46
Vote:
 

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

#46692
Dec 27, 2010 20:04
Vote:
 

Thanks Andres. Just wondering PageAdaptors also for cms 5 R2? 

#46725
Dec 31, 2010 13:01
Vote:
 

yes, that is a .net thing and not a episerver thing :)

#46726
Dec 31, 2010 15:09
Vote:
 

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 :)

#46770
Jan 04, 2011 11:19
Vote:
 

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!

#47158
Jan 17, 2011 16:58
Vote:
 

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

#47159
Jan 17, 2011 17:05
Vote:
 

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!

<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="Planet3.EpiServer.Library.Web.Adapters.PageExplorerAdapter" />
    </controlAdapters>
  </browser>
</browsers>

#47176
Edited, Jan 18, 2011 13:19
Vote:
 

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

#47177
Edited, Jan 18, 2011 13:22
Vote:
 

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.

#47180
Edited, Jan 18, 2011 13:36
Vote:
 

strange. But can as you pointet out see it in the code section

The code shows how powerfull page adaptors can be..

#47183
Edited, Jan 18, 2011 13:48
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.