Asif Maniar

Software Engineer
posts - 18 , comments - 16 , trackbacks - 0

Tuesday, August 28, 2012

Adding Facebook Open Graph Tags to an MVC Application

If you have any kind of share functionality within your application it’s a good practice to add
the basic Facebook open graph tags to the header of all pages.

For an MVC application this can be as simple as adding these tags to the Head section of the Layouts file.

<head>
    <title>@ViewBag.Title</title>
    <meta property="og:title" content="@ViewBag.FacebookTitle" />
    <meta property="og:type" content="website"/>
    <meta property="og:url" content="@ViewBag.FacebookUrl"/>
    <meta property="og:image" content="@ViewBag.FacebookImage"/>
    <meta property="og:site_name" content="Site Name"/>
    <meta property="og:description" content="@ViewBag.FacebookDescription"/>
</head> 
 
These ViewBag properties can then be populated from any action:
 
private ActionResult MyAction()
    {
        ViewBag.FacebookDescription = "My Actions Description";
        ViewBag.FacebookUrl = "My Full Url";
        ViewBag.FacebookTitle = "My Actions Title";
        ViewBag.FacebookImage = "My Actions Social Image";
        ....
    }
 
You might want to populate these ViewBag properties with default values when the actions don’t populate them. 
This can be done in 2 places.
 
1. In the Layout itself. (check the ViewBag properties and set them if they are empty)
 
@{
    ViewBag.FacebookTitle = ViewBag.FacebookTitle ?? "My Default Title";    
ViewBag.FacebookUrl = ViewBag.FacebookUrl ?? HttpContext.Current.Request.RawUrl; ViewBag.FacebookImage = ViewBag.FacebookImage ?? "http://www.mysite.com/images/logo_main.png"; ViewBag.FacebookDescription = ViewBag.FacebookDescription ?? "My Default Description"; }
 
2. Create an action filter and add it to all Controllers or your base controller.
 
public class FacebookActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var viewBag = filterContext.Controller.ViewBag;

            viewBag.FacebookDescription = "My Actions Description";
            viewBag.FacebookUrl = "My Full Url";
            viewBag.FacebookTitle = "My Actions Title";
            viewBag.FacebookImage = "My Actions Social Image";

            base.OnActionExecuting(filterContext);
        }
   }
 
Add attribute to your BaseController.
 
[FacebookActionFilter]
public class HomeController : Controller
 {
  ....
 }
 

Posted On Tuesday, August 28, 2012 4:13 PM | Comments (0) |

Wednesday, March 21, 2012

Tricks and Optimizations for you Sitecore website

When working with Sitecore there are some optimizations/configurations I usually repeat in order to make my app production ready.

Following is a small list I have compiled from experience, Sitecore documentation, communicating with Sitecore Engineers etc.

This is not supposed to be technically complete and might not be fit for all environments.

 

Simple configurations that can make a difference:

1) Configure Sitecore Caches. This is the most straight forward and sure way of increasing the performance of your website.

Data and item cache sizes (/databases/database/ [id=web] ) should be configured as needed. You may start with a smaller number and tune them as needed.

<cacheSizes hint="setting">
          <data>300MB</data>
          <items>300MB</items>
          <paths>5MB</paths>
          <standardValues>5MB</standardValues>
  </cacheSizes>

Tune the html, registry etc cache sizes for your website.

 

<cacheSizes>
      <sites>
        <website>
          <html>300MB</html>
          <registry>1MB</registry>
          <viewState>10MB</viewState>
          <xsl>5MB</xsl>
        </website>
      </sites>
    </cacheSizes>

Tune the prefetch cache settings under the App_Config/Prefetch/ folder.

Sample /App_Config/Prefetch/Web.Config:

<configuration>
  <cacheSize>300MB</cacheSize>
  <!--preload items that use this template-->
  <template desc="mytemplate">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</template>

  <!--preload this item-->
  <item desc="myitem">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX }</item>

  <!--preload children of this item-->
  <children desc="childitems">{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</children>
</configuration>

Break your page into sublayouts so you may cache most of them.

Read the caching configuration reference: http://sdn.sitecore.net/upload/sitecore6/sc62keywords/cache_configuration_reference_a4.pdf

 

2) Disable Analytics for the Shell Site

