Sense/Net 6.0 Devblog
The development blog of Sense/Net 6.0
Back to Sense/Net

Download Sense/Net 6.0 Beta5

February 20, 2010 01:20 by Peter Zentai

Its here!

Release highlights

  • Content Lists (previously Listers) have been improved internally, and augmented with an intuitive user interface.
  • Workspaces are now ready for real-life deployment and everyday work.
  • We finally implemented indexing and search via Lucene.NET, boosting overall speed by several magnitudes.
  • A new permission management system has been added, boosting overall speed and providing full support for ACLs.
  • We have re-thinked the Content Type hierarchy, and in the process, removed several long-running bugs from the system.
  • We put much work into making Windows authentication function properly out-of-the-box.
  • We have integrated Microsoft ASP.NET MVC support into the product.
  • New, declarative UI hints have been added to CTDs, helping eschew cumbersome custom Content Views.
  • The newsletter module has been factored out of the main product, and will be released as an add-on in improved form in the near future.
  • Uncountable bugs have been fixed since the last release.

Efficient JSON communication in ASP.NET MVC

January 20, 2010 19:59 by Peter Zentai

A really short example on how ASP.NET MVC can be used for two-way JSON string communication.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Storage.Security;
using System.Web.Script.Serialization;
using System.Runtime.Serialization.Json;

namespace SenseNet.Services.ContentStore
{
public class JsonFilter : ActionFilterAttribute
{
public string Param { get; set; }
public Type DataType { get; set; }

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(DataType);
var data = ser.ReadObject(filterContext.HttpContext.Request.InputStream);
filterContext.ActionParameters[Param] = data;

}
}
}
public class SecurityController : Controller
{

public ActionResult GetACL(string path)
{
var node = Node.LoadNode(path);
var acl = node.Security.GetAcl();
return Json(acl);
}

[AcceptVerbs(HttpVerbs.Post)]
[JsonFilter(DataType = typeof(SnAccessControlList), Param = "acl")]
public ActionResult SetACL(SnAccessControlList acl)
{
var node = Node.LoadNode(acl.Path);
node.Security.SetAcl(acl);
return null;
}
}

}

Tags:

Categories: .Net
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

We are hiring!

January 20, 2010 13:46 by Tamás Bíró

Sense/Net is hiring developers, designers, GUI specialists and more to its Budapest office and to our new research center in Veszprém.

www.sensenet.com/jobs

Tags:

Categories: Announcement
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Custom view for the custom fields

January 4, 2010 14:24 by Miklós Tóth

Peter already wrote a few words about managing the custom fields of a Content List. The idea is that the collection of the custom fields can be presented as a list itself. This is possible because the topmost layer (e.g. a ListView) only needs a collection of Contents to work. The background of this - using Content fields with the standard Eval method and the way of presenting the custom fields as a list of Contents - will be a subject of another post (dynamic, in-memory content type creation, anybody?), right now I'm focusing on the presentation.

The solution is to create a custom view for the given ContentList. This view will show a list of the custom fields of the ContentList.

Creating a custom view

