Archive

Archive for September, 2011

PHP: Redirection Overview

September 30th, 2011 No comments

    This post is more for me than for somebody else, because it will let me keep the posted information handy. WordPress is written in PHP and I have to recall this script language when I need to alter some iNove theme scripts. This time I craved for adding redirection to some script. The easiest way is to utilize code like the following:

header( "HTTP/1.1 301 Moved Permanently" ); // this line is optional and can be skipped
header( "Location: http://dotnetfollower.com" );

Using header(“Location: some new url“) method we can transfer user to a new page. Under the hood header() asks browser to make another request for another url. Note that the header() must be called before any text (e.g. HTML tags, blank lines, something forwarded by PHP echo function and so on) is sent to the user browser, otherwise it will not work. That is the major limitation of the given approach we have to be aware of.

The first line of the code sample above sends the redirect response code to the browser. Redirect response code is considered as a reason of redirection. You can skip the sending of redirect code. But if you delete or move page to another location, I insistently recommend to send a relevant code, because such codes are being analyzed practically by all Search Engines. Including, of course, the evident leader – Google Search Engine, which examines such codes very thoroughly. The Google PageRank of a page you redirect to can directly depend on the redirect code you return. For instance, in example above I return 301, which means that page was moved permanently to another location that is pointed out in the second line of code. The code 301 also tells Search Engines that link value of the requested url has to be given to the new one. In theory, the new page inherits PageRank of the requested (but absent at the time) page. In reality, it happens not instantly, but some time later.

Redirect response codes are a subset of status codes available in HTTP 1.1. The complete HTTP 1.1 specification is accessible in RFC 2616. The first digit of the status code defines the category of response. The status codes are grouped into five categories:

  • 100s – informational, indicate that request is received and is being processed. For example,

    • 100 Continue;
    • 101 Switching Protocols and etc.
  • 200s – success, indicate that request was successfully received, understood, and accepted. For example,

    • 200 OK;
    • 202 Accepted and etc.
  • 300s – redirection, indicate further action must be taken in order to complete the request, because a requested resource has been moved. Response Http-header usually includes a Location header indicating the new address. For example,

    • 301 Moved Permanently;
    • 303 See Other and etc.
  • 400s – client error, indicate that request contains bad syntax or cannot be fulfilled. For example,

    • 400 Bad Request;
    • 401 Unauthorized;
    • 404 Not Found;
    • 408 Request Time-out and etc.
  • 500s – server Error, indicate that server failed to fulfill an valid request. For example,

    • 500 Internal Server Error;
    • 502 Bad Gateway;
    • 503 Service Unavailable and etc.

Evidently, the most applicable category for redirection is a 300s status code. The full list of available status codes of this category is presented below:

  • 300 Multiple Choices;
  • 301 Moved Permanently;
  • 302 Found;
  • 303 See Other;
  • 304 Not Modified;
  • 305 Use Proxy;
  • 307 Temporary Redirect;

OK, let’s revert to the changes I made in one of the iNove theme scripts. Some external sites have the corrupted links to one of my articles – Silverlight for Windows Phone 7: How to bind UserControl to itself. These bad links look like ‘http://dotnetfollower.com/wordpress/2011/06/silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself/">http://dotnetfollower.com/wordpress/2011/06/silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself/</a>‘. As you can see, such link doubles the required url and contains a few improper symbols (quotes and Html-tags). Of course, when somebody goes through the link he receives 404 Not Found error. I decided to change 404.php so that it directs user to valid url of the article if the initial requested url contains substring ‘silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself’. The changes are below:

<?php 
$request=$_SERVER['REQUEST_URI'];