<site name="shell" virtualFolder="/sitecore/shell" physicalFolder="/sitecore/shell" 
rootPath="/sitecore/content" startItem="/home" language="en" database="core" domain="sitecore" 
loginPage="/sitecore/login" content="master" contentStartItem="/Home" enableWorkflow="true" 
enableAnalytics="false" xmlControlPage="/sitecore/shell/default.aspx" browserTitle="Sitecore" 
htmlCacheSize="2MB" registryCacheSize="3MB" viewStateCacheSize="200KB" xslCacheSize="5MB" />
 

3) Increase the Check Interval for the MemoryMonitorHook so it doesn’t run every 5 seconds (default).

<hook type="Sitecore.Diagnostics.MemoryMonitorHook, Sitecore.Kernel">
        <param desc="Threshold">800MB</param>
        <param desc="Check interval">00:05:00</param>
        <param desc="Minimum time between log entries">00:01:00</param>
        <ClearCaches>false</ClearCaches>
        <GarbageCollect>false</GarbageCollect>
        <AdjustLoadFactor>false</AdjustLoadFactor>
      </hook>
 

4) Set Analytics.PeformLookup (Sitecore.Analytics.config) to false if your environment doesn’t have access to the internet or you don’t intend to use reverse DNS lookup.

<setting name="Analytics.PerformLookup" value="false" />
 

5) Set the value of the “Media.MediaLinkPrefix” setting to “-/media”:

<setting name="Media.MediaLinkPrefix" value="-/media" />

Add the following line to the customHandlers section:

<customHandlers>
  <handler trigger="-/media/" handler="sitecore_media.ashx" />
  <handler trigger="~/media/" handler="sitecore_media.ashx" />
  <handler trigger="~/api/" handler="sitecore_api.ashx" />
  <handler trigger="~/xaml/" handler="sitecore_xaml.ashx" />
  <handler trigger="~/icon/" handler="sitecore_icon.ashx" />
  <handler trigger="~/feed/" handler="sitecore_feed.ashx" />
</customHandlers>

Link: http://squad.jpkeisala.com/2011/10/sitecore-media-library-performance-optimization-checklist/

 

6) Performance counters should be disabled in production if not being monitored

<setting name="Counters.Enabled" value="false" /> 

 

7) Disable Item/Memory/Timing threshold warnings. Due to the nature of this component, it brings no value in production.

<!--<processor type="Sitecore.Pipelines.HttpRequest.StartMeasurements, Sitecore.Kernel" />--> 

<!--<processor type="Sitecore.Pipelines.HttpRequest.StopMeasurements, Sitecore.Kernel">
  <TimingThreshold desc="Milliseconds">1000</TimingThreshold> 
  <ItemThreshold desc="Item count">1000</ItemThreshold> 
  <MemoryThreshold desc="KB">10000</MemoryThreshold> 
</processor>—>
 

8) The ContentEditor.RenderCollapsedSections setting is a hidden setting in the web.config file, which by default is true. Setting it to false will improve client performance for authoring environments.

<setting name="ContentEditor.RenderCollapsedSections" value="false" /> 

 

9) Add a machineKey section to your Web.Config file when using a web farm. Link: http://msdn.microsoft.com/en-us/library/ff649308.aspx

 

10) If you get errors in the log files similar to:

WARN Could not create an instance of the counter 'XXX.XXX'

(category: 'Sitecore.System')

Exception: System.UnauthorizedAccessException

Message: Access to the registry key 'Global' is denied.

Make sure the ApplicationPool user is a member of the system “Performance Monitor Users” group on the server.

 

11) Disable WebDAV configurations on the CD Server if not being used.

More: http://sitecoreblog.alexshyba.com/2011/04/disable-webdav-in-sitecore.html

 

12) Change Log4Net settings to only log Errors on content delivery environments to avoid unnecessary logging.

<root>
      <priority value="ERROR" />
      <appender-ref ref="LogFileAppender" />
    </root>
(From Sitecore: please note that this may result in Sitecore Support not being able to help you immediately due to log incompleteness. 
Logging reconfiguration might be necessary before
further investigation could be performed.)
 

13) Disable Analytics for any content item that doesn’t add value. For example a page that redirects to another page.

image

image

 

14) When using Web User Controls avoid registering them on the page the asp.net way:

<%@ Register Src="~/layouts/UserControls/MyControl.ascx" TagName="MyControl" TagPrefix="uc2" %>

Use Sublayout web control instead – This way Sitecore caching could be leveraged