A custom view for a ContentList is a simple ASP.NET user control that contains the markup for the chosen data bound control. You can place this control (let's call it MyCustomView.ascx) to the Views folder of any ContentList, and you'll see it in the Views menu on the upper right corner. You can test the view by selecting it from the menu.

We placed two things into this user control:

  • ListGrid: this is a common ASP.NET ListView with a few shiny scripts added
  • SenseNetDataSource control

Using SenseNetDataSource with a ListGrid

Few weeks ago we introduced the SenseNetDataSource control, that makes building data-driven sites a lot easier. Like SQLDataSource, it gives all data bound controls access to pure data - in our case, contents in the Content Repository. One of its functions is to give access to a specific multireference member of a content (e.g.: a Product content may have a Suppliers member that refers to some Supplier contents).

This way we can get a collection of editable custom fields of a ContentList. In markup, it looks like:

<sn:SenseNetDataSource ID="ViewDatasource" MemberName="FieldSettingContents" runat="server"  />

The MemberName can refer to any reference field of a content. In this case we created a "virtual" field called FieldSettingContents for the ContentList class that creates the contents from the custom fields. This is a _very_ special use case, in most cases you will use a simple reference field defined in the content type definition.

We can present the results with any data bound control:

listgrid

Creating a new template for a built-in content list view

January 3, 2010 12:14 by Dániel József

One of the main features of the Xmas preview edition of Sense/Net 6.0 Beta 5 is the Content List View framework. A Content List View is basically an ASP.NET User Control that can be used to display Content items in various fashions. We have created the framework to allow for several levels of customization. Deployers and builders have the option to create entirely new User Controls either with a compiled classbehind dll placed in the binary folder of the Sense/Net installation, or using solely declarative components, such as the newly introduced SenseNetDataSouce. However, existing, built-in views can also be altered, and several variants of a single built-in view can be created in the system.

The template engine

The framework of built-in Content List Views is comprised by two levels - a template level and an instance level. The instance level is the actual User Controls that are loaded and executed by the Webforms engine to display the Content List. Every View configured by users on a single List is represented by an instance containing the configuration-, and metadata of the view, and also the actual User Control code that is auto-generated from said metadata. What facilitates this auto-generation is the template level of the infrastructure.

In creating this template engine, we considered several options to use as the templating language. In the end we have decided for XSLT, considering its wide penetration in the web industry, and also its relative simplicity and power. The code that does the actual work in creating the instance View from the template resides in the class Portal.UI.ContentListViews.Handlers.ViewBase. This is the Content Handler for the Content type ContentListView, the type of View instances. Upon the Save event of a ContentListView, the code contained in this class loads the document referred in the Template property as an XSLT template, and executes the transform on the XML document passed on by the virtual method GetSource(). By overriding this method in the Handlers of specific Content List View types - such as list views, calendar views, icon views, etc. -, the metadata necessary to generate the markup for the given view type can be passed to the template. In the ViewBase class, the data passed to the template is an empty XML document.

In the final product, the selection of this template file will be obscured from the user as an internal detail, but currently it needs to be selected by hand upon creating a View instance. The reason for this is flexibility for testing and prototyping, and also the fact that we were in a hurry, and had more important stuff to code. Wink For you, as a developer checking out the new interesting features of our product, this is most definitely a Good Thing.

Currently there is a single View type implemented in the system, the list view. The list view - not to be confused by the "Content List View" framework, which means the entire big thing - is the "one item per row" view all so know from user interfaces dating back more than twenty years, including those of Norton Commander, Windows Explorer and Microsoft SharePoint Server. The handler class for this View type is Portal.UI.ContentListViews.Handlers.ViewBase.ListView. The XML source used for the transform is the XML formatted list of columns to be displayed in the view.

Editing the template

Now, the User Controls used as the instances for List Views were designed to be user-modifiable. The ClassBehind implementing the logic of the View type contains references to well-known control IDs. Any User Control markup containing the needed controls of the right type with the right IDs will function in this system.

However, the markup of a View instance should - and in fact, can - only be modified through the XSLT template. Any changes to the instance markup itself would be overwritten during Save. To give you a taste of how this works, let's have a look at the built-in template for list views: /Root/System/SystemPlugins/ListView/Templates/ListView.xslt. (This path may change in later releases.)

This is the main template in the XSLT file, describing the outline of the markup. As you can see, we have decided to escape all ASP.NET markup in CDATA blocks. This was necessary to maintain readability and simple editing, as ASP.NET code is non-xml and contains lots of special characters, and so doesn't play nice with XSLT at all. Note how the ClassBehind SenseNet.Portal.UI.ContentListView.ListView is referred in the header, and also note the two known controls: the sn:ListGrid and the sn:SenseNetDataSource. (The ListGrid is a wrapper over the ASP.NET ListView webcontrol, adding JavaScript for visual and behavioral effects. The ClassBehind will accept standard asp:ListView controls without problems.)

To create a new look for your ListView, try creating a copy of this template as MyListview.xslt. At first, try changing the sn:ListGrid to an asp:ListView control without further modifications. Now, take one of the existing List View instances under the demo document library, and modify its Template to MyListView.xslt. Upon saving, the new template will be used to regenerate the markup. On viewing the document library with this View, you will see that the Javascript effects have disappeared.

Further things to play with:

  • Add additional HTML markup to the view.
  • Try to convert the list view template to use an Unordered List instead of a tabular layout.
  • Add an additional, declarative ASP.NET server control to the view.
  • Try databinding it to the SenseNetDataSource.

Sense/Net 6.0 Beta 5 preview is on codeplex.com

December 28, 2009 12:12 by Peter Zentai

The source code only version of the Sense/Net 6.0 Beta 5 has just made available on codeplex as an alpha release.

There are quite a number of things we need to polish before we can reach production quality. And we are hardly working hard on the evaluation materials too that describe the Beta 5 features in detail. So get this version to experiment only with the Sense/Net 6.0 Content Lists, the Templated Field Controls and the Lucene based Content Query.  Do not implement anything important with it just yet.

More...

Announcing Sense/Net 6.0 Beta 5 XmasAlpha1 :)