$pos = strpos($request, 'silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself');
if ($pos !== false)
{
	header( "HTTP/1.1 301 Moved Permanently" );
	header( "Location: http://dotnetfollower.com/wordpress/2011/06/silverlight-for-windows-phone-7-how-to-bound-usercontrol-to-itself/" );
	exit();
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head profile="http://gmpg.org/xfn/11">
	<meta http-equiv="Content-Type" content="<?php bloginfo('html_type'); ?>; charset=<?php bloginfo('charset'); ?>" />

	<title><?php bloginfo('name'); ?><?php wp_title(); ?></title>
	... <!-- some text is skipped -->
</head>

<body>
	<div id="container">
		... <!-- some text is skipped -->
		<div id="notice">
			<h1><?php _e('Welcome to 404 error page!', 'inove'); ?></h1>		
			... <!-- some text is skipped -->
		</div>
		<div class="fixed"></div>
	</div>
</body>
</html>

Note again, the redirecting is in the beginning of the file in order to prevent sending any text to the browser. That’s important.

OK then, let’s find out what alternative ways to make redirect we have.

The first alternative I know is an usage of Html-tag meta with an attribute http-equiv=”refresh”. Meta refresh is a legacy approach, which instructs a web browser to refresh the current page (or frame) after a certain interval. In addition it can prompt the browser to go to a different url during the next page refreshing.

<meta http-equiv="refresh" content="5; url=http://dotnetfollower.com">

Or

echo '<meta http-equiv="refresh" content="5; url=http://dotnetfollower.com">';

The meta-tag redirects a browser to http://dotnetfollower.com after 5 seconds. Meta-tag has to be placed only inside the <head></head> region of Html markup. Inside other tags (e.g. <body></body>) it will be ignored. I think it’s a quite serious limitation.

The second alternative is a JavaScript redirect:

<script language="javascript">
	window.location.href='http://dotnetfollower.com';
</script>

Or

echo '<script language="javascript">';
echo 'window.location.href="http://dotnetfollower.com";';
echo '</script>';

This code makes browser go to another page. Such script-tag can be placed inside either the <header></header> or <body></body> regions of Html. It’s unlikely, but the JavaScript support can be turned off in browser. Obviously, in this case JavaScript redirection is unable to work as well.

So, let’s show a summary table:

Redirect method Description Limitations
Location header Sends a raw HTTP Location header to redirect a browser to a location different than requested url. It may follow the sending of a redirect response code, which acts as a reason of redirection Must be called before any actual output is sent

Example

    header( "Location: http://dotnetfollower.com" ); // directs user to the new location
    
Meta refresh Redirects a browser to a new location after a certain amount of seconds Has to be placed inside the <head></head> Html tags

Example

    <meta http-equiv="refresh" content="5; url=http://dotnetfollower.com">
    <!-- Directs the browser to the new location after 5 seconds -->
    
JavaScript redirect This code makes browser start loading another page Can be placed inside the <header></header> or <body></body> Html tags. It requires the JavaScript support is turned on in client browser

Example

    <script language="javascript">
        window.location.href='http://dotnetfollower.com';
    </script>
    

PS There is a function http_redirect. It’s a part of the PECL extension to PHP. Of course, to use http_redirect the extension has to be installed. I believe http_redirect uses Location header as well.

If you know other redirection methods in PHP, please don’t hesitate to reveal it here in the comments.

Related posts:

JavaScript: When parseFloat is really needed

September 22nd, 2011 No comments

     parseFloat function parses a passed string and returns a number with floating point. The given function determines whether the first character in the specified string is a number, then if it’s true, the function parses the string until it reaches the end of the number, and returns the number as a number, not as a string. In other words, parseFloat makes an explicit type conversion from a string to a proper number with floating point.

Today I’ve used this JavaScript function for the first time. On my page I extract some values from query string (window.location.search), then these values take part in some calculations. I’ve met with the fact that the calculation was absolutely wrong. Let’s see the next simplest formula:

var res = 90 + someVal;

someVal is a value extracted from query string. if someVal will be, for example, “30.555” (exactly with quotes), res will be “9030.555”. It’s kind of slightly unexpected result, though it’s absolutely explainable. JavaScript makes automatic type conversion when dealing with the operator ‘+’. If one operand is a string and the other is not, the other is converted to a string and the result is concatenated. Obviously, we have to turn string someVal into an appropriate number. parseFloat helps us out:

var someNumber = parseFloat(someVal);
var res = 90 + someNumber;

OK, now res is expectedly 120.555.

There is an alternative – Number():

var someNumber = Number(someVal);

I use parseFloat instead Number(), because in my case the value extracted from query string may contain some additional alphabetical characters. For example, I can get from query string something like “30.555oz.”. I’m too lazy to cut out the excess symbols, I prefer using parseFloat. Repeating what I said in the beginning of this article, parseFloat successfully parses a string, which starts with a string representation of a number. All symbols after last digit symbol will be ignored. That means that the string “30.555oz.” will be transformed to the number 30.555. Another string “3YQJGP7CCACF” will be transformed to 3 and so on. In contrast, Number() accepts only a valid string number without any additional symbols.

I’d like also to mention about another useful function – parseInt, which is logically associated with parseFloat. Good examples of its usage you can find here.

In addition I’d like to note that some people complain about a parseFloat rounding problem (e.g. here or here): when, for example, string “3.05” turns into number 3.04999999999…. The solution is to use the toFixed function, which formats a number to use a specified number of trailing decimals. toFixed accepts the number of digits after the decimal point.

var someNumber = parseFloat('3.05');
someNumber = someNumber.toFixed(2);

toFixed helps to overcome this rounding problem, however, for justice’ sake, I need to note that we may not always know how many digits after point our initial string number had. Therefore, the usage of toFixed function with an inappropriate number of digits may affect accuracy of calculations.

Categories: JavaScript Tags: , ,

SharePoint: How to add Content Type programmatically

September 16th, 2011 No comments

     To add a new Content Type through the SharePoint Object Model I’ve developed a few auxiliary methods and classes. The main class is ContentType class. It contains information used for specifying the required properties of the SPContentType object being added.

public class ContentType
{
    public string         ParentContentTypeName { get; set; }
    public string         Name                  { get; set; }
    public List<FieldRef> FieldRefs             { get; set; }

    public ContentTypeFormUrls   FormUrls       { get; set; }
    public ContentTypeProperties Properties     { get; set; }
    public SPContentTypeId       Id             { get; set; }
}

The FieldRefs property represents a collection of fields that have to be added to the Content Type. The other property names are self-descriptive.

The ContentType class makes reference to classes like FieldRef, ContentTypeFormUrls and ContentTypeProperties.

public class FieldRef
{
    public Guid?  ID                 { get; set; }
    public string Name               { get; set; }
    public string DisplayName        { get; set; }
    public bool?  IsRequired         { get; set; }
    public bool?  IsHidden           { get; set; }
    public bool?  IsReadOnly         { get; set; }

    public string Aggregation        { get; set; }
    public string Customization      { get; set; }
    public string PIAttribute        { get; set; }
    public string PITarget           { get; set; }
    public string PrimaryPIAttribute { get; set; }
    public string PrimaryPITarget    { get; set; }
    public string Node               { get; set; }
}

public class ContentTypeFormUrls
{
    public string DisplayFormUrl { get; set; }
    public string NewFormUrl     { get; set; }
    public string EditFormUrl    { get; set; }
}

public class ContentTypeProperties
{    
    public string Description                 { get; set; }
    public string Group                       { get; set; }
    public bool?  IsHidden                    { get; set; }
    public bool?  IsReadOnly                  { get; set; }
    public string NewDocumentControl          { get; set; }
    public bool?  RequireClientRenderingOnNew { get; set; }
    public bool?  IsSealed                    { get; set; }
}

The main method is AddContentType:

public static void AddContentType(SPWeb spWeb, SPList spList, ContentType contentType)
{
    // get appropriate parent content type
    SPContentType parentContentType = spWeb.AvailableContentTypes[contentType.ParentContentTypeName];
    if (parentContentType != null)
    {
        if (spList.ContentTypes[contentType.Name] == null)
        {
            spList.ContentTypesEnabled = true;

            // create new content type
            SPContentType spContentType = new SPContentType(parentContentType, spList.ContentTypes, contentType.Name);
            // set content type id
            SetContentTypeId(spContentType, contentType.Id);
            // set content type properties
            SetContentTypeProperties(spContentType, contentType.Properties);

            // add fields to conent type
            foreach (FieldRef fieldRef in contentType.FieldRefs)
            {
                if (spList.Fields.ContainsField(fieldRef.Name))
                {
                    SPField targetField = fieldRef.ID.HasValue ? spList.Fields[fieldRef.ID.Value] : spList.Fields.GetFieldByInternalName(fieldRef.Name);
                    if (targetField != null && !spContentType.Fields.ContainsField(fieldRef.Name))
                    {
                        SPFieldLink fLink = new SPFieldLink(targetField);
                        SetFieldRefProperties(fLink, fieldRef);
                        spContentType.FieldLinks.Add(fLink);
                    }
                }
                else
                    Console.Write(string.Format("Couldn't find field {0} in the list {1}", fieldRef.Name, spList.Title));
            }

            // set content type form urls
            SetContentTypeFormUrls(spContentType, contentType.FormUrls);

            // save changes
            spList.ContentTypes.Add(spContentType);
            spContentType.Update();
            spList.Update();
        }
        else
            Console.Write(string.Format("Content type {0} already exists in the list {1}", contentType.Name, spList.Title));
    }
    else
        Console.Write(string.Format("Couldn't find the parent content type {0}", contentType.ParentContentTypeName));
}

It utilizes SetContentTypeFormUrls, SetContentTypeProperties, SetFieldRefProperties and SetContentTypeId (you can read about the last method in detail in my other post – How to set Id to just created SPContentType).

public static void SetContentTypeFormUrls(SPContentType spContentType, ContentTypeFormUrls contentTypeFormUrls)
{
    if (contentTypeFormUrls == null)
        return;

    if (contentTypeFormUrls.NewFormUrl != null)
        spContentType.NewFormUrl = contentTypeFormUrls.NewFormUrl;

    if (contentTypeFormUrls.DisplayFormUrl != null)
        spContentType.DisplayFormUrl = contentTypeFormUrls.DisplayFormUrl;

    if (contentTypeFormUrls.EditFormUrl != null)
        spContentType.EditFormUrl = contentTypeFormUrls.EditFormUrl;
}

public static void SetContentTypeProperties(SPContentType spContentType, ContentTypeProperties cntProps)
{
    if (cntProps == null)
        return;

    if (cntProps.Description != null)
        spContentType.Description = cntProps.Description;
    if (cntProps.Group != null)
        spContentType.Group = cntProps.Group;
    if (cntProps.IsHidden != null)
        spContentType.Hidden = cntProps.IsHidden.Value;
    if (cntProps.IsReadOnly != null)
        spContentType.ReadOnly = cntProps.IsReadOnly.Value;
    if (cntProps.IsSealed != null)
        spContentType.Sealed = cntProps.IsSealed.Value;
    if (cntProps.NewDocumentControl != null)
        spContentType.NewDocumentControl = cntProps.NewDocumentControl;
    if (cntProps.RequireClientRenderingOnNew != null)
        spContentType.RequireClientRenderingOnNew = cntProps.RequireClientRenderingOnNew.Value;
}

public static void SetContentTypeId(SPContentType spContentType, SPContentTypeId contentTypeId)
{
    try
    {
        FieldInfo fi = typeof(SPContentType).GetField("m_id", BindingFlags.NonPublic | BindingFlags.Instance);
        if (fi != null)
            fi.SetValue(spContentType, contentTypeId);
        else
            Console.Write("Couldn't set content type id!");
    }
    catch(Exception ex)
    {
        Console.Write(string.Format("Couldn't set content type id! {0}", ex.Message));
    }
}

public static void SetFieldRefProperties(SPFieldLink fLink, FieldRef fieldRef)
{
    if (fieldRef == null)
        return;

    if (fieldRef.DisplayName != null)
        fLink.DisplayName = fieldRef.DisplayName;
    if (fieldRef.IsHidden != null)
        fLink.Hidden = fieldRef.IsHidden.Value;
    if (fieldRef.IsRequired != null)
        fLink.Required = fieldRef.IsRequired.Value;
    if (fieldRef.IsReadOnly != null)
        fLink.ReadOnly = fieldRef.IsReadOnly.Value;

    if (fieldRef.Aggregation != null)
        fLink.AggregationFunction = fieldRef.Aggregation;
    if (fieldRef.Customization != null)
        fLink.Customization = fieldRef.Customization;
    if (fieldRef.PIAttribute != null)
        fLink.PIAttribute = fieldRef.PIAttribute;
    if (fieldRef.PITarget != null)
        fLink.PITarget = fieldRef.PITarget;
    if (fieldRef.PrimaryPIAttribute != null)
        fLink.PrimaryPIAttribute = fieldRef.PrimaryPIAttribute;
    if (fieldRef.PrimaryPITarget != null)
        fLink.PrimaryPITarget = fieldRef.PrimaryPITarget;
    if (fieldRef.Node != null)
        fLink.XPath = fieldRef.Node;
}

Here is how you can use all of that stuff:

public void AddSomeContentType(SPWeb spWeb, SPList spList)
{
    ContentType newContentType = new ContentType()
    {
        ParentContentTypeName = "Item",

        Id   = new SPContentTypeId("0x0100078C8B39671A4532AB9C5AB6DCB388A6"), // id has to correspond to the parent content type
        Name = "some new content type name",

        Properties = new ContentTypeProperties() { Description = "super modern content type", Group = "List Content Types" },
        FormUrls   = new ContentTypeFormUrls() { DisplayFormUrl = "Lists/your list name/your custom page name.aspx" },
        FieldRefs  = new List<FieldRef>() 
            { 
                new FieldRef() { Name = "Title",  ID = new Guid("{fa564e0f-0b71-4ab7-b863-0177e6ddd247}"), IsRequired = false, IsReadOnly = true, DisplayName = "List Item Title" },
                new FieldRef() { Name = "Status", IsRequired = true, DisplayName = "List Item Status" },
                new FieldRef() { Name = "Involved_User", ID = new Guid("869963ef-9ca3-4ad7-a5f0-8fff724a6877"), DisplayName = "User Involved" } 
            }
    };

    AddContentType(spWeb, spList, newContentType);
}

Please note that you don’t have to fill out all properties of the auxiliary classes (ContentTypeProperties, ContentTypeFormUrls and FieldRef); the unspecified properties will be merely ignored, and the appropriate properties of SPContentType object will be left with their default values. Als,o pay attention to the fact that Content Type identifier has to contain the id of parent Content Type. Find out how to build a Content Type identifier recursively here.

SharePoint: How to set Id to just created SPContentType

September 15th, 2011 No comments

     When adding Content Type programmatically, sometimes you may need to set a certain Id to it, to ensure that other parts of the application can refer to the Content Type using a known identifier. If you use SharePoint 2010 you can get this done instantly, as SharePoint 2010 provides a handy SPContentType constructor, which accepts SPContentTypeId as parameter:

public SPContentType(SPContentTypeId contentTypeId, SPContentTypeCollection contentTypes, string name);

But if you use SharePoint 2007, you don’t have such a constructor or any built-in means to set the required identifier. Besides, the Id property of SPContentType appears to be read-only. After studying the SPContentType class with Reflector I’ve discovered that the Content Type id is stored in the private m_id property:

private SPContentTypeId m_id;

This means that we can use Reflection to set this property. Here is a special SetContentTypeId method I’ve implemented for doing that:

public void SetContentTypeId(SPContentType spContentType, SPContentTypeId contentTypeId)
{
    try
    {
        FieldInfo fi = typeof(SPContentType).GetField("m_id", BindingFlags.NonPublic | BindingFlags.Instance);                
        fi.SetValue(spContentType, contentTypeId);                
    }
    catch (Exception ex)
    {
        Console.Write(string.Format("Couldn't set content type id! {0}", ex.Message));
    }
}

The method accepts the instance of the SPContentType class and the required identifier as the instance of the SPContentTypeId class. Here is an example of use:

public void AddSomeNewContentType(SPWeb spWeb, SPList spList)
{
    SPContentType parentContentType = spWeb.AvailableContentTypes["some parent content type name"];
    if (parentContentType != null)
    {
        SPContentType spContentType = new SPContentType(parentContentType, spList.ContentTypes, "some new content type name");
        SetContentTypeId(spContentType, new SPContentTypeId("0x0100078C8B39671A4532AB9C5AB6DCB388A6")); // content type id you need has to be here, for example 0x0100078C8B39671A4532AB9C5AB6DCB388A6

        // set other properties of content type

        spList.ContentTypes.Add(spContentType);
        spContentType.Update();
        spList.Update();
    }
}

Please note that you must NOT use the SetContentTypeId method with existing built-in or earlier created Content Types. Otherwise, it may corrupt the SharePoint data integrity, especially when there are list items created based on the changed Content Type.

Note again, use the SetContentTypeId method ONLY immediately following the creation of a Content Type and ONLY before adding it to whatever collection of Content Types or before calling SPContentType.Update(). This is very important.

My next post describes how to add Content Type programmatically.