<sc:Sublayout ID="ID" Path="/layouts/UserControls/MyControl.ascx" Cacheable="true" runat="server" />

 

15) Avoid querying for all children recursively when all items are direct children.

Sitecore.Context.Database.SelectItems("/sitecore/content/Home//*");

//Use:

Sitecore.Context.Database.GetItem("/sitecore/content/Home");
 

16) On IIS — you enable static & dynamic content compression on CM and CD

More: http://technet.microsoft.com/en-us/library/cc754668%28WS.10%29.aspx

image


 

17) Enable HTTP Keep-alive and content expiration in IIS.

image

 

18) Use GUID’s when accessing items and fields instead of names or paths. Its faster and wont break your code when things get moved or renamed.

Context.Database.GetItem("{324DFD16-BD4F-4853-8FF1-D663F6422DFF}")
Context.Item.Fields["{89D38A8F-394E-45B0-826B-1A826CF4046D}"];

//is better than

Context.Database.GetItem("/Home/MyItem")
Context.Item.Fields["FieldName"]

 

Hope this helps.

Posted On Wednesday, March 21, 2012 5:18 PM | Comments (0) |

Friday, February 10, 2012

A Simple implementation of the Decorator Design Pattern using C#

The main motivation before the decorator pattern is to be able to add data and behavior to objects dynamically
without relying on inheritance.

A decorator usually conforms to the interface of the component its decorating.
Lets see this with an example.

Am building a system that calculates the price of a pizza. I can have many toppings and the total price of
the pizza is calculated based on what toppings I pick. One way to do this would be to use inheritance and
class hierarchies like Pizza, CheesePizza, ChickenPizza etc…which can get very inflexible.

A better way is to implement this using decorators. Below PizzaWithCheese decorates
the Pizza and adds the price of cheese to the total and so does the PizzaWithChicken.
When executed you should see the following output:

Total for cheese pizza: 15
Total for chicken pizza: 16
Total for cheese + chicken pizza: 21

 class DecoratorPattern
    {
        private interface IPizza
        {
            int GetPrice();
        }
 
        private class Pizza : IPizza
        {
            public int GetPrice()
            {
                return 10;
            }
        }
 
        //Decorator 1
        private class PizzaWithCheese : IPizza
        {
            private IPizza _pizza;
            private int _priceofCheese;
 
            public PizzaWithCheese(IPizza pizza, int priceofCheese)
            {
                _pizza = pizza;
                _priceofCheese = priceofCheese;
            }
 
            public int GetPrice()
            {
                //get price of the base pizza and add price of cheese to it
                return _pizza.GetPrice() + _priceofCheese;
            }
        }
 
        //Decorator 2
        private class PizzaWithChicken : IPizza
        {
            private IPizza _pizza;
            private int _priceofChicken;
 
            public PizzaWithChicken(IPizza pizza, int priceofChicken)
            {
                _pizza = pizza;
                _priceofChicken = priceofChicken;
            }
 
            public int GetPrice()
            {
                //get price of the base pizza and add price of chicken to it
                return _pizza.GetPrice() + _priceofChicken;
            }
        }
 
 
        public static void Test()
        {           
            var pizzaWithCheese = new PizzaWithCheese(new Pizza(), 5);
            var pizzaWithChicken = new PizzaWithChicken(new Pizza(), 6);
 
            //pizza with chicken and cheese is easy to build now.
            //we can keep decotaring the base pizza with as many decorators as necessary at runtime
            var pizzaWithChickenAndCheese = new PizzaWithChicken(pizzaWithCheese, 6);
 
            Console.WriteLine("Total for cheese pizza: " + pizzaWithCheese.GetPrice());
            Console.WriteLine("Total for chicken pizza: " + pizzaWithChicken.GetPrice());
            Console.WriteLine("Total for cheese + chicken pizza: " + pizzaWithChickenAndCheese.GetPrice());
        }
    }

Posted On Friday, February 10, 2012 4:05 PM | Comments (1) |

Tuesday, February 7, 2012

A Simple implementation of the Proxy Design Pattern using C#

A proxy is an object that can be used to control creation and access of a more complex object thereby deferring the cost of
creating it until the time its needed.

Below is a simple implementation of the proxy pattern in C#. The ComplexProtectedExpensiveResource is private to the
ProxyContainer and cannot be instantiated by a client. The client creates an instance of the SimpleProxy class which controls
its access to the more complex and expensive to create ComplexProtectedExpensiveResource class.

