Monthly Archives: April 2013

SharePoint Language specific CSS loading

Today I worked on a annoying problem where I had to figure out how to do a language specific CSS file loading during header load from a Master Page.

The solution is as following in our case:

Our code deploys under the LAYOUTS folder our project style sheets, js files etc. We created for each language ID it’s own folder and under each language folder the css file that we wanted to use for a each language.

Next we addedthe following line inside the <HEAD> element and the right CSS file based on the language ID was loaded:

<SharePoint:CssRegistration name=”<% $SPUrl:/_layouts/projectname/styles/~language/master.css %>” runat=”server”/>

In the definition above what is in bold and in blue is what you MUST add for the reference to work. What is in green and not in bold is your path where your CSS file is located. Change it to fit your needs.

What is the most interesting part is the “~language” definition in the URL. This will retrieve the proper language ID(example for English-US: en-US). The final URL reference will look something like this:

/_layouts/projectname/styles/en-US/master.css

C# .NET – Displaying debug information in Debug compilation

If you want to output extra infromation in your code to see what is going on in problem situations and you only want to include it in debug compilations and not in Release version you can add a “Debug” Conditional attribute to a function and use that function to output data into your system output. There are many ways but this is a simple and straightforward way to do it:

[Conditional(“DEBUG”)]
private void DebugInfo(String info)
{
#if DEBUG
Response.Write(“</br>” + info);
#endif
}

The function and it’s content will be left out in Release compilation.

How to deal with a single long continuous word in CSS and HTML

If you have a very long piece of text that has no white spaces or anything that would make the word into smaller bits or information this will likely cause problem when rendered by the browser. Might cause your content to explode width wise.

In one on my problem cases I was creating a custom XSLT to display email related data. One of the data was the To information of emails which holds the recipients of an email. If the email had lots of recipient they where one after another in a single continuous string. This caused a problem when the data was retrieved from the CMS system and displayed to the user. Width wise everything exploded.

What we did in our case the had a table into which the data was inserted. The TD element was modified so that the content would be scroll able and forced to a certain width. This may help someone if you have a similar problem.

<td style=”WIDTH: OVERFLOW: hidden” class=”ms-vb”>
<span style=”DISPLAY: block;WIDTH: 257px; OVERFLOW: auto”>
<xsl:value-of select=”fieldname”/>
</span>
</td>

Hiding Custom SharePoint fields from edit mode, new window or display mode

A quick post:

Lets say that you do not want to allow users to possibility to edit and insert values to a custom field in one of your lists. To do this you can set the values of one of the following properties of a SPField object:

  1. ShowInEditForm
  2. ShowInDisplayForm
  3. ShowInNewForm

You can do this programmatically at any time, below is a link on how to do it:

http://www.sharepointdynamics.net/2011/09/how-to-hide-a-sharepoint-list-column-from-a-list-form-new-edit-and-display/


#Get the web and site column objects
$web = Get-SPWeb http://portal
$column = $web.Fields["Test Column"]

#Set the PushChangesToLists property for the changes to be applied
#to lists where the column has already been added
$column.PushChangesToLists = $true

#Change the ShowInEditForm and other properties and update objects
$column.ShowInEditForm = $false
$column.ShowInNewForm = $false
$column.ShowInDisplayForm = $true
$column.ShowInViewForms = $true

$column.Update()
$web.Update()
$web.Dispose()

Remember to check that the change is applied to the list field also. If not then do the change directly to the list field.

 

Or by XML definition:

<Field Type=”Number” ShowInEditForm=”FALSE” ShowInNewForm=”FALSE” DisplayName=”Counter” Required=”FALSE” EnforceUniqueValues=”FALSE” Indexed=”FALSE” Group=”My Group” ID=”{7f111eff-569d-4852 8bf2-ba0fcee6c69a}” SourceID=”{b1498d8e-4ad9-4d0d-8584-53d9a5de8c11}” StaticName=”Counter” Name=”Counter”>
<Default>0</Default>
</Field>

Links to MSDN about the properties:

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfield.showineditform.aspx

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfield.showindisplayform.aspx

http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfield.showinnewform.aspx

SharePoint Modified field gone crazy or it just won’t work :)