December 24, 2009 13:56 by Peter Zentai

Due to a number of reasons (some of them are the flu epidemic, the release scope changes and the global warming) we could not reach all of our goals for Sense/Net 6.0 Beta 5 till December 24.  We will release a source only preview – because we promised it and although the system is DANGEROUSLY UNSTABLE, the new features surely worth a look.

First and foremost the most important new features are:

Lists and Libraries

We now have a fully working Content List feature. Lists are collections of contents – much like folders – but with many powerful features in addition. Some of these are:

  • Users can define extra list fields – fields that are represented on all list items regardless of content type
  • The Content List View framework is a convenient way to present lists of items in many of the predefined ways like List View, Folder View or Calendar View.  You can easily create your own views in a wide set of ASP.NET technologies and XSLT.
  • Lists provide an easy to learn extensibility point in the system through the means of New Item Actions, List Actions (menus), Setting Actions and View Actions
  • Libraries are special lists where the list items are files. For example a List of Documents is a Document Library.

Here is how a Document Library is presented in our Demo Workspace. It may look quite familiar to SharePoint users and developers. Lists are presented with the Content List Portlet now in the SenseNet.CorePortlets namespace.

image

Settings –> Edit Properties navigate to the Edit Action associated with the list. Inputs are bit raw :) but hell, do you see the Matrix as code too?

image

From the list properties, most important is Enabled (Child) Content Types and Default view. Items enlisted in the allowed child types will populate the default New Item Action list:

image

Items in the List can have custom fields. Select Settings –> Manage Fields to get into the Manage Fields Action. I guess it looks pretty self.explanatory

image

(The collection of Fields looks and behaves much like a Content List – heyy it IS a content list in the background anyway :)) 

As everything else in this release, the editing experience of a custom field definition is implemented with the Sense/Net 6.0 Content View and Application Model technologies. This means that you can customize parts or all of the editing experience utilizing your existing investment in the conventional Sense/Net 6.0 portal development technology.

image

Custom fields then appear on the New Item form of the Lists

image 

Beyond managing, extending and filling in with items, lists also have to be presented somehow. This is the Content List View framework – the pendant of the Content View technology for individual content types.

image

Views can be built on many different ways:

  • markup only ASCX or
  • XSLT or
  • a custom subclass of our ListView or
  • a User Controls with code behind.

Select Settings –> Manage Views to see something already familiar: a List of Views assigned to the Document List.

image

Views can have properties that define their runtime behavior. Configuring a View is practically the same thing as editing a content’s property page. (Yes – before I forget to mention, from Sense/Net 6.0 Beta 5 POCOs can be Contents too, they don’t have to subclass the GenericContent class, we call it Virtual Content Type Framework.)

 

image

My Little Custom Fields just appeared.

image

 

Our default ConntentListViews are very simple and easy to customize, here is the code for a ListView with to columns

<%@ Control Language="C#" Inherits="SenseNet.Portal.UI.ContentListViews.ListView" %>
<sn:ListGrid ID="ViewBody" DataSourceID="ViewDatasource" runat="server" >
<LayoutTemplate>
<table class="viewBody">
<tr id="Tr1" runat="server">
<th>Name</th>
<th>Modified by</th>
</tr>
<tr runat="server" id="itemPlaceHolder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>
<sn:SmartMenu NodePath='<%# Eval("Path") %>' runat="server" >
<a href='<%# Eval("BrowseActionUrl")%>'><%# Eval("GenericContent_DisplayName") %></a>
</sn:SmartMenu>
</td>
<td><%# Eval("GenericContent_ModifiedBy") %><td>
</tr>
</ItemTemplate>
</sn:ListGrid>
<asp:Literal runat="server" id="ViewScript" />
<sn:SenseNetDataSource ID="ViewDatasource" runat="server" />

ContentQuery is now Lucene.Net based

This means that it is much faster plus apart from the previous XML Query, you can use the Lucene Query syntax as well.
image
 
More to come as this is just the half of it, next time we dig deep in the code and the means of extensiblity.

 

The eval package will be on codeplex.com in a day or so, so check back often if you are interested.

Searching a tree structure with Lucene.Net

December 16, 2009 03:00 by Peter Zentai

 

   1: /*
   2: Newsflash!
   3: 
   4: It’s now just a couple of days and we release Beta5. 
   5: In that the Content Query infrastructure is moved to the Lucene platform. 
   6: Sense/Net 6.0 endorses the eat your own dogfood philosophy, 
   7: so the Content Query feature is massively used across the system 
   8: in the core functionality. Even the repository type system 
   9: relies on the Content Query layer. 
  10: We expect a nice boost in the global system performance 
  11: in every content releated feature. Read on to find out why.
  12: */

Lucene is amazing – it is really everything you want from a software component: it’s smart, does it’s job well - asking very little in response, greatly simplifies your (professional) life :), open source (so you can feel more confident: you will have a higher success rate in mitigating project risks coming from relying on a 3rd party tool),  extensible and proven.

On our way to implement the Sense/Net Content Repository Lucene Adapter we had some experiences that worth sharing on using Lucene with deeply structured content and path based queries.

As Lucene follows a flat model (one and only one document type, arbitrary fields, no structures), the most trivial approach would be to flatten the tree by storing the position of each node as an extra stored/indexed field added to the node’s own fields– let call it the Path field.

The PrefixQuery

Now, finding nodes in a specific sub-tree looks really simple as we have PrefixQuery at hand:

private IEnumerable<DocumentWrapper> Search()
{
var pathquery = new PrefixQuery(new Term("Path", "c:\\Development"));
var result = Searcher.Search(pathquery);
foreach (Document o in result.Iterator)
{
yield return (new DocumentWrapper(o));
}
}

In your first couple of test scenarios it works nice. So you increase the size of your prototype to let’s say handle quarter a million of items now. Just to run into the the dreaded “TooManyClauses was unhandled” exception

image

It turns out the the PrefixQuery compiles into a list of possible TermQueries connected with OR relation, so basically the query Path = “C:\Development*” transforms into this: Path = “C:\Development\Folder1” OR Path = “C:\Development\Folder2” OR … and so on. The list is capped at 1023.

This 1023 limit looks really bad, but googling around the topic quickly brings up a solution that appears to be working.

The PrefixFilter solution!

The PrefixFilter let’s you efficiently filter rows from the query result based on a prefix criteria.

var query = new MatchAllDocsQuery();
var filter = new PrefixFilter(new Term("Path", "c:\\Development"));
var result = Searcher.Search(query, filter);

The query completes nicely, but with a touch of sluggishness. It has two major drawbacks however

- This approach splits the original query into two parts – a query and a filter, and it would complicate things to a level that would likely to be error prone in complex query cases (where multiple references of filters would be needed)