Note that the ComplexProtectedExpensiveResource is created by the SimpleProxy instance only when needed and after
verifying that the client indeed has access to it.

 

namespace Patterns
{
    class ProxyContainer
    {
        private class ComplexProtectedExpensiveResource
        {
            internal void DoWork()
            {
                //do some heavy lifting
            }
        }

        // The Proxy
        public class SimpleProxy
        {
            ComplexProtectedExpensiveResource _complexProtectedResource;
            private string _password;

            public SimpleProxy(string password)
            {
                _password = password;
            }

            public void DoWork()
            {
                if (Authenticate())
                {
                    _complexProtectedResource.DoWork();    
                }
            }

            bool Authenticate()
            {
                //authenticate request
                if (_password == "password")
                {
                    //create expensive object if authenticated
                    if (_complexProtectedResource == null)
                        _complexProtectedResource = new ComplexProtectedExpensiveResource();
                    return true;
                }
                return false;
            }
        }
    }

    // The Client
    class ProxyPattern : ProxyContainer
    {
        static void DoWork()
        {
           var simpleProxy = new SimpleProxy("password");
           simpleProxy.DoWork();
        }
    }
}

Posted On Tuesday, February 7, 2012 4:02 PM | Comments (1) |

Monday, December 12, 2011

Using Sitecores pipeline to pre-populate item in current language

Sitecore has a powerful event pipeline infrastructure that you can leverage to plugin commands into various item creation, change, move, publish etc events.

Recently I had to add functionality to Sitecore so that when an items version is created all fields from a target language are copied into the newly created version to ease editing.

To do this first we add an event handler in the Web.Config file for the versionAdded event.

 <event name="item:versionAdded">
        <handler type="MyNamespace.MyClass, MyAssembly" method="OnVersionAdded" />
      </event>

 

Then add the OnVersionAdded event to the class added to the type attribute above.

The Method is as shown:

namespace MyNameSpace{
    public class MyClass
    {
        public void OnVersionAdded(object sender, EventArgs args)
        {
           //get item
            var item = Event.ExtractParameter(args, 0) as Item;
            try
            {
                if (item != null && item.Version.Number == 1)
                { 
                        //copy fields from fallback item
                        //we were using Language Fallback as explained below. Get whatever sourceItem you want to use 
                        var fallbackItem = FallbackLanguageManager.GetFallbackItem(item, false);

                        item.Editing.BeginEdit();
                        fallbackItem.Fields.ReadAll();
                        //copy over all fields from source to target language
                        foreach (Sitecore.Data.Fields.Field field in fallbackItem.Fields)
                        {
                            if (!field.Shared && !field.Name.StartsWith("__") && field.Name.Trim() != "")
                            {
                                item.Fields[field.Name].SetValue(field.Value, true);
                            }
                        }
                        item.Editing.EndEdit();
                        item.Editing.AcceptChanges();
                    }
                }
            }
            catch (Exception exception)
            {
                Sitecore.Diagnostics.Log.Error("Application Error", exception, this);
                if (item != null)
                { 
                    item.Editing.CancelEdit(); 
                }
            }
    }
}
 
Note that above code uses a FallbackLanguageManager to get the fallback item. 
This is basically the current item in the fallback language configured for the current language.
This is based on the Fallback provider released by Alex Shyba at Sitecore:
http://sitecoreblog.alexshyba.com/2010/11/approaching-language-fallback-with.html
 
If you are not using the fallback simply change the line to get the Item from the desired source language like:
var fallbackItem = item.Database.GetItem(item.ID, Language.Parse("en"));
 
Code for the FallbackLanguageManager (borrowed from Alex’s fallback provider source)
 
