Rendering C1 functions in an external page - data missing - c1-cms

I have a sub-application (YetAnotherForum.NET) living in a child directory of my Composite C1 site. In order to maintain a consistent look and feel I want to pull in C1 functions for the navigation elements.
Note: All html mark-up in code below has had pointy brackets replaced with square brackets in order to allow posting here.
I've figured out I can call C1 functions using this syntax:
[f:function ID="Function1" name="Custom.Layout.FooterLinks" runat="server"/]
However, the data behind the function seems to be unavailable. Any ideas what the data issue might be? Perhaps I need the external page to inherit from some form of Composite C1 page?
Here's the function code:
#using Composite.Data;
#using Composite.Data.Types;
#using Composite.Data.ProcessControlled.ProcessControllers.GenericPublishProcessController;
#using CompositeC1Contrib.RazorFunctions;
#inherits CompositeC1WebPage
#functions {
private IEnumerable FooterLinkPages()
{
IEnumerable pages = DataFacade.GetData();
IEnumerable returnPages;
using (DataConnection connection = new DataConnection())
{
returnPages = (from l in connection.Get()
join p in pages on l.Page equals p.Id
where l.PublicationStatus == GenericPublishProcessController.Published
&& p.PublicationStatus == GenericPublishProcessController.Published
orderby l.Position ascending
select p).ToList();
}
return returnPages;
}
}
[ul class="unstyled"]
#foreach (IPage page in FooterLinkPages())
{
[li]#(String.IsNullOrWhiteSpace(page.MenuTitle) ? page.Title : page.MenuTitle)[/a][/li]
}
[/ul]

You need to wrap the data access code in:
using(Composite.Core.Threading.ThreadDataManager.EnsureInitialize())
{
using (DataScope localeScope = new DataScope(new System.Globalization.CultureInfo("en-NZ")))
{
...
}
}

Related

Composite C1: How to make the page tree to display UrlTitle instead of Title (Solution)

When Composite C1 displays the tree of sites and pages in the Content perspective, it uses the page Title property as the node label. This is not always comfortable, especially when the pages are translated to different cultures, so you can't even read those titles and quickly find the page you are looking for.
The idea is to use the UrlTitle as the label, so every tree node would represent a part of the page URL.
See the solution below.
I didn't want to recompile the assemblies, so my hack is ugly, but affects only javascript and aspx.
Edit /Composite/scripts/compressed/top.js.
Find there SystemTreeNodeBinding.prototype.onBindingAttach=function(){ and inject the following code at the beginning of this function:
if(window.treeNodeProcessor)window.treeNodeProcessor(this.node);
Now you can modify the tree node before it is displayed; you just need to create your global function treeNodeProcessor.
Edit /Composite/top.aspx (top.aspx.cs is affected as well, so save them together).
At the end of the head element add your javascript:
<script type="text/javascript">
function byKey(key) {
return function(p) {
return p.Key == key;
}
}
window.treeNodeTitles = {
<% WriteTreeNodeTitles(); %>
"": ""
};
window.treeNodeProcessor = function(node) {
if(node._data.PropertyBag) {
var uri = node._data.PropertyBag.find(byKey('Uri')).Value;
node._data.Label = window.treeNodeTitles[uri] || node._data.Label;
}
}
</script>
Unfortunately, there's no UrlTitle in the node object passed to treeNodeProcessor. However, every page node has PropertyBag that stores a value like ~/page(a9d30645-02f7-4412-bd4e-6f3a02782481) under key Uri. So, you have to query the UrlTitle on your own, what is made in method WriteTreeNodeTitles (see below).
Edit /Composite/top.aspx.cs.
Add new method:
protected void WriteTreeNodeTitles()
{
using (var conn = new DataConnection())
{
foreach( string line in conn.Get<IPage>().Select( p => " \"~/page(" + p.Id.ToString().ToLower() + ")\": \"" + p.UrlTitle + "\",\r\n" ) )
{
Response.Write( line );
}
}
}
You have to add few usings, of course:
using System.Linq;
using Composite.Data;
using Composite.Data.Types;
So, now your top.aspx contains the mapping of page guid-like urls to UrlTitles, and the treeNodeProcessor function that uses that mapping for modifying your page tree.

Circular references and stack overflow exceptions

I have this many to many association between KundeInfo and HovedKategori, which I have mapped in my MS SQL database like this:
I have implemented the methods KundeInfo.HovedKategoris:
public IEnumerable<KundeInfo> KundeInfos
{
get
{
using (var dc = new DataClassesBSMAKSDataContext())
{
dc.DeferredLoadingEnabled = false;
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.HovedKategori_Id == Id);
var kundeInfos = dc.KundeInfos.Where(x => kundeInfoHovedKategoris.Any(y => y.KundeInfo_Id == x.Id));
return kundeInfos.ToList();
}
}
}
... and HovedKategori.KundeInfos:
public IEnumerable<HovedKategori> HovedKategoris
{
get
{
using (var dc = new DataClassesBSMAKSDataContext())
{
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.KundeInfo_Id == Id);
var hovedKategoris = dc.HovedKategoris.Where(x => kundeInfoHovedKategoris.Any(y => y.HovedKategori_Id == x.Id));
return hovedKategoris.ToList();
}
}
}
This retrieves the associated KundeInfos from a specific HovedKategori and opposite. The problemhowever lies in the serialization. When I call ToList(), or serialize these objects to JSON, linq will try to first follow all references returned by HovedKategori.KundeInfos, if it were that method I were to call first, and then it would for each returned object, try to follow all references returned by KundeInfo.HovedKategoris and so on, until it would cast a stack overflow exception.
If I could somehow prevent linq from following certain properties with an [Ignore] attribute or something, it would work, but I haven't been able to find anything like that.
What can I do in this situation?
This is in part a design issue. What you should really ask yourself is if you need navigation properties in every possible direction. For example if you just add a Kategori ID instead of a navigation property you could still query your context (by using the ID) but do you really need to always get all the Kategori's with all underlying data?
also if you make your properties virtual you have lazy loading and will only get the information if you .Include it or explicitly reference it.
Ok - So I solved this problem by just making it into methods on the respective classes, which it should be in the first place, since it retrieved these entities from the database. So yes, partially design issue.
Virtual did not work, I considered using projection and an abstract class, but it would be such a haystack of inheritance, and class casts, that it would not even be worth considering.
public IEnumerable<KundeInfo> KundeInfos()
{
using (var dc = new DataClassesBSMAKSDataContext())
{
var kundeInfoHovedKategoris = dc.KundeInfoHovedKategoris.Where(x => x.HovedKategori_Id == Id);
var kundeInfos = dc.KundeInfos.Where(x => kundeInfoHovedKategoris.Any(y => y.KundeInfo_Id == x.Id));
return kundeInfos.ToList();
}
}

WPF list<t> sort

my first post here.
i have a list"<"frameworkelement> that i'm populating with a select process. each frameworkelement has a uid that holds its ZOrder.
i need to sort these by the ZOrder from lowest to highest. i can get this using a listbox and adding the Uid's like this:
//Add Object Uid's
ListBox lstTempOrder = new ListBox();
foreach(FrameworkElement feObject in MainWindow.Data.SelectedObjects)
{
lstTempOrder.Items.Add(feObject.Uid);
}
//Reorder from 0 to above of the ZIndexes
lstTempOrder.Items.SortDescriptions.Add(new System.ComponentModel.SortDescription("", System.ComponentModel.ListSortDirection.Ascending));
but i need to do this with a List"<"FrameWorkElement> and Sort.
Here is the code where i populate the List"<"T> (SelectedObjects and CopyObjectsCollections are List"<"FrameWorkElement>" lists.
foreach(FrameworkElement feObject in MainWindow.Data.SelectedObjects)
{
MainWindow.Data.CopyObjectsCollection.Add(feObject);
}
i've looked at CollectionViewSource and IComparer but i can't really make any sense of it.
I might have miss-read your question, but if you just want to sort your List<T>, then why don't you just use the LinQ OrderBy method?
MainWindow.Data.CopyObjectsCollection =
MainWindow.Data.CopyObjectsCollection.OrderBy(f => f.Uid).ToList();
If that sorts it the wrong way round for your requirements, then you can use this:
MainWindow.Data.CopyObjectsCollection =
MainWindow.Data.CopyObjectsCollection.OrderByDescending(f => f.Uid).ToList();
UPDATE >>>
OrderBy is a LinQ extension method. Add using System.Linq; at the top of your class to use it. f relates to an instance of your FrameworkElement object. The above lambda expression basically means 'sort using the Uid property values'.
UPDATE 2 >>>
The OrderBy method does not alter the original collection... that is why my example sets the collection to the result of the OrderBy method. See this basic example:
List<FrameworkElement> elements = new List<FrameworkElement>();
elements.Add(new FrameworkElement() { Uid = "Object1003-1" });
elements.Add(new FrameworkElement() { Uid = "Object1002-2" });
elements.Add(new FrameworkElement() { Uid = "Object1002-1" });
elements.Add(new FrameworkElement() { Uid = "Object1001-1" });
elements.Add(new FrameworkElement() { Uid = "Object1001-3" });
elements.Add(new FrameworkElement() { Uid = "Object1001-2" });
string result = string.Join(", ", elements.Select(f => f.Uid));
elements = elements.OrderBy(f => f.Uid).ToList();
string orderedResult = string.Join(", ", elements.Select(f => f.Uid));
By comparing the values of result and orderedResult you can see that this orders them perfectly.
UPDATE 3 (and hopefully the LAST one) >>>
Dude, you need to learn about Lambda expressions... take a look at the Lambda Expressions (C# Programming Guide) page at MSDN for more information.
elements = elements.OrderBy(f => f.Uid).ToList();
The f in this Lambda expression is declared in this expression before the '=>'. It is fairly standard to name these parameters with one letter like Exceptions, but we could name it anything:
elements = elements.OrderBy(frameworkElement => frameworkElement.Uid).ToList();

How to remove a record from the entity - MVVM, WPF

I have the following code in ViewModel, I would like to remove a record from an entity but it doesn't work. Can someone please shed some light...?
Usertable users = new Usertable();
users.User_ID = Entity.User_ID;
users.user_role = "Admin";
Entity.CompanyRoles.Remove(users);
Instead if I replace the Remove with Add, it will add one record to the entity.
Only Remove is a concern to me.
First you need to fetch the entity you are about to remove, then remove it, then save your changes to the datacontext:
var userToRemove = Entity.CompanyRoles.Single(cr => cr.user_role == "Admin");
Entity.CompanyRoles.DeleteObject(userToRemove);
Entity.SaveChanges();
Objects are compared by reference, not values, so unless Entity.CompanyRoles contains the exact reference in memory as the object you're trying to remove, it won't find the object in the collection and remove it.
There are two ways to fix it.
The best way is to get a reference to the object in the collection like Paul suggests
var userToRemove = Entity.CompanyRoles.Single(cr =>
cr.user_role == "Admin" && cr.User_ID = Entity.User_ID);
Entity.CompanyRoles.Remove(userToRemove);
Or another method that works is to overwrite the .Equals() of the object to consider them equal if the data is the same, regardless of the memory reference. I would not usually recommend this except in special cases since it affects any operation that compares one copy of this object to another using .Equals()
public override bool Equals(object obj)
{
if (obj == null || !(obj == MyClass))
return false;
var obj2 = obj as MyClass;
return obj2.user_role == this.user_role && obj2.User_ID == this.User_ID;
}

RIA-Services - how to WhereOr or use an IN style construct

I am using SL 4, WCF RIA Services against Entity Framework 4.0. I have an Entity, Visit, that has a string Status field. I have a search screen where I need to display results that have StatusA or StatusB. I am struggling to find a way to specify a client-side query that specifies a collection of statuses that should be matched. If I was to write what I want in SQL it would look something like:
select * from Visit where Status in ('StatusA', 'StatusB');
Client side, it appears to be straightforward to chain Where methods for a WhereAnd effect:
var query = this.PqContext.GetVisitsQuery();
if (!string.IsNullOrEmpty(this.PracticeName))
{
query = query.Where(v => v.PracticeName.ToUpper().Contains(this.PracticeName.ToUpper()));
}
if (this.VisitDateAfter.HasValue)
{
query = query.Where(v => v.VisitDate > this.VisitDateAfter);
}
if (this.VisitDateBefore.HasValue)
{
query = query.Where(v => v.VisitDate < this.VisitDateBefore);
}
However, I can't seem to find a straightforward way to do a WhereOr style operation. I have tried this:
var statuses = new List<string>();
if (this.ShowStatusA)
{
statuses.Add("StatusA");
}
if (this.ShowStatusB)
{
statuses.Add("StatusB");
}
if (statuses.Any())
{
query = query.Where(BuildContainsExpression<Visit, string>(e => e.Status, statuses));
}
Where BuildContainsExpression looks like:
private static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector)
{
throw new ArgumentNullException("valueSelector");
}
if (null == values)
{
throw new ArgumentNullException("values");
}
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any())
{
return e => false;
}
var equals =
values.Select(
value =>
(Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate<Expression>(Expression.Or);
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
But this throws a "Bitwise operators are not supported in queries." exception. Any clues? Is there an alternative way to build an expression tree that works here or do I need to pass all the parameters over to the server and use the BuildContainsExpression there?
Your time and your guidance are much appreciated.
You can create a query method such as the following in your domain service:
GetVisitsByStatus(string[] statusList) {
// create the LINQ where clause here
}
And then from the client, call context.GetVistsByStatusQuery(string[]).
Not all of LINQ is (or even can) be exposed over the URL, so there are always cases where you need to use simple parameters, and have the middle tier construct the LINQ expressions that eventually define the query that goes to the back-end data store.
Hope that helps.

Resources