Monday, August 31, 2015

Sitecore logs - How to find errors and issues

Environment - Sitecore 8.0 rev. 150621

Hi all,

If you worked as a pure .NET developer and you moved to work with Sitecore you will get the same problems as I had few months back from now.

"How am I going debug this ???"
"Where are the errors ??"
"How can I find the errors on my site and any function ???"

Those are my questions when I start Sitecore. Since Sitecore is a product and it uses it's own dll files to perform it's internal tasks we can't debug the classes inside the Sitecore like we do in a pure .Net project. But Sitecore provide a way to take you out from these troubles. Sitecore log files.
You can find these log files on your data_folder/App_Data/Sitecore/logs

Sitecore uses Apache's log4net to create log files. You can have a read on log4net from here.

There are 3 types of log files

1. Standard log (log)
This is the most common log. This log will log errors, warnings, information, etc according to your settings.

            Verbose log
This logging feature is disabled by default. This feature is designed to provide search index configuration and provide more rich and more detailed version of the problem. You can enabled this feature by removing the '.example' from the 'Sitecore.ContentSearch.VerboseLogging.config.example' cofig files inside the {your_site_folder}/App_Config/Include

* Verbose Log should enable only on special scenarios and never run long period of time specially on production environments. Since verbose logger would creates an extremely large log file, it can cause to a performance issue.