public class FallbackLanguageManager
    {
        public static Item GetFallbackItem(Item item, bool forceFallback)
        {
            var fallbackLanguage = GetFallbackLanguage(item.Language, item.Database);
            var enforcedFallbackLanguage = Language.Parse("en");

            if (fallbackLanguage != null && !String.IsNullOrEmpty(fallbackLanguage.Name) && !fallbackLanguage.Equals(item.Language))
            {
                return item.Database.GetItem(item.ID, fallbackLanguage, Version.Latest);
            }

            if (forceFallback && !item.Language.Equals(enforcedFallbackLanguage))
            {
                return item.Database.GetItem(item.ID, enforcedFallbackLanguage, Version.Latest);
            }

            return null;
        }

        public static Language GetFallbackLanguage(Language language, Database database)
        {
            var sourceLangItem = GetLanguageDefinitionItem(language, database);
            return sourceLangItem != null ? GetFallbackLanguage(sourceLangItem) : null;
        }

        public static Item GetLanguageDefinitionItem(Language language, Database database)
        {
            var sourceLanguageItemId = LanguageManager.GetLanguageItemId(language, database);
            return ID.IsNullOrEmpty(sourceLanguageItemId) ? null : ItemManager.GetItem(sourceLanguageItemId, Language.Parse("en"), Version.Latest, database, SecurityCheck.Disable);
        }

        protected static Language GetFallbackLanguage(Item langItem)
        {
            var fallbackLangName = langItem[SitecoreFields.FallbackLanguage];
            Language fallbackLang;
            return Language.TryParse(fallbackLangName, out fallbackLang) ? fallbackLang : null;
        }
    }
 
Hope this helps!

Posted On Monday, December 12, 2011 5:41 PM | Comments (0) |

Monday, October 24, 2011

Removing / Restricting Access to Sitecores Interface on Content Delivery Environments

Here are a few steps you can follow if you want to restrict access to Sitecore’s Interface on content delivery environments.

1) Open Internet Information Services (IIS) Manager

2) Expand your Content Delivery website

3) Click on the Sitecore folder

4) Double Click on Authentication in the middle content pane

 

image

 

5) In the Authentication Pane right click Anonymous Authentication and click Disable

image

 

6) Try visiting the Sitecore login page on the Content Delivery site and verify that you get an access denied exception

image

 

7) You might also want to copy the yourwebroot/sitecore/service folder into the webroot and update the paths in the web.config file for the various error pages.

 

Hope this helps.

Posted On Monday, October 24, 2011 5:40 PM | Comments (0) |

Monday, April 18, 2011

Mix 2011 Session List using OData, datajs and jQuery

At the recent Mix 2011 conference the datajs team talked about this new cross-browser JavaScript library that makes writing data centric web apps easier.

Its fairly simple to use and can be very powerful.

Here is a quick application I threw together using the library. The code uses datajs, jQuery, jQuery UI and jQuery templates.

Microsoft has an OData Service at http://live.visitmix.com/odata/Sessions which exposes all Mix sessions. I used this source to get a list of all sessions using the datajs library and bound them to a template using the templating library.

The below code uses the OData.read function to read the list of sessions and then binds the results to the sessionTemplate template.