The problem is very simple in this case. The modified field does not work anymore. When a document or any list item is modified the time stamp does not get updated as it should.

To fix this problem you need to do two things unfortunately.

  1. First you need to update the out of the box Modified field. You can do this by a manually modified URL to the edit page of fields. You have to do this because the field is visible by default. Below is a sample URL:

http://vsp2007dev:24327/_layouts/fldedit.aspx?field=Modified

Pay attention to the bold part of the URL.Just past the bold part after your application domain name and you will be fine. Next all you need is to simply press the “OK” button and be sure to leave everything else unchanged. Especially the update option must be set to yes. What this will do is simply go through every “Modified” field use in your SharePoint application and makes some updates to the field. This will fix the time stamp problem.

2. After you do the seconds step you will run into another problem that an xml attribute from the Modified field schema is missing. This needs to be fixed by a script. Below you will find the code that fixes this error AND also does the update procedure in step 1 described above. So no manual labor here :).

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.SharePoint;

using System.Xml;

using System.IO;

 

namespace ModifiedFieldFix

{

    public class Program

    {

        static void Main(string[] args)

        {

            StringBuilder output = new StringBuilder();

            TextWriter tw = new StreamWriter(“LOGFILE_” + DateTime.Now.ToFileTime()  + “.txt”);

            if (args != null && args.Length > 0)

            {

                tw.WriteLine(“Given param: ” + args[0]);

                try

                {

                    using (SPSite msite = new SPSite(args[0]))

                    {

                        tw.WriteLine(“Site opened”);

                        SPField siteMField = msite.RootWeb.Fields[new Guid(“28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f”)];

                        siteMField.Update(true);

                        tw.WriteLine(“Modified field update request performed”);

                        foreach (SPWeb mweb in msite.AllWebs)

                        {

                            tw.WriteLine(“———————————————————————-“);

                            tw.WriteLine(String.Empty);

                            tw.WriteLine(“WEB UNDER PROCESSING: ” + mweb.Title);

                            tw.WriteLine(“WEB URL: ” + mweb.Url);

                            for (int i = 0; i < mweb.Lists.Count; i++)

                            {

                                try

                                {

                                    SPList mlist = mweb.Lists[i];

                                    SPField mfield = mlist.Fields[new Guid(“28cf69c5-fa48-462a-b5cd-27b6f9d2bd5f”)];

                                    tw.WriteLine(String.Empty);

                                    tw.WriteLine(“START OF LIST PROCESSING”);

                                    tw.WriteLine(“######################################################”);

                                    tw.WriteLine(“List Title: ” + mlist.Title + ” Field Title: ” + mfield.Title + ” FormBaseType ” + mfield.FromBaseType);

                                    tw.WriteLine(“List location:” + mweb.Url + “/” + mlist.RootFolder.Url);

 

                                    if (mfield.FromBaseType != true)

                                    {

                                        XmlTextReader xmlReader = new XmlTextReader(new StringReader(mfield.SchemaXml));

 

                                        XmlDocument xmlDocument = new XmlDocument();

 

                                        XmlNode xmlNode = xmlDocument.ReadNode(xmlReader);

 

                                        //xmlNode.Attributes[“Hidden”].Value = “FALSE”;

 

                                        XmlAttribute fromBaseType = xmlDocument.CreateAttribute(“FromBaseType”);

 

                                        fromBaseType.Value = “TRUE”;

 

                                        xmlNode.Attributes.Append(fromBaseType);

 

                                        tw.WriteLine(xmlNode.OuterXml);

 

                                        mfield.SchemaXml = xmlNode.OuterXml;

 

                                        mfield.Update();

                                    }

                                    tw.WriteLine(“######################################################”);

                                    tw.WriteLine(“END OF LIST PROCESSING”);

                                    tw.WriteLine(String.Empty);

                                }

                                catch (Exception ex)

                                {

                                    tw.WriteLine(“Error in list procesing of the MODIFIED FIELD FIX!!”);

                                    tw.WriteLine(ex.Message);

                                    tw.WriteLine(ex.StackTrace);

                                }

                                finally

                                {

                                    if (mweb != null)

                                        mweb.Dispose();

                                }

                            }

                            tw.WriteLine(“———————————————————————-“);

                            tw.Flush();

                        }

 

                    }

                }

                catch (Exception ex)

                {

                    tw.WriteLine(ex.Message);

                    tw.WriteLine(ex.StackTrace);

                }

                tw.WriteLine(output.ToString());

                tw.WriteLine(“END OF PROGRAM”);

                tw.Flush();

                tw.Close();

                Console.Write(output.ToString());

            }

        }

    }

}

SharePoint PowerShell access and command execution problems

I ran into a problem some time ago and only today I found a solution to this problem.

The problem was when I tried to run a powershell script as a user who is not an SharePoint admin. In other words the problem is related to not having sufficient privileges to run scripts against SharePoint in powershell.

The problem can be solved by using the following command:

Get-SPDatabase | Add-SPShellAdmin SomeDomain\SomeUserName

Lost files in Sharepoint Recycle Bin that can not be restored

This time I am writing a post about a problem that a client of mine had.

The problem is rather simple BUT required allot of time and energy to fix the problem or rather in my case to recover the items:

The client at some point by someone deleted files which ended up in the recycle bin. Later the client wanted to restore these files BUT was unable to. Sharepoint was telling that the restore operation failed even though you where able to clearly see that the files where available to restore in the Recycle bin of the site.

NOTICE: Before you start to try any of the things written in this post take notice TO NOT DO these things in you client product/live environment. If you chose to do so you will cause your client to lose their MS support. MS does not support manipulation of the content databases directly to SQL Server. So take a database backup, restore it in a test or development environment and fix you problem there.

NOTICE: Use this code at your own risk. The code does some crazy stuff directly to SharePoint content database. I will not take any responsibility on anything that you do with the application and/or any part of it and it’s code.

Here are the steps which I ended up taking and a more detailed explanation on them:

  1. Trying to restore the items through the object model
  2. Investigating the Sharepoint content database
  3. Trying to restore the files by fixing content database Recycle Bin table missing information in the files
  4. Trying to recover the files directly from the AllDocsStream table

Lets start from the first item and advance from there:

1. Trying to restore the items through the object model

This is basically the same as the UI BUT I decided to give it a quick try since nothing from the UI worked and sometimes you can do things through code that you can not do through the UI.

Long story short the object model did not offer much help and any restore operation failed again or nothing happened. For this a created a forms application(code is included at the end of this post).

Here is how the final version of the application looks like: appppp

In this first step I create the functionality in the “Blue” area. These where the object model fixes which did not help.

There basic functionality are provided which are in the “Blue” area:

  1. Restore the selected items from recycle bin
  2. Delete selected items
  3. And move them to the Site Collection Recycle Bin

2. Investigating the Sharepoint content database

At this point because the object model and the UI did not do what they are supposed to I simply gave up on using the “preferred” way of Microsoft and went directly into the content database. My first destination was the “Recycle Bin” table.

After a few moments of careful examination of the clients content database table I noticed that the documents that can not be restored have 4 table fields which are null while other data row in the table all have data in them.

The fields which where null where:

  1. ListId
  2. ListItemId
  3. AuthorId
  4. ListDirName

So I decided to create a code that would restore the missing values. I decided that the AuthorId and ListItemId would be defined by me during the code(since they could be anything any existing user and any integer number that is large enough not to conflict with existing ID enumeration in the target list) and the ListId and ListDirName are to be retrieved through the object model. This had to be done by retrieving the values of the following fields WebId, SiteId and DirName. In the code used these values to get the site, web and the list. After I got the list it was easy to just update the table with the list ID and the rootfolder ServerRelative URL into the ListDirName field.

You can find the code for this in the button named: “Repair Recycle Bin Items(DB)”

3. Trying to restore the files by fixing content database Recycle Bin table missing information in the files

Unfortunately what I did in step two was not enough because the sites and lists did not exist anymore for these deleted files that needed to be restored from the bin.

The next step was to try to update the following tables so that the document/file would be restored to a different web and list that actually exists.

I had to make modifications to the following tables:

  1. AllDocs = Data regarding the file/document itself, where it is, it’s parent, state etc
  2. AllDocStreams = The actual binary data of the file
  3. RecycleBin = The data of a deleted file that has ended up in the recycle bin

By retrieving the DocId value from RecycleBin table from a file that can not be restored you can correlate to the documents information in the AllDocs and AllDocStream.

This step is very complex and I due to time constraints I did not get it to work properly. Some piece of data was missing to get the file restored to another location. I managed to get the file restored BUT while the list showed that it had files in it you could not access the file. So something was missing from somewhere.

You can check the code for this in the button: “Repair Recycle Bin items(DB) to inserted URL”

4. Trying to recover the files directly from the AllDocsStream table

Finally the last step was to go directly into the AllDocsStream with ADO .NET, read the binary data, retrieve the documents real name from the RecycleBin table and write that data into the hard drive.

This was SUCCESSFUL!!! It worked like a charm and the client was happy to get their files restored.

In the AllDocsStrems table the most important field is the “Content” field. This is the actual binary data that you need to retrieve and use it to write it into your hard drive.

From the RecycleBin table you need the value from the LeafName field.

Using these two piece of information you can reconstruct the file.

Here is the code that solved my problem, you can find it behind the button: “Recover files from DB”

            

String connectionString = ConfigurationSettings.AppSettings[“DBConnectionString”];

            SqlDataReader rdr = null;

            this.textBox2.Text += “Opening connection to DB”;

            using (SqlConnection connection = new SqlConnection(connectionString))

            {

                connection.Open();

                this.textBox2.Text += “Connection opened”;

                List<RecycleBinDBItem> items = new List<RecycleBinDBItem>();

                try

                {

                    // Get all such items which can not be restored properly from the UI or by object model

                    SqlCommand cmd = new SqlCommand(“SELECT * FROM RecycleBin WHERE ListDirName IS NULL”, connection);

 

                    rdr = cmd.ExecuteReader();

                    this.textBox2.Text += “Select clause executed”;

 

                    while (rdr.Read())

                    {

                        this.textBox2.Text += “Reading line: \n”;

                        this.textBox2.Text += rdr[“DirName”].ToString();

                        items.Add(new RecycleBinDBItem(rdr[“DocId”].ToString(), rdr[“LeafName”].ToString()));

                    }

                }

                finally

                {

                    // close the reader

                    if (rdr != null)

                    {

                        rdr.Close();

                    }

                    int itemIDCounter = 5000;

                    int authorID = 1;

                    // Loop through every recycle bin items that is missing needed field data from the RecycleBin table to be restored properly

                    foreach (RecycleBinDBItem item in items)

                    {

                        try

                        {

                            SqlCommand cmd = new SqlCommand(String.Format(“SELECT Content FROM AllDocStreams WHERE Id = ‘{0}'”, item.docID ), connection);

 

                            // 2. Call Execute reader to get query results

                            byte [] bArray = (byte[])cmd.ExecuteScalar();

                            // Craete the file and write the binary data from the Database

                            FileStream fs = File.Create(item.fileName, (int)bArray.Length);

                            fs.Write(bArray, 0, bArray.Length);

                            fs.Flush();

                            fs.Close();

                        }

                        catch (SqlException sqlex)

                        {

                            this.textBox2.Text += sqlex.Message + ” #######\n” + sqlex.StackTrace;

                        }

                        catch (Exception ex)

                        {

                            this.textBox2.Text += ex.Message + ” #######\n” + ex.StackTrace;

                        }

                        finally

                        {

                            if (rdr != null)

                            {

                                rdr.Close();

                            }

                        }

                    }

                }

            }

            this.textBox2.Text += “DB operations done!”;

Final words;

Hope this might help someone else who is suffering from this problem. The application and it’s code is not the prettiest BUT I did what I could in the time that was given to me and also what the client is ready to spend to solve this problem. It’s only a tool and should be treated as such.

FINAL NOTICE: Use this code at your own risk. The code does some crazy stuff directly to SharePoint content database. I will not take any responsibility on anything that you do with the application and/or any part of it and it’s code.

Check out the CodePlex Project for the code:

https://recyclebinrestoreapp.codeplex.com/

Testing for software developers – Thoughts

Today I had conversation with a few developers and we discussed the issue of testing in situations where there is no dedicated testes or testing manager OR someone who would test what you are doing besides yourself.

What to do?

Well with SharePoint there are lots of different things to test which complicate things. Things like TDD are not necessarily useful with SharePoint or similar environments where you are creating small apps for a ECM or CMS products like SharePoint. It can be useful BUT in my experience it needs more complex application logic to or other lets say external databases that need to be integrated in a clients SharePoint application. Webparts are usually rather small in logic on functionality.

Here is a checklist on how to improve your testing as a developer and minimize the risk of bugs and errors in your application:

  • Categorize your “testing plan” based on functionality in different parts of your application. For Example: Testing a List Event in SharePoint can have certain steps you want to take. These steps are most likely to stay the same next time you do a similar list event.
  • For each your “testing plan” write down what you had to test to ensure that what you where working on performed without problems. These things might even come up as a bug later on while the client tests your new work.

This is a over simplified way of looking at things BUT if you have a checklist for different things you know you are most likely to encounter then every time you work on your code you can go through your checklist and see if you have included into your code similar steps to avoid repeating your errors from the past. Saves time and energy.

I know there are all kinds of “best practices”, terms and procedure etc for testing and quality assurance but this is just meant for a developer for your own personal needs. Especially in situations where there are no quality assurance steps available, no testing or similar possible.

The main point of this article: It is always good to spend a few moment to think how one can use what one has  learned and put it to use later to make your life easier. Less stress more fun :)!

SharePoint quick search does not trigger / work in Chrome

I ran into a problem with Chrome last week. The problem is as following:

When you type a search term in the SharePoint Quick Search Box and press enter this should cause a search to be performed. This works great on Internet Explorer and FireFox BUT on Chrome this does not work. The problem in Chrome is that you have to press the enter key TWICE before a search if performed.

Well as usual with SP and weird problems I could not find a reasonable solution related to SP and since this was a Chrome problem I decided to do the “normal” and go around the problem.

How to do it?

Findthe input text box using JQuery and “force” an enter key press in that box. This for some reason “re-activates” the key pressing event of the text box  In my tests to see what is wrong with the text box in the SP quick search I found that in Chrome no key pressing events launched. The forcing of enter reinstates the key pressing events to the text box.

Here is the JQuery that will fix your problem. Replace the JQuery search terms to your search box for this to work:

// To fix Chrome enter pressing when trying to search something the first enter does not work
$j(‘input[name*=”InputKeywords”]’).focus(function () {
// This will refocus the text box and the key press event will fire normally
$j(this).trigger({
type: ‘keypress’,
which: 13
});
});

Sharepoint search results are incomplete or not working

I decided to write a few words about this problem. Mostly on a couple of a more weird situations.

This is a broad topic and I am sure people have many different kinds of problems with the SharePoint search but I’ll write here a couple of them that I encountered and have not found a clear answer yet on the web(well last time I checked 🙂 ).

Problem number 1 – Search results point to a different domain name:

This problem I first encountered a few years back. The client had created their application under a certain domain name and later they wanted to use another one. Well things were configured so that overall everything worked except search results kept pointing to the old domain name even though we indexed using the new domain name.

The problem turned out to be related to SharePoint settings in Alternate Access Mapping. Once updated the application default zone to use the new domain name search results showed up correctly. Part of the problem was created by the clients request to still keep the old domain name in use but this is another story. Eventually the clients application ended up using only one domain name.

So if you encounter problems with your search results pointing to odd places it should not point then check your AAM, check your bindings in IIS and definitely be sure that the DNS server is configured properly.

Problem number 2 – Refinement configurations do not show results when filtering by site:

This is a problem I am working currently on right now so I’ll be updating this one soon BUT it seems that after many attempts to fix this issues everything seems to be OK everywhere EXCEPT I am thinking of HTTPS. The filtering is working for sites that do not have HTTPS enabled BUT does not work on the site with HTTPS enabled. So if you have a similar problem you might want to check your search settings and overall settings regarding your SharePoint application related to HTTPS.

But I’ll be back on this one later.