Umbraco: Determining Who Last Updated Content - umbraco

I see in the API there is a way to grab the UpdateDate field. Is there a way to determine WHO made the last update to a particular content node? In other words, the user ID of the person that last edited a particular node?
Here's some of the code I'm attempting to use:
Document[] releaseDocs = Document.GetRootDocuments();
User currentUser = User.GetCurrent();
foreach (var doc in releaseDocs)
{
docPermissions = currentUser.GetPermissions(doc.Path);
if ((docPermissions.Contains("F")) && (docPermissions.Contains("U")))
{
if (doc.HasPendingChanges())
{
if (doc.ParentId > -1)
{
lblPageContent += doc.Writer.Name;
}
}
}
}

I did some more research on this and, while writerID and writerName are supposed to store the last editor, there appears to be a bug in Umbraco. Their support suggested creating a hidden field that updates with the editor user data, so that's my temporary work around for now.

You could try writerName and writerID same way you use UpdateDate like as below:
if you are using Razor you can use as below:
#Model.writerName
#Model.writerID
in XSLT:
<xsl:value-of select="$currentpage/#writerName" />
<xsl:value-of select="$currentpage/#writerID" />
in C# Usercontrol:
using System;
using umbraco.NodeFactory;
namespace UmbracoTestingProj
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Node node = Node.GetCurrent();
int writerId = node.WriterID;
string wrtierName = node.WriterName;
}
}
}
I hope this would help,
if you have question just just comment below

Related

Object reference not set to an instance of an object in umbraco 4.8.1

Due to performance issue , I have added the publishing code in thread.
My code is:
public void functionname()
{
----------------
------------
try
{
HttpontextforMailSending gethttpcontextforpublish2 = new HttpontextforMailSending()
{
HttpContextReference = HttpContext.Current,
courseDocument = shortCourseDocument,
createdUser = new User(0)
};
Thread t2 = new Thread(PublishDocument);
t2.Start(gethttpcontextforpublish2);
}
catch { }
-------------
-----------
}
private void PublishDocument(object input)
{
HttpontextforMailSending httpcontextformail = (HttpontextforMailSending)input;
Document course = httpcontextformail.courseDocument;
User createduser = httpcontextformail.createdUser;
if (course != null && createduser != null)
{
course.Publish(createduser);
umbraco.library.UpdateDocumentCache(course.Id);
}
}
public class HttpontextforMailSending
{
public HttpContext HttpContextReference { get; set; }
public Document courseDocument { get; set; }
public User createdUser { get; set; }
}
But I get Object reference not set to an instance of an object" error on "course.Publish(createduser);
The Umbraco version is 4.8.1.
May be this error is due to course.HttpContext. It has null value.
However when I set it as course.HttpContext = httpcontextformail.HttpContextReference; it shows a warning
"umbraco.cms.businesslogic.web.document.HttpContext is obsolete. Do not use this. GethttpContextvia regular ASP.Net methods instead.
When I debug this code, I get HttpContext on httpcontextformail.HttpContextReference.
But after executing course.HttpContext = httpcontextformail.HttpContextReference;, course.HttpContext still have null value.
Please help me
Thanks
Umbraco expects to be used within a web request and therefore the API will not work if called outside of a web request in another application etc.
If you want to execute some something using the Umbraco API externally then you will need to use Umbraco Base (or similar) so that the API has the needed HttpContext.
Umbraco Base is a RESTlike system for allowing the execution of queries or code via defined URLs within an Umbraco context.

MVVM Light - Unable to update parent view from child - nested edit

My situation is slightly different than from other posts and I was not able to solve it with the other trhreads. So that why I ask.
I have a class that is obtained from deserializing an XML like:
<?xml version="1.0" encoding="UTF-8"?>
<node>
<leaf>
<name>node 1</name>
<text>text 1</text>
<url>url 1</url>
</leaf>
<leaf>
<name>node 2</name>
<text>text 2</text>
<url>url 2</url>
</leaf>
</node>
so the class is:
[XmlRoot("node")]
public class csNodeList
{
public csNodeList()
{
Leaf = new csLeafCollection();
}
[XmlElement("leaf")]
public csLeafCollection Leaf
{
get;
set;
}
}
public class csLeaf
{
public csLeaf()
{
Name ="";
Description = "";
Address = "";
}
[XmlElement("name")]
public string Name
{
get;
set;
}
[XmlElement("text")]
public string Description
{
get;
set;
}
[XmlElement("url")]
public string Address
{
get;
set;
}
}
public class csLeafCollection : System.Collections.ObjectModel.ObservableCollection<csLeaf>
{
}
Then I have 2 Views, one to show all the leafs and one to edit one leaf. I've implemented commit and rollback so I use messaging back and forth to pass the new values and I store the old ones.
To do so I copy the objects a a backup variable and then I modify the ones associated via binding to the XAML view, in this way (in theory) any change to the ViewModel data should be reflected.
Also is better because if I commit the changes I just discard the backup variables (this is 90% of the times) and if I need to roll back I copy back from the backup variables.
MainView:
public const string listPropertyName = "list";
private csNodeList _list = new csNodeList();
public csNodeList list
{
get
{
return _list;
}
set
{
Set(listPropertyName, ref _list, value, false);
}
}
Using the message I send back the new values of a node and I put them in the correct position:
private void DoSomething(csMessage message)
{
csMessage rmessage;
if (message != null)
{
switch (message.destination)
{
case csMessage.e2MessageDest.updateNode:
//_editP should be fine.
list.Leaf[list.Leaf.IndexOf(_editP)].Name = ((csLeaf)message.payload).Name;
list.Leaf[list.Leaf.IndexOf(_editP)].Text= ((csLeaf)message.payload).Text;
list.Leaf[list.Leaf.IndexOf(_editP)].Address = ((csLeaf)message.payload).Address;
RaisePropertyChanged(listPropertyName , null, _list, true);
break;
}
}
}
The code is executed correctly and the item is changed.
BUT the RaisePropertyChanged is ignored. I've tried even just the one with the listPropertyName without any change.
If I save the changes exit from the app and get back I see the new value correctly stored
Can you please help me?
Thanks,
Massimo
The reason why your RaisePropertyChanged is ignored is hat yor Leaf class des not implement INotifyOropertyChanged. Commonly the model is wrapped into a view model which then implements INotifyPropertyChanged to notify the view hat something has happened.
However, you also can implement INotifyPropertyChanged on the model class directly. To implement INotifyPropertyChanged each property has to raise the propty changed event.
public string Property {
get { ... }
set {
if (_propertyField == value)
return;
_propertyField = value;
RaisePropertyChanged("Property");
}
}
The code assumes hat there is a method RaisePropertyChanged which actually taises the PropertyChangedEvent.
Thank you everyone for the help.
Investigating your suggestion I've found a slightly different solution; as you correctly said the issue is that the leaf fields are not "observable" so they do not generate a notification event.
I've noticed that if I add or Delete a profile the binding is updated.
So what I've decided to do is not to edit directly the leafs but to replace the node.
What I do not like is that I have to create a node to replace the old one and this allocates a little bit more memory... but for small data like the one I have it can work without any major impact on the app performance/memory foot print.
Here is what I do:
csLeaf _leaf = new slLeaf();
_leaf.Name = ((csLeaf)message.payload).Name;
_leaf.Text= ((csLeaf)message.payload).Text;
_leaf.URL = ((csLeaf)message.payload).Address;
list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;
To optimized readabilty of code I've enhanced it adding a constructor with 3 parameters so that the code can be:
csLeaf _leaf = new slLeaf(((csLeaf)message.payload).Name, ((csLeaf)message.payload).Text, ((csLeaf)message.payload).Address);
list.Leaf[list.Leaf.IndexOf(_editP)] = _leaf;
The constructor is:
public csLeaf(string _name, string _description, string _address)
{
Name = _name;
Description = _description;
Address = _address;
}

Umbraco Document Type Field Default Value

I would like set a default value for a date picker field in a Document Type in Umbraco.
How can I do this?
This can be easily done using Events, on Document.New
http://our.umbraco.org/wiki/reference/api-cheatsheet/using-applicationbase-to-register-events/overview-of-all-events
Just create a new class (eg. UmbracoEvents.cs)
using System;
using umbraco.BusinessLogic;
using umbraco.cms.businesslogic.web;
using Examine;
public class UmbracoEvents: ApplicationBase
{
/// <summary>Constructor</summary>
public UmbracoEvents()
{
Document.New += new Document.NewEventHandler(Document_New);
}
private void Document_New(Document sender, umbraco.cms.businesslogic.NewEventArgs e)
{
if (sender.ContentType.Alias == "News")
{
sender.getProperty("date").Value = DateTime.Today; // Set the date for a new News item to today
}
}
}
You could use the Standard Values or the Default Values for Umbraco package.
That can be achieved with sebastiaans first link. As it says on the page you just put in $date$ as the standard value. Then the package will insert current date when the user creates a document of this type.

In EF4, Is there a way to have unmapped properties make it across the wire?

I have a custom field that I've added to one of my EF entities in a shared.cs file like so:
public partial class Content
{
public int Test = 5;
}
On the client side, the OnCreated handler for that same class looks like this:
partial void OnCreated()
{
this.Test = 42;
}
I've added an event handler to the SavingChanges event for the context on the server like this:
partial void OnContextCreated()
{
this.SavingChanges += (sender, e) =>
{
foreach (object o in GetChangedEntities())
{
if (o is Content)
{
// Break to see what the value of Test is;
}
}
}
}
When I break at the comment (which is not really a comment my code :), the value of Test is always 5. In fact, I can't seem to set it to 42 anywhere on the client and have that value make it to the server.
I have set breakpoints all over the place, and the value is definitely being set to 42 on the client-side. Is there something that I'm doing wrong, or is this behavior just not supported? Incidentally, I've also tried this as a property instead of a field--just in case.
I needed to mark my property/field as a [DataMember] like this:
public partial class Content
{
[DataMember]
public int Test = 5;
}
and then move it out of the shared.cs file to a CustomProperties.cs (or similar) file in the server project to avoid multiple delcarations. Now it crosses the wire just fine.

Databinding to a DomainDataSource Query Parameter

I need to bind a username to a DomainDataSource QueryParameter. My understanding is that the following does not work:
<RiaControls:DomainDataSource x:Name="MyData" LoadSize="20" QueryName="GetStockByCompany" AutoLoad="True">
<RiaControls:DomainDataSource.DomainContext>
<ds:InventoryDomainContext />
</RiaControls:DomainDataSource.DomainContext>
<RiaControls:DomainDataSource.QueryParameters>
<riadata:Parameter
ParameterName="userName"
Value="{Binding Path=User.Name}" />
</RiaControls:DomainDataSource.QueryParameters>
</RiaControls:DomainDataSource>
I am not opposed to using the C# code-behind part of the page, but I'm not sure what event to put this in.
So far I've tried this:
public Inventory()
{
InitializeComponent();
Loaded += Inventory_Loaded;
}
private void Inventory_Loaded(object sender, RoutedEventArgs e)
{
this.MyData.QueryParameters.Add(new Parameter { ParameterName = "userID", Value = RiaContext.Current.User.Name});
}
But since InitializeComponent() fires first, and loades the data, which causes the DomainDataSource to bomb due to the Query not having any parameters to run... it didn't work.
Next I tried this...
[xaml file]
<RiaControls:DomainDataSource x:Name="MyData" LoadSize="20" QueryName="GetStockByCompany" AutoLoad="True" LoadingData="MyData_LoadingData">
[cs file]
private void MyData_LoadingData(object sender, LoadingDataEventArgs e)
{
this.MyData.QueryParameters.Add(new Parameter { ParameterName = "userID", Value = RiaContext.Current.User.Name});
}
Unfortunately, the event never fired. I'm not sure why.
I even tried this:
[xaml file]
<RiaControls:DomainDataSource x:Name="MyData" LoadSize="20" QueryName="GetStockByCompany" AutoLoad="True" LoadedData="MyData_LoadedData">
[cs file]
private void MyData_LoadedData(object sender, LoadedDataEventArgs e)
{
this.MyData.QueryParameters.Add(new Parameter { ParameterName = "userID", Value = RiaContext.Current.User.Name});
}
But that was just dumb.
I'm at a loss. How do I load this query, with the parameter, as the page loads?
Thanks!
Hmmm I not a specific answer to your problem but I may know a way to avoid the situation entirely.
I noticed you have a method named "GetStockByCompany" that accept the currently logged in user as a parameter...
You can completely remove the need for the parameter and instead on your server side query for "GetStockByCompany" use this in your "Where" part:
this.ServiceContext.User.Identity.Name
Ex - Getting all the albums for the currently logged in user:
album = this.Context.AlbumSet
.Where(n => n.AlbumId == AlbumId)
.Where(n => n.aspnet_Users.UserName == this.ServiceContext.User.Identity.Name)
.First();
Binding the query parameter works, the typical usage is that you bind it directly to controls.
To set the parameter in code behind, give the parameter a name and set the value property. There's no need to add the whole parameter in code behind.

Resources