2. Crawling log (Crawling.log)
Track information about indexing operations. (I'll talk about Sitecore Indexing in latter session)

Following shows an example of  a crawling log.

11704 13:22:13 INFO  [Index=social_messages_web] Initializing SitecoreItemCrawler. DB:web / Root:/sitecore/social/Messages
11704 13:22:13 INFO  [Index=sitecore_analytics_index] Initializing TimedIndexStrategy with interval '00:01:00'.
11704 13:22:13 INFO  [Index=sitecore_core_index] Initializing IntervalAsynchronousUpdateStrategy with interval '00:01:00'.
11704 13:22:13 INFO  [Index=sitecore_master_index] Initializing SynchronousStrategy.
11704 13:22:13 INFO  [Index=sitecore_web_index] Initializing OnPublishEndAsynchronousStrategy.
11704 13:22:13 FATAL [Index=sitecore_web_index] Initialization of OnPublishEndAsynchronousStrategy failed because event queue is not enabled.

By default the Crawling log is set to output logs on 'INFO' level.

3. Search log (Search.log)
Shows queries that were generated and run.

Following shows an example for a search log.

3724 13:47:58 INFO  ExecuteQueryAgainstLucene (sitecore_analytics_index): type:contact - Filter :
3724 13:47:58 INFO  ExecuteQueryAgainstLucene (sitecore_analytics_index): type:contact - Filter :
3724 13:47:58 INFO  ExecuteQueryAgainstLucene (sitecore_analytics_index): +contact.contactid:a2a2d13f8603400d864c7914aadd593d +type:visit - Filter :

To get the full level details of the content search you have to enable the 'Sitecore.ContentSearch.config' and change the logging level to 'DEBUG'.

You can find all the configurations related to these log files in the <log4net> section of the web.config file.
According to the requirement you can increase or decrease the level of the log. The following provides the logging levels minimum to high.

  1. OFF - nothing gets logged 
  2. FATAL
  3. ERROR
  4. WARN
  5. INFO
  6. DEBUG
  7. ALL - everything gets logged

* Your log levels can be changed when you enabled the Verbose logging.

Sitecore will create logs for each day. If you want fresh logs on a day, you can restart the site from Microsoft IIS Manager. Then Sitecore will create logs with date and number. (ex- log.20150828.132211.txt)

* If you are still like to see the .Net errors on the page when you are testing you can turn off the custom errors on the web.config file.

Change <customErrors mode="On"  /> to <customErrors mode="Off"  />




Tuesday, August 18, 2015

Sitecore Web Forms for Marketers Save Actions: The action does not exist

Environment - Sitecore 8.0 rev. 150621

Sitecore Web Forms for Marketers Save Actions are used to provide functionalities which need to perform when a certain form is getting submitted.


When dealing with the save actions on Sitecore 8 there is an issue with the graphical interface on save action management. Therefore some save action which removed from the form can be still exist in the save actions raw values.
You can find this by checking on the Sitecore log files. If there is a such issue, in the Sitecore logs you can find a error similar bellow.

6092 05:46:44 WARN  Web Forms for Marketers : The '{D866FC78-08F5-4AAA-975C-26E991B1D881}' action does not exist

Follow these steps to find this save action exists.

1. Go to your Sitecore instance and go to Web Forms for Marketers, go to the form which throwing the error and turn on the 'Raw values' from the 'view' tab


2. Copy the value (xml) of the 'Save Actions' field of the 'Submit' section to a notepad.

3. Copy the Sitecore item. (according to the log message the item is - {D866FC78-08F5-4AAA-975C-26E991B1D881})

4. Find the copied Sitecore item on the notepad.
    If the item is found on the notepad you have to remove it from that xml.

    Remove unwanted save action from xml.

    4.1. Create a new form.
    4.2. Add the other save actions on your form to the newly created form.
    4.3. Copy the value (xml) of the 'Save Actions' field of the 'Submit' section of the newly created form.
    4.4. Paste it to the 'Save Actions' field of the 'Submit' section form that throwing errors.
    4.5. Do the required changes to the save actions.













Monday, July 27, 2015

Sitecore - Add Visual Studio Project to existing sitecore instance

Sitecore - Add Visual Studio Project to existing Sitecore instance

Hi,

Some of you have created fresh Sitecore instance for check some Sitecore functionalities like Web Forms For Marketers, Email Campaign Manager, etc.
Now If you wan't to add some extended capabilities or you want to add your code you have to create a Visual Studio Project to this existing Sitecore instance.

This is how you can do it.
I assume that you have a Sitecore instance. I have created a fresh instance as SitecoreFresh. (If you don't have one and if you want to try this out you can easily create a Sitecore instance using the Sitecore Instance Manager : Sitecore Instance Manager)

1. Open Visual Studio.
2. Go to new project. Under Visual C# section select ASP.NET Web Application.
3. Give Name, Location and Solution name as you desire.
4. From template selection section select 'Empty' and select the 'Web Forms' check box. (This is because the Visual Studio will add the required references to your project.). Select Ok.



5.  Go to the location you create this project.
6. Copy the .sln file and paste it inside the folder where your Sitecore instance root.



7. Go again to the location where you create the project (Step 5).
8. Go inside the folder which contain the website.
9. Copy the 'Properties' folder and '.csproj' file and paste it inside the website folder of your fresh Sitecore instance.
10. Double click on the copied .sln file on your Sitecore fresh folder.
11. Visual Studio will complain the project cannot be load. This is because the opened sln file is belong to the project that you created at step 4.
12. Remove the broken project from the Solution Explorer.
13. Right click on the solution at Solution Explorer and Add Existing Project.
14. Select the csproj file on your fresh instance folder and Open it.
15. This will load your fresh Sitecore project to the Solution Explorer.
16. Go to references and add the Sitecore.Kernal.dll. (Create a lib folder and copy the dll to that folder and get it from there).
17. Include the folder and files that you want in the project using the 'Show All Files' function in Visual Studio solution explorer.



Ok, we are all done here. You are good to go.



Thursday, June 4, 2015

Get Sitecore items (child items) inside Sitecore folder.

1. Create a class and create a method with your required assess method.

         protected Database Database
        {
            get
            {
                return Sitecore.Context.Database;
            }
        }

         protected Item GetItem(Guid id)
        {
            if (Sitecore.Context.Item != null && Sitecore.Context.Item.ID.Guid.Equals(id))
            {
                return Sitecore.Context.Item;
            }
            else
            {
                return Database.GetItem(new ID(id));
            }
        }


2. Get the folder item

            Guid folder_guid = new Guid("{Your-item-id}");
            var folder_item = GetItem(folder_guid);


3. Loop through the items inside that folder

            if (folder_item != null)
            {
                foreach (Item childItem in folder_item.Children)
                {
                    //Do your logic with  the childItem
                }
            }



Wednesday, April 29, 2015

Sitecore - Item language

Sitecore 'items' and 'languages'

Hi all, in this post I like to discuss some little things that you might like to know when you are playing with the Sitecore items and langues.

1. Get the languages of an item.

Item currenItem = (Get the current item to the variable) - The way of getting the item can be different the method you are using and the parameters.

                    Language[] langs = currenItem.Languages;

2. Get the  item versions of an certain language

                    foreach (var lang in langs)
                    {
                        var langItem = currenItem.Database.GetItem(currenItem.ID, lang);
                        var vers = langItem.Versions.GetVersions();
                    }

3. Get the fields of an item

                     latest.Fields.ReadAll();   //This will provide all the fields in the item
                     var fields = latest.Fields;

Hope this is useful.

Monday, December 15, 2014

Sitecore - Custom Workbox to Show the latest version of an Item

Current Environment: Sitecore 7.2 rev 140526

Requirement:
Show the latest version of an item when there is more than one version of that item.



This is the item listing in the Sitecore workbox with the default settings.

















Our requirement is to make it like this:      

Show only the latest version of a item.
Show the number of versions of that item.


















Step 1:
Get a copy of the Workbox.xml from the ~\Website\sitecore\shell\Applications\Workbox folder

Step 2:
Paste the copied Workbox.xml inside ~\Website\sitecore\shell\Override folder

Step 3:
Change the
<CodeBeside Type="Sitecore.Shell.Applications.Workbox.WorkboxForm,Sitecore.Client"/> to
<CodeBeside Type="(Namespace of your solution).(dll name)"/>

Ex: <CodeBeside Type="Custom_Workflow.CustomWrokbox"/>

Step 4:
Import the Sitecore.Client reference to the solution

Step 5:
Inherit the WorkboxForm to your class

Ex:  class CustomWrokbox : WorkboxForm

Step 6:
Override the DisplayStates() behaviour.

Code

namespace My_Workflow
{
    class CustomWrokbox : WorkboxForm
    {
        private NameValueCollection stateNames;

        protected override void DisplayStates(IWorkflow workflow, XmlControl placeholder)
        {
            Assert.ArgumentNotNull((object)workflow, "workflow");
            Assert.ArgumentNotNull((object)placeholder, "placeholder");
            this.stateNames = (NameValueCollection)null;
            foreach (WorkflowState state in workflow.GetStates())
            {
                if (WorkflowFilterer.FilterVisibleCommands(workflow.GetCommands(state.StateID)).Length > 0)
                {
                    List<DataUri> filteredUriList = new List<DataUri>();
                    DataUri[] items = this.GetItems(state, workflow);
                    Assert.IsNotNull((object)items, "items is null");
                 
                    List<DataUri> latestItemList = new List<DataUri>();
                 
                    for (int index = 0; index < items.Count(); index++)
                    {
                        bool currentItemAdded = false;
                        Item currentItem = Sitecore.Context.ContentDatabase.Items[items[index]];

                        if (latestItemList.Count > 0)
                        {
                         
                            for (int i = 0; i < latestItemList.Count; i++)
                            {
                                if ((currentItem.ID == latestItemList[i].ItemID) && (currentItem.Version.ToInt32() > latestItemList[i].Version.ToInt32()) && (currentItem.Language == latestItemList[i].Language))
                                {
                                    latestItemList.Remove(latestItemList[i]);
                                    latestItemList.Add(items[index]);
                                    currentItemAdded = true;
                                    break;
                                }
                            }

                            if (currentItemAdded == false)
                            {
                                latestItemList.Add(items[index]);
                            }
                        }
                        else
                        {
                            latestItemList.Add(items[index]);
                        }
                    }

                    items = latestItemList.ToArray();

                    string str1 = ShortID.Encode(workflow.WorkflowID) + "_" + ShortID.Encode(state.StateID);
                    Section section1 = new Section();
                    section1.ID = str1 + "_section";
                    Sitecore.Web.UI.HtmlControls.Section section2 = section1;
                    placeholder.AddControl((System.Web.UI.Control)section2);
                    int length = items.Length;
                    string str2 = string.Format("<span style=\"font-weight:normal\"> - ({0})</span>", length > 0 ? (length != 1 ? (object)string.Format("{0} {1}", (object)length, (object)Translate.Text("items")) : (object)string.Format("1 {0}", (object)Translate.Text("item"))) : (object)"none");
                    section2.Header = state.DisplayName + str2;
                    section2.Icon = state.Icon;
                    if (Settings.ClientFeeds.Enabled)
                    {
                        FeedUrlOptions feedUrlOptions = new FeedUrlOptions("/sitecore/shell/~/feed/workflowstate.aspx")
                        {
                            UseUrlAuthentication = true
                        };
                        feedUrlOptions.Parameters["wf"] = workflow.WorkflowID;
                        feedUrlOptions.Parameters["st"] = state.StateID;
                        section2.FeedLink = feedUrlOptions.ToString();
                    }
                    section2.Collapsed = length <= 0;
                    Border border = new Border();
                    section2.Controls.Add((System.Web.UI.Control)border);
                    border.ID = str1 + "_content";
                    this.DisplayState(workflow, state, items, (System.Web.UI.Control)border, 0, this.PageSize);
                    this.CreateNavigator(section2, str1 + "_navigator", length);
                }
            }
        }

        private DataUri[] GetItems(WorkflowState state, IWorkflow workflow)
        {
            Assert.ArgumentNotNull((object)state, "state");
            Assert.ArgumentNotNull((object)workflow, "workflow");
            ArrayList arrayList = new ArrayList();
            DataUri[] items = workflow.GetItems(state.StateID);
            if (items != null)
            {
                foreach (DataUri index in items)
                {
                    Item obj = Context.ContentDatabase.Items[index];
                    if (obj != null && obj.Access.CanRead() && (obj.Access.CanReadLanguage() && obj.Access.CanWriteLanguage()) && (Context.IsAdministrator || obj.Locking.CanLock() || obj.Locking.HasLock()))
                        arrayList.Add((object)index);
                }
            }
            return arrayList.ToArray(typeof(DataUri)) as DataUri[];
        }

        private void CreateNavigator(Sitecore.Web.UI.HtmlControls.Section section, string id, int count)
        {
            Assert.ArgumentNotNull((object)section, "section");
            Assert.ArgumentNotNull((object)id, "id");
            Navigator navigator = new Navigator();
            section.Controls.Add((System.Web.UI.Control)navigator);
            navigator.ID = id;
            navigator.Offset = 0;
            navigator.Count = count;
            navigator.PageSize = this.PageSize;
        }
    }
}



NOTE: Code in red is the custom code. Other code is Sitecore default dll code.
Sitecore code can be change with the Sitecore version and the revision you are working with.

Friday, December 12, 2014

Sitecore - Set Reminder using Wrokflow Action

Current Environment: Sitecore 7.2 rev 140526

Requirement:
Need to set a reminder programmatically, from a custom workflow action.

Sitecore Step 1: Create a workflow action within the workflow which is set to that item template.

Create a workflow. (I used the default sample workflow).
You can find how to create the workflow action in sitecore cookbook: http://sdn.sitecore.net/upload/sitecore6/workflow_cookbook_a4.pdf.


Sitecore Step 2: Provide Type String

Select the created workflow action.
In the Type String field provide the following details.
namespace.class_name, dll_name
Ex: TestWorkflow.SetReminder, TestWorkflow

Coding

    public class SetReminder
    {
        public void Process(WorkflowPipelineArgs args)
        {
            var item = args.DataItem;
            if (item != null)
            {
                using (new Sitecore.SecurityModel.SecurityDisabler())
                {
                    item.Editing.BeginEdit();
                    try
                    {
                        item.Fields["__reminder text"].Value = "Review this item";
                        Sitecore.Data.Fields.DateField dateField = item.Fields["__reminder date"];
                        DateTime date = DateTime.Today.AddYears(1);

                        if (dateField != null)
                        {
                            dateField.Value = string.Format("{0}{1}{2}T{3}", date.Year, date.Month, date.Day,
                                date.ToString("hhmmss"));
                        }
                    }
                    finally
                    {
                        item.Editing.EndEdit();
                    }
                }
            }

        }