//enable jsonp since this is a cross dom
OData.defaultHttpClient.enableJsonpCallback = true;
//read all sessions and bind to sessionsTemplate
OData.read("http://live.visitmix.com/odata/Sessions?$orderby=Title asc",
      function (data, request) {
       results = data.results;
              $("#sessionTemplate").tmpl(
              results,
              {
                  getTags: getTags
              }
).appendTo("#sessionList");
 

The getTags method is passed into the template so the tags can be loaded for each session when it renders.

function getTags(sessionId) {
       //get list of all tags for this session
       OData.read("http://live.visitmix.com/odata/Sessions(" + sessionId + ")/Tags",
        function (data, request) {
              //bind to the tagTemplate
              $("#tagTemplate").tmpl(data.results).appendTo("#" + sessionId);
         });
     return "";
 }
 

Here is the sessions template:

<script id="sessionTemplate" type="text/x-jquery-tmpl">
            <li><b><a href='http://channel9.msdn.com/events/mix/mix11/${SessionCode}' target="_blank">${Title}</a></b> <p>${Abstract}</p>
                <p id='${SessionID}'>
                                              //call the getTags method to get the tags html
                    Tags: {{html $item.getTags($item.data.SessionID)}}
                </p>
            </li>
        </script>
 
The Tags template: (links to channel9 page for that tag)
<script id="tagTemplate" type="text/x-jquery-tmpl">
   <a href='http://channel9.msdn.com/Events/MIX/MIX11?t=${TagValue}' target="_blank">${TagValue}</a>&nbsp;
</script>
 
The Tags section in the sessionList template invokes the getTags function passed to it which in turn makes a request using the OData library for all tags for the current session.

Example - tags for session with ID of 150: http://live.visitmix.com/odata/Sessions(150)/Tags

I also used the jQuery autocomplete plugin to create a autocompleted textbox so users can search for a session by title.

$("#searchTextbox").autocomplete({
  source: function (request, response) {
      response($.map(results, function (item) {
       if (item.Title.toLowerCase().indexOf($.trim(request.term.toLowerCase())) != -1) {
              return {
                        label: item.Title,
                        sessionId: item.SessionID
                      }
                  }
              }))
              },
        select: function (event, ui) {
        OData.read("http://live.visitmix.com/odata/Sessions(" + ui.item.sessionId + ")",
        function (data, request) {
                $("#sessionList").empty();
                $("#sessionTemplate").tmpl(data).appendTo("#sessionList");
                });
       }
}
 

The above code uses the source method of the autocomplete plugin to populate the list of sessions and then overrides the select method to bind to the selected session.

You can read more about the autocomplete plugin here.

Note: This isnt the most efficient way of loading data as it doesnt use paging, background fetching or local storage provided by HTML5 and datajs. Just wanted to demo the odata portion of the datajs library and how it can be used.

Hope to change this to use local storage in the future.

You may view the entire sample here: http://www.asifmaniar.com/Samples/Mix-2011-sessions-using-OData-and-datajs.aspx

 

 

 

 

 

 

Posted On Monday, April 18, 2011 5:16 PM | Comments (0) |

Monday, October 25, 2010

Copying Sitecore Items across Languages

When using multiple languages/cultures in the Sitecore CMS the content of an item isn’t usually copied over from an existing language to a new language version.

While working on a multi lingual website after content was added to the primary language (en-US) I had to write a script to copy all fields from the primary language into other languages (Example en-GB).
 
Here is some code that I used for Sitecore 6.2.
 
The following method copies an item from the Source Language to a Target Language provided that the source item has at least one version. Note that if the Target Item lacks a version one is created and only the custom fields (ones not starting with a __) are copied over.
void Copy(Sitecore.Data.Items.Item item, Sitecore.Globalization.Language sourceLanguage,
             Sitecore.Globalization.Language targetLanguage)
        {
            //get a reference to the master DB
            Sitecore.Data.Database masterDB = Sitecore.Configuration.Factory.GetDatabase("master");
          
            Sitecore.Data.Items.Item targetItem = masterDB.Items[item.ID, targetLanguage];
            Sitecore.Data.Items.Item sourceItem = masterDB.Items[item.ID, sourceLanguage];
 
            if (targetItem == null || sourceItem == null || sourceItem.Versions.Count == 0)
                return;
            //Disable the security context
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                try
                {
                    if (targetItem.Versions.Count == 0)
                    {
                  //add a version if none exist
                        targetItem = targetItem.Versions.AddVersion();
                    }
                    //edit item in target language
                    targetItem.Editing.BeginEdit();
                    sourceItem.Fields.ReadAll();
                    //copy over all fields from source to target language
                    //we omit internal fields which start with __
                    foreach (Sitecore.Data.Fields.Field field in sourceItem.Fields)
                    {
                        if (!field.Shared && !field.Name.StartsWith("__") && field.Name.Trim() != "")
                        {
                            targetItem.Fields[field.Name].SetValue(field.Value, true);
                        }
                    }
 
                    targetItem.Editing.EndEdit();
                    targetItem.Editing.AcceptChanges();
                }
                catch (Exception ex)
                {
                    targetItem.Editing.CancelEdit();
                    Response.Write(ex.Message);
                }
            }
           
The following method copies an item from a Source Language to a Target language. In this case two items can be different but are expected to have the same Fields.
 
 void Copy(Sitecore.Data.Items.Item sourceItem, Sitecore.Globalization.Language sourceLanguage,
            Sitecore.Data.Items.Item targetItem, Sitecore.Globalization.Language targetLanguage)
        {
            Sitecore.Data.Database masterDB = Sitecore.Configuration.Factory.GetDatabase("master");
 
            targetItem = masterDB.Items[targetItem.ID, targetLanguage];
            sourceItem = masterDB.Items[sourceItem.ID, sourceLanguage];
 
            if (targetItem == null || sourceItem == null || sourceItem.Versions.Count == 0)
                return;
 
            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                try
                {
                    if (targetItem.Versions.Count == 0)
                    {
                        targetItem = targetItem.Versions.AddVersion();
                    }
                    targetItem.Editing.BeginEdit();
                    sourceItem.Fields.ReadAll();
                    foreach (Sitecore.Data.Fields.Field field in sourceItem.Fields)
                    {
                        if (!field.Shared && !field.Name.StartsWith("__") && field.Name.Trim() != "")
                        {
                            targetItem.Fields[field.Name].SetValue(field.Value, true);
                        }
                    }
 
                    targetItem.Editing.EndEdit();
                    targetItem.Editing.AcceptChanges();
                }
                catch (Exception ex)
                {
                    targetItem.Editing.CancelEdit();
                    Response.Write(ex.Message);
                }
            }
 
        }
 
You can call the above methods after querying Sitecore’s Master DB for items you would like to clone.
protected void Import(object sender, EventArgs e)
        {
            try
            {
                Sitecore.Data.Database masterDB = Sitecore.Configuration.Factory.GetDatabase("master");
 
                Sitecore.Data.Items.Item[] items = Sitecore.Context.Database.SelectItems
                 ("/sitecore/content/Home/MySectionToCopy//*");
              
                //get source and target language for the copy
                Sitecore.Globalization.Language sourceLanguage = Sitecore.Data.Managers.LanguageManager.GetLanguage("en-US", masterDB);
                Sitecore.Globalization.Language targetLanguage = Sitecore.Data.Managers.LanguageManager.GetLanguage("en-GB", masterDB);
 
                if (sourceLanguage == null || targetLanguage == null)
                    return;
 
                foreach (Item item in items)
                {
                  //copy item from source to target language
                    Copy(item, sourceLanguage, targetLanguage);
                }
            }
            catch (Exception ex)
            {
                Response.Write(ex.Message);
            }
 
        }
 
Note: You also have the option to plug into Sitecore’s event pipeline and automatically copy a source language into the target language when a version is added.

Posted On Monday, October 25, 2010 5:20 PM | Comments (1) |

Wednesday, October 20, 2010

Windows 7 Phone Details for Developers

Attended the windows phone jump start event yesterday and had a chance to see some presentations and play with the new windows phone.

The Good:

  •  Xbox Live is integrated into the phone http://www.xbox.com/en-US/Live/Mobile/Home
  •  Free MS Office 2010 on the phone
  • Zune is integrated
  • GPS/Accelerometer/Camera (nothing new there)
  • All developer tools are free http://create.msdn.com/en-us/home/getting_started (comes with VS2010, Blend, XNA Studio and deployment tools)
  • .NET developers can write apps in C# & Silverlight using VS2010 and the free tools
  • Free XNA Studio for Game developers
  •  No multi tasking support but the Notifications API lets you send notifications to the phone from an external server and an application close event allows developers to save the applications state before exit
  • Launchers framework allows for fire and forget actions (like sending an SMS)
  • Choosers Framework lets you choose some piece of information (like a contact, photo or song) and drive it back to the application
  • Trial API available to offer trial of applications/games
  • Ad API available for Ad integration
  • $100/Year membership allows you to unlock 3 phones for testing apps, 5 free application submissions and unlimited paid app submissions
  • 30%-70% profit split between Microsoft and Developers for paid apps

 

Not so Good:

  • No copy/paste (might be in the next release)
  • The onboard Sql CE Server is closed to app developers for now (have to use isolated storage)
  • Bluetooth support is limited to headphones
  • Looks like you need a Win 7 / 2008 box to install the dev tools
  • No CDMA support yet (it's coming next year)

Links:

http://create.msdn.com

http://blogs.msdn.com/b/davedev/

http://www.microsoft.com/windowsphone/en-us/default.aspx

http://silverlight.codeplex.com/

http://channel9.msdn.com/blogs/egibson/windows-phone-7-jump-start-session-1-of-12-introduction

 

Posted On Wednesday, October 20, 2010 4:03 PM | Comments (1) |

Tuesday, October 12, 2010

Enabling HTTP Redirect in IIS7

If you want a virtual directory or a site in IIS7.x to redirect to another url you first have to make sure you have installed http redirect for IIS.

To do that goto Control Panel > Program and Features and select Turn Windows features on or off

 

Choose IIS and click on Add Role Services and make sure you check HTTP Redirection

 

 Let the feature be installed and configured.

Now you will see the HTTP Redirect option in the Features View of IIS.

Click HTTP Redirect and add the necessary redirect information to add a redirect for a virtual directory or an entire site.

 

Posted On Tuesday, October 12, 2010 4:00 PM | Comments (1) |

Powered by: