One challenge to getting a custom form to display what you want is understanding the eccentricities of SharePoint Designer and the SharePoint storage. I had a custom list which needed to display the created by and modifed by data. These fields needed to be displayed on the main form along with the other data items. This information is normally parked in the footer of the form. This can be done by using the SharePoint:CreatedModifiedInfo control item. One thing to note is that the fields in Sharepoint are stored internally with different names:
Created By = @Author
Modified By = @Editor
Code to add to your custom form:
<tr>
<td valign="top" class="ms-formlabel"><nobr>Created by</nobr></td>
<td valign="top" class="ms-formbody">
<SharePoint:CreatedModifiedInfo ControlMode="Edit" runat="server">
<CustomTemplate>
<SharePoint:FormField FieldName="Author" runat="server" ControlMode="Display" DisableInputFieldLabel="true" /><br/>
<SharePoint:FieldValue FieldName="Modified" runat="server" ControlMode="Display" DisableInputFieldLabel="true"/>
</CustomTemplate>
</SharePoint:CreatedModifiedInfo>
</td>
</tr>
This missive records my trials and tribulations as I code my way through projects. Fix the problem once and reuse the solution!
Tuesday, July 29, 2014
Custom Edit List Form Modified By field is blank -
Custom Form Does Not Display “Created By” value
I was customizing a sharepoint list today and customer asked for a modification to the New and Edit forms for the list. I was using Designer to accomplish the task and was able to get most of it working. The customer had asked for a field on the edit form to show the Modified by field. This is the standard canned field which is part of every list in SharePoint. I thought no problem I just get the internal field name and use the <xsl:value-of select="@Editor"/>.
Challenge:
Each time I would load the edit form the field would resolve to empty. This made no sense and really drove me crazywhile I tried to understand the problem.
Sample:
<tr>
<td valign="top" class="ms-formlabel" width="190px">
<H3 class="ms-standardheader">
<nobr>Modified By</nobr>
</H3>
<!--Always Blank --/>
<xsl:value-of select="@Editor" disable-output-escaping="yes"/>
</td>
<td valign="top" class="ms-formbody" width="400px">
<SharePoint:FormField runat="server" id="ModifiedByID" ControlMode="New" FieldName="Editor" __designer:bind="{ddwrt:DataBind('u','ModifiedByID','Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Editor')}"/>
<SharePoint:FieldDescription runat="server" id="ff7description{$Pos}" FieldName="Editor" ControlMode="Edit"/>
</td>
</tr>
Solution:
It helped me to look at the entire form and pay close attention to the syntax for the FormField control.
This includes the attribute ControlMode. This tells the form control what state it is currently in. (New,Edit,Display) The key mistake I had made was to set this controlmode attribute to New instead of Edit.Once I set the controlmode attribute to Edit the value began to be passed to the form and displayed.
Corrected:
<tr>
<td valign="top" class="ms-formlabel" width="190px">
<H3 class="ms-standardheader">
<nobr>Modified By</nobr>
</H3>
<!--Always Blank --/>
<xsl:value-of select="@Editor" disable-output-escaping="yes"/>
</td>
<td valign="top" class="ms-formbody" width="400px">
<SharePoint:FormField runat="server" id="ModifiedByID" ControlMode="Edit" FieldName="Editor" __designer:bind="{ddwrt:DataBind('u','ModifiedByID','Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Editor')}"/>
<SharePoint:FieldDescription runat="server" id="ff7description{$Pos}" FieldName="Editor" ControlMode="Edit"/>
</td>
</tr>
I was customizing a sharepoint list today and customer asked for a modification to the New and Edit forms for the list. I was using Designer to accomplish the task and was able to get most of it working. The customer had asked for a field on the edit form to show the Modified by field. This is the standard canned field which is part of every list in SharePoint. I thought no problem I just get the internal field name and use the <xsl:value-of select="@Editor"/>.
Challenge:
Each time I would load the edit form the field would resolve to empty. This made no sense and really drove me crazywhile I tried to understand the problem.
Sample:
<tr>
<td valign="top" class="ms-formlabel" width="190px">
<H3 class="ms-standardheader">
<nobr>Modified By</nobr>
</H3>
<!--Always Blank --/>
<xsl:value-of select="@Editor" disable-output-escaping="yes"/>
</td>
<td valign="top" class="ms-formbody" width="400px">
<SharePoint:FormField runat="server" id="ModifiedByID" ControlMode="New" FieldName="Editor" __designer:bind="{ddwrt:DataBind('u','ModifiedByID','Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Editor')}"/>
<SharePoint:FieldDescription runat="server" id="ff7description{$Pos}" FieldName="Editor" ControlMode="Edit"/>
</td>
</tr>
Solution:
It helped me to look at the entire form and pay close attention to the syntax for the FormField control.
This includes the attribute ControlMode. This tells the form control what state it is currently in. (New,Edit,Display) The key mistake I had made was to set this controlmode attribute to New instead of Edit.Once I set the controlmode attribute to Edit the value began to be passed to the form and displayed.
Corrected:
<tr>
<td valign="top" class="ms-formlabel" width="190px">
<H3 class="ms-standardheader">
<nobr>Modified By</nobr>
</H3>
<!--Always Blank --/>
<xsl:value-of select="@Editor" disable-output-escaping="yes"/>
</td>
<td valign="top" class="ms-formbody" width="400px">
<SharePoint:FormField runat="server" id="ModifiedByID" ControlMode="Edit" FieldName="Editor" __designer:bind="{ddwrt:DataBind('u','ModifiedByID','Value','ValueChanged','ID',ddwrt:EscapeDelims(string(@ID)),'@Editor')}"/>
<SharePoint:FieldDescription runat="server" id="ff7description{$Pos}" FieldName="Editor" ControlMode="Edit"/>
</td>
</tr>
Wednesday, July 23, 2014
Xamarin and Sole Source justification
Great post by Tim Anderson and Andy Dent about Xamarin vs. other competitors
- Xamarin (+ MVVMCross) ideal cross product solution. https://github.com/MvvmCross/MvvmCross
- The purpose of PhoneGap is to allow HTML-based web applications to be deployed and installed as native applications
- The goal of Titanium Mobile is to provide a high level, cross-platform JavaScript runtime and API for mobile development (today we support iOS, Android, and the browser, with BlackBerry 10 and Windows Phone coming soon and eventually, respectively).
Source:
http://stackoverflow.com/questions/17249500/xamarin-2-0-vs-appcelerator-titanium-vs-phonegap
- Xamarin (+ MVVMCross) ideal cross product solution. https://github.com/MvvmCross/MvvmCross
- The purpose of PhoneGap is to allow HTML-based web applications to be deployed and installed as native applications
- The goal of Titanium Mobile is to provide a high level, cross-platform JavaScript runtime and API for mobile development (today we support iOS, Android, and the browser, with BlackBerry 10 and Windows Phone coming soon and eventually, respectively).
Source:
http://stackoverflow.com/questions/17249500/xamarin-2-0-vs-appcelerator-titanium-vs-phonegap
Friday, July 18, 2014
JSON deserializing and serializing
Convert XML to JSON
http://www.utilities-online.info/xmltojson/
JSON convert between XMLNode and json
XmlNote myXmlNode = JsonConvert.DeserializeXmlNode(myJsonString);
// or .DeserilizeXmlNode(myJsonString, "root");
// if myJsonString does not have a root
string jsonString = JsonConvert.SerializeXmlNode(myXmlNode);
SDK documentation
http://james.newtonking.com/json/help/index.html
http://www.utilities-online.info/xmltojson/
JSON convert between XMLNode and json
XmlNote myXmlNode = JsonConvert.DeserializeXmlNode(myJsonString);
// or .DeserilizeXmlNode(myJsonString, "root");
// if myJsonString does not have a root
string jsonString = JsonConvert.SerializeXmlNode(myXmlNode);
SDK documentation
http://james.newtonking.com/json/help/index.html
Wednesday, July 16, 2014
Connection string sytnax for IIS to SQL Server connection.
I always forget the syntax for connnection strings in my applications. The guys at connectionsstrings.com do a great job but stumbled across another way to get at the correct syntax.
The steps below.
1. Right-click
an empty spot on the desktop and choose NEW, TEXT DOCUMENT from the context
menu
2. Save it with
a .udl extension, and click yes when it asks are you sure.
3. Double-click
the new udl file you just created. It will open a dialogue. Go to the Provider
tab, and choose the appropriate provider.
4. Go to the
Connection tab and fill in the server name and database name, and choose NT
authentication (or use a specific username and password, which is SQL
authentication). Now click Test Connection. If it works, you're ready to click
OK and move on to the final step. If it doesn't you need to resolve permission
issues, or you've mis-typed something.
5. Now
right-click the file on the desktop and open it in notepad. It will display the
connection string that you can copy and paste to wherever you need it.
Identity Impersonation with Classic ASP
The ideal:
I have written before about using impersonation in IIS. That was using the tools provided in .Net Framework. This allows a web application to provide dedicated account access to a database. Thus the application can prompt a user to login with their Windows credentials and a separate service account can be used to support all data access between that web application and the database. This makes the application most secure as it restricts database access to a single account. Auditing also becomes much simpler.
The reality:
Now it is great to think about how easy it is in .Net. The reality is that there are still classic ASP applications which are simply being ported forward to IIS7/7.5. I cringe when a customer asks for this. They can't understand why the software has to be updated. One issue is impersonation.
I had a customer wanting to port the application forward and just assuming that they could apply all the security "boxes" just like a .Net application. Sadly this is not the case for classic ASP. It does not have a native mechanism to support impersonation.
"As most of you know ASP executes under the guise, typically, of the IUSR_ account in IIS (IIS_IUSRS group for IIS 7) which has a very few permissions to do anything on the system (in most cases anyway). "
So what can you do? I found some great help from Doug Parsons over at Wrox.com
The fix:
You can write a ActiveX dll which you register on your IIS box. This dll is then used by your ASP application each time you want to access the database.
Using Statements
Class Definition
In the Solution Explorer locate the AssemblyInfo.cs file underneath the Properties folder of your project (you may need to click the 'Show all Files' button at the top of the solution explorer) and open it. Add the following line to the file:
Hit Ctrl + Shift + B (Build) to create your assembly.
Start* up a Visual Studio Command Prompt which can, typically, be found in Start -- > Programs --> Visual Studio 2008 --> Visual Studio Tools and navigate to the directory where your assembly is (something like: projectDirectory\bin\debug\) and issue the following command: regasm Impersonate.dll /tlb
*if you are running Win7/8 run this prompt with Admin rights
In your ASP editor (I used Notepad) use the following code to test your assembly:
Most of this is very basic but there are some things to point out here:
Pitfalls and Gotchas
Source:
C# rework:
http://p2p.wrox.com/classic-asp-basics/77007-solution-impersonation-classic-asp.html
MSDN Original (VB6)
http://support.microsoft.com/kb/248187/en-us
I have written before about using impersonation in IIS. That was using the tools provided in .Net Framework. This allows a web application to provide dedicated account access to a database. Thus the application can prompt a user to login with their Windows credentials and a separate service account can be used to support all data access between that web application and the database. This makes the application most secure as it restricts database access to a single account. Auditing also becomes much simpler.
The reality:
Now it is great to think about how easy it is in .Net. The reality is that there are still classic ASP applications which are simply being ported forward to IIS7/7.5. I cringe when a customer asks for this. They can't understand why the software has to be updated. One issue is impersonation.
I had a customer wanting to port the application forward and just assuming that they could apply all the security "boxes" just like a .Net application. Sadly this is not the case for classic ASP. It does not have a native mechanism to support impersonation.
"As most of you know ASP executes under the guise, typically, of the IUSR_ account in IIS (IIS_IUSRS group for IIS 7) which has a very few permissions to do anything on the system (in most cases anyway). "
So what can you do? I found some great help from Doug Parsons over at Wrox.com
The fix:
You can write a ActiveX dll which you register on your IIS box. This dll is then used by your ASP application each time you want to access the database.
Using Statements
csharp Code:
using System; using System.Runtime.InteropServices;
csharp Code:
namespace Impersonate
{[indent]public class UserLogon
{[indent][DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
[DllImport("advapi32.dll")]
public static extern int ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll")]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
public void Logon(string strAdminUser, string strAdminPassword, string strAdminDomain)
{
IntPtr hToken;
RevertToSelf();
LogonUser(strAdminUser, strAdminDomain, strAdminPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out hToken);
ImpersonateLoggedOnUser(hToken);
CloseHandle(hToken);
}
public void Logoff()
{
RevertToSelf();
}
[/indent]}
[/indent]}
{[indent]public class UserLogon
{[indent][DllImport("advapi32.dll", SetLastError=true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
[DllImport("advapi32.dll")]
public static extern int ImpersonateLoggedOnUser(IntPtr hToken);
[DllImport("advapi32.dll")]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
private const int LOGON32_LOGON_INTERACTIVE = 2;
private const int LOGON32_PROVIDER_DEFAULT = 0;
public void Logon(string strAdminUser, string strAdminPassword, string strAdminDomain)
{
IntPtr hToken;
RevertToSelf();
LogonUser(strAdminUser, strAdminDomain, strAdminPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out hToken);
ImpersonateLoggedOnUser(hToken);
CloseHandle(hToken);
}
public void Logoff()
{
RevertToSelf();
}
[/indent]}
[/indent]}
In the Solution Explorer locate the AssemblyInfo.cs file underneath the Properties folder of your project (you may need to click the 'Show all Files' button at the top of the solution explorer) and open it. Add the following line to the file:
csharp Code:
[assembly: ComVisible(true)]
Start* up a Visual Studio Command Prompt which can, typically, be found in Start -- > Programs --> Visual Studio 2008 --> Visual Studio Tools and navigate to the directory where your assembly is (something like: projectDirectory\bin\debug\) and issue the following command: regasm Impersonate.dll /tlb
*if you are running Win7/8 run this prompt with Admin rights
In your ASP editor (I used Notepad) use the following code to test your assembly:
asp Code:
[size=2]<% Option Explicit Dim objLogon, oFs, oTextFile Set objLogon = Server.CreateObject("Impersonate.UserLogon") objLogon.Logon "username", "password", "domainname" Response.Write("Impersonating") set oFs = server.createobject("Scripting.FileSystemObject") set oTextFile = oFs.OpenTextFile("C:\foo.txt", 2, True) oTextFile.Write "This is some data wrote as a domain user" oTextFile.Close set oTextFile = nothing set oFS = nothing objLogon.Logoff Set objLogon = Nothing Response.Write("Done Impersonating") %> [/size]
Most of this is very basic but there are some things to point out here:
- Make sure that the call to CreateObject for objLogon has the name of your assembly. The format for the name will be Namespace.Class. If you have followed along using my code then the correct name is Impersonate.UserLogon.
- Provide a valid username, password, and domain. I have not tried to impersonate a Local User but, I assume, you could simply provide the PC name as the domain and have this work.
- Make sure that you call LogOff so that the thread reverts back to running as the IUSR account.
Pitfalls and Gotchas
- If you are running IIS 7 on a x64 platform you may get a cryptic "Active X is unable to Create Object". Before you google the problem first start up inetmgr, and locate the Application Pool that your ASP script is running from, right click on it and select 'Advanced Settings' from the context menu. The second option under the General section is 'Enable 32-Bit Applications'; change the value from False to True. If you still get the error, then Google it ;]
- For testing purposes, I suggest moving your dll out of your debug directory and into another one and then register the dll from there. The reason for this is if you have to make any changes to the dll you will not be able to rebuild your project since the OS will have a lock on the dll in your bin directory.
- If you need to make changes to your assembly after you have already registered it issue the following command from a Visual Studio command prompt inside the directory your assembly is registered from: regasm /u Impersonate.dll tlb:Impersonate.tlb Make your changes and then re-register the assembly using the regasm from the main tutorial. If, after you have unregistered the dll you are unable to replace the file (Access is denied because the file is in use by another process) start taskmgr and kill the w3wp.exe process (you will need to tick off the 'Show porcesses from all users' checkbox). You should now be able to replace the file.
Source:
C# rework:
http://p2p.wrox.com/classic-asp-basics/77007-solution-impersonation-classic-asp.html
MSDN Original (VB6)
http://support.microsoft.com/kb/248187/en-us
Tuesday, July 15, 2014
Cool tool for JSON to C# classes
This is a great site which reads your Json stream and recommends a C# class to contain the resulting query in your solution.
http://json2csharp.com/
http://json2csharp.com/
Code Sample: Return Office element from Json query in Windows Phone
I wanted to capture this deprecated piece of code which I no longer need in my phone app.
We are supplying a list of office specific information to the phone client via a JSON query.
Calling Method:
using System.Net.WebClient;
using Newtonsoft.Json.JsonConvert;
private static Dictionary<int, Office> _officeLookup;
private static List<Office> theOffices = new List<Office>();
WebClient webClient = new WebClient();
webClient.Headers["Accept"] = "application/json";
webClient.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(apiUrl));
private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e);
{
try
{
if(e.Result != null)
{
var offices = JsonConvert.DeserializeObject<Office[]>(e.Result);
theOffices = offices.ToList();
int id = 0;
foreach(Office office in offices)
{
_officeLookup.Add(id,office);
id++;
}
}
}
catch (Exception ex)
{
//Error handling here
}
}
We are supplying a list of office specific information to the phone client via a JSON query.
Calling Method:
using System.Net.WebClient;
using Newtonsoft.Json.JsonConvert;
private static Dictionary<int, Office> _officeLookup;
private static List<Office> theOffices = new List<Office>();
WebClient webClient = new WebClient();
webClient.Headers["Accept"] = "application/json";
webClient.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
webClient.DownloadStringAsync(new Uri(apiUrl));
private void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e);
{
try
{
if(e.Result != null)
{
var offices = JsonConvert.DeserializeObject<Office[]>(e.Result);
theOffices = offices.ToList();
int id = 0;
foreach(Office office in offices)
{
_officeLookup.Add(id,office);
id++;
}
}
}
catch (Exception ex)
{
//Error handling here
}
}
Thursday, July 10, 2014
Using progress indicator in windows phone systemtray
I am going to add a progress bar to my existing phone app and found a good explanation at Microsoft's site. It includes an example for implementing the progress bar programmatically or
declaratively.
Code Behind
using Microsoft.Phone.Shell;
namespace SystemTrayTest
{
public partial class MainPage : PhoneApplicationPage
{
ProgressIndicator prog;
public MainPage()
{
InitializeComponent();
SystemTray.SetIsVisible(this, true);
SystemTray.SetOpacity(this, 0.5);
SystemTray.SetBackgroundColor(this, Colors.Purple);
SystemTray.SetForegroundColor(this, Colors.Yellow);
prog = new ProgressIndicator();
prog.IsVisible = true;
prog.IsIndeterminate = true;
prog.Text = "Click me...";
SystemTray.SetProgressIndicator(this, prog);
}
}
}
XAML
<shell:SystemTray.ProgressIndicator>
<shell:ProgressIndicator IsIndeterminate="True" IsVisible="True" Text="Click me..." />
</shell:SystemTray.ProgressIndicator>
Source:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff626537(v=vs.105).aspx
declaratively.
Code Behind
using Microsoft.Phone.Shell;
namespace SystemTrayTest
{
public partial class MainPage : PhoneApplicationPage
{
ProgressIndicator prog;
public MainPage()
{
InitializeComponent();
SystemTray.SetIsVisible(this, true);
SystemTray.SetOpacity(this, 0.5);
SystemTray.SetBackgroundColor(this, Colors.Purple);
SystemTray.SetForegroundColor(this, Colors.Yellow);
prog = new ProgressIndicator();
prog.IsVisible = true;
prog.IsIndeterminate = true;
prog.Text = "Click me...";
SystemTray.SetProgressIndicator(this, prog);
}
}
}
XAML
<shell:SystemTray.ProgressIndicator>
<shell:ProgressIndicator IsIndeterminate="True" IsVisible="True" Text="Click me..." />
</shell:SystemTray.ProgressIndicator>
Source:
http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff626537(v=vs.105).aspx
Wednesday, July 2, 2014
How to load XML file located inside the folder of the application in window phone 8
Problem:
I have some onboard xml files which I needed to load into memory. I was puzzling over how to load them into memory for use in my app. I tried using XDocument library but I kept getting the error:
"Cannot find file '/XML Files/A.xml' in the application xap package.
XDocument doc = XDocument.Load( "XML Files/MyXmlFile.xml" );
This was not working. I tried several variations but still was getting the error every time I ran to code.
Solution:
It turns out that my syntax was not the problem. I needed to set the properties on the xml file correctly.
1.)
2.)
Source:
http://stackoverflow.com/questions/4538874/how-to-load-xml-file-located-inside-the-folder-of-the-application-in-window-phon
I have some onboard xml files which I needed to load into memory. I was puzzling over how to load them into memory for use in my app. I tried using XDocument library but I kept getting the error:
"Cannot find file '/XML Files/A.xml' in the application xap package.
XDocument doc = XDocument.Load( "XML Files/MyXmlFile.xml" );
This was not working. I tried several variations but still was getting the error every time I ran to code.
Solution:
It turns out that my syntax was not the problem. I needed to set the properties on the xml file correctly.
1.)
Build Action=C
ontent
2.)
Copy to output=
Copy if newer
(or Copy always
).Source:
http://stackoverflow.com/questions/4538874/how-to-load-xml-file-located-inside-the-folder-of-the-application-in-window-phon
Subscribe to:
Posts (Atom)