- Performance drops to mediocre/bad this way: in my test environment (Processor: Core2Duo, 2Ghz, Lucene index: 219000 rows, my complete c:'\ drive effectively) searching for c:\Development* returned 34.000 hits in 500 milliseconds. What is even worse: the execution time does not drop significantly as the result count get smaller. You may say: hey, 500 milliseconds is fantastic for such a mighty query!! yes. for the desktop it is… For the “Server Side”– it isn’t. In our case millions of queries will run a day, most of them around content pathes, and if each would cost us 500ms then we are – let’s face it - mucked.

So what else do we have?

The ConstantScoreQuery Solution?

The ConstantScoreQuery wraps a filter – a PrefixFilter in our case – and turns it into a Query class, ready to be combinef with other query parts. It removes the complexity of the split query model at least.

private IEnumerable<DocumentWrapper> Search()
{
var prefixFilter = new PrefixFilter(new Term("Path", "c:\\Development"));
var query = new ConstantScoreQuery(prefixFilter);
var result = Searcher.Search(query);
....
}

But

- The performance is even worse, 800 milliseconds now

- Plus now we need to give up ranking and scoring or at least we are threatened by constant scores instead of the default relevant scores.

And this is the point where we can be relatively sure that the problem lies in our approach and not in the implementation. Lets think in TERMS.

The Ancestors array property way

Instead of querying the Path field for a prefix match let’s extend our Lucene storage model. On the top of the path field let’s store each and every ancestor’s path of the node in the Ancestor array property.

 

private static Field[] CreateAncestorFields(string path, string separator)
{
string[] fragments = path.Split(new string[] { separator }, StringSplitOptions.None);
List<Field> fields = new List<Field>();
for(int i = 0; i < fragments.Length; i++)
{
string value = string.Join(separator, fragments, 0, i+1);
fields.Add(new Field("Ancestor", value, Field.Store.NO, Field.Index.NOT_ANALYZED_NO_NORMS));
}
return fields.ToArray();
}

so:

the document at c:\development\sensenet\luceneadapter\program.cs will have 4 Ancestor fields, these:

c:\
c:\development
c:\development\sensenet
c:\development\sensenet\luceneadapter

Lucene thinks in TERMS. Each of the above lines became a TERM of its own, and when we search for contents with a specific term as a value for one of the content’s fields, then we are back in Lucene’s domain. A simple TermQuery to the Ancestor field will produce the desired result: in no time.
private IEnumerable<DocumentWrapper> Search()
{
var query = new TermQuery(new Term("Ancestor", "c:\\Development"));
var result = Searcher.Search(query);
....
}

 
 Capture
  Times are milliseconds.

 

And what is the trade here? Index gets bigger of course. For ~219K items this meant about 30 extra MBs. Compared to the total index size which is 1.6GB this tradeoff is acceptable. The memory footprint is small however: searching the index gobbled up only an additional 4MB of memory.

Senset/Net 6.0 Content Repository RESTful API in ASP.NET MVC

December 10, 2009 20:06 by Peter Zentai

From Beta5 Sense/Net 6.0 will be packed together with the ASP.NET MVC assemblies as some of the core functionality is implemented using ASP.NET MVC.

As this point we use ASP.NET MVC in our revamped properietary Content Repository RESTful API. In this interpretation ASP.NET MVC provides us a superlightweight approach to expose RESTful functionality without the complexity and higher cost of ownership of Windows Communication Foundation.

ASP.NET MVC integration to the Sense/Net 6.0 Smart Application Model is in the plan. When finished you will be able to use ASP.NET MVC controllers as the other Sense/Net 6.0 context bindable application types (webform page, Ihttphandler) exploiting our taxonomically spiced declarative application composition concept.
For example:

/workspaces/mysales/documents?action=search can resolve to an MVC controller.action that gets the current context as an MVC model instance.

Tags:

Categories: Feature
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed

Beta5 and RTM release deliverables have changed

December 7, 2009 15:07 by Peter Zentai

We reevaluated our BETA5 and RTM plans in response to some really important customer feedback. According to this: performance and reliability of the ContentQuery feature in large sized implementations are more important then the workflow feature. We also learned from our customers that the WF3 penetration is still not a factor we should consider as legacy. Therefore we won’t target WF3 as our Workflow platform – we go directly with the WF4 version. This also has impact on our final product release date as it will be aligned with the .NET 4 official release date.

So here is the new planning:

Sense/Net 6.0 Beta5 - planned release date is XMAS

Most important features:
- User defined ContentList/Spreadsheet feature (this is the feature that is called Lists in Sharepoint)
- ContentListView framework (this is the extensible GUI aspect coming from the ContentList feature set)
- Moving out the indexing/searching/NodeQuery logic from SQL Server to Lucene.Net. I’ll post about it separately, since this is  a major (but hopefully not the Great) leap forward.

Sense/Net 6.0 RC – date is end of 2010 February. This release will be what we planned as RTM and will remain in candidate state until the .NET 4.0 framework is released. Although only a release candidate the production quality of the Sense/Net features not requiring .NET 4 will be release ready.

Most important features:
- Content Workflow support feature
- Rehosted Visual Workflow designer for end user workflow creator scenarios

Sense/Net 6.0 RTM – planned release date is the .NET 4 release date.

Tags:

Categories:
Actions: E-mail | Permalink | Comments (0) | Comment RSSRSS comment feed
Bookmark and Share