17 May 2014

WFFM custom field with Placeholder


Here is a simple entry on this blog, but I wanted to add it here to show how simple it is to create or extend fields in WFFM.

On a recent project we ad this form where the text fields did not have labels. Instead, the text was placed on the TextBox. Basically something like the below image:



Obviously you could add it as Default Value and get some JS to clear the field on focus... But a nicer way to do this is to ensure the text is placed on the placeholder attribute:

< input class="scfSingleLineTextBox" id="uc_2_form_ED630308419A4EB9A4D237E767A70A2A_field_D553FA1A5DF1403BA2A732D45CD4298D" maxlength="256" name="uc_2$form_ED630308419A4EB9A4D237E767A70A2A$field_D553FA1A5DF1403BA2A732D45CD4298D" placeholder="First Name" type="text" />


If you go into the form editor in sitecore, unfortunately you will not have the placeholder attribute on the default fields: Single Line, Email...

So, lets create a custom field:

1- Create an item in Sitecore for your custom Field

2- Create a class on your project as per the following

using Oakton.Business.Interfaces.Wffm;
using Sitecore.Form.Core.Attributes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using ScWffm = Sitecore.Form;

namespace MyProject.Sitecore.Wffm
{
    [ValidationProperty("Text")]
    public class SingleLineText : ScWffm.Web.UI.Controls.SingleLineText, IPlaceholderField
    {
        [VisualCategory("Custom Properties")]
        [VisualProperty("Placeholder Text", 2)]
        [DefaultValue("")]
        public string PlaceholderText { get; set; }

        protected override void OnInit(EventArgs e)
        {
            if (!String.IsNullOrEmpty(PlaceholderText))
            {
                textbox.Attributes["placeholder"] = PlaceholderText;
            }

            base.OnInit(e);
        }
    }
}


From the above code, you can see that the class will inherit from the SingleLineText and extend it to include a PlaceholderText Property. This property will be visible on the WFFM edit form. Then render as placeholder attribute on the frontend...

When editing the form you will now be able to select the new field:



Then you will see the new property on the left hand side:

 Don't forget to publish the fields and form and there you have a custom field in WFFM...

11 May 2014

Using Dictionary for static string


Just a quick post today to talk abut Dictionaries.

I have seen quite a lots of code where we always have some text floating around in either ascx or cshtml. Usually people tells me that this text will never be updated... Ever... and in that case why should we worry about it. I know it is unlikely but when it does happen and the client wants to update the text then that means deployment: TEST > UAT > PROD. Well I have to admit that when I started with Sitecore 6 years ago, I did not know what to do with those neither so we usually placed them as a field in items but tricky for re-usability :)

Anyway, since 6.6, it does not really take long to cater for it. A simple way is o use the dictionary in Sitecore.

Here is a quick view on how you can setup your text and retrieve it...

You can create a new dictionary item such as:

Also it is better if you create some kind of a struture under Dictionary: A, B, C, D... Now on the code side you can retrieve the value by doing something like:




OK. That is the easiest use.
Now from Sitecore 6.6 there is a concept of domain dictionary which is even better if you have multiple sites running on the same sitecore instance. Indeed, it would be so easier to separate each dictionary for each site. Also, a great thing about domain dictionary is if you have all component as modular: Navigation is one module, banner is another one... then you could even think about having a dictionary per module...

So here we go, the first thing you need to do is to create a Sitecore Dictionary Domain on your website. The template you will use is: /sitecore/templates/System/Dictionary/Dictionary Domain
Under this item, you can create a folder and/or a dictionary item...



Now before being able to use it, you will need to define your dictionary domain for your site. For that, you can open the web.config or your specific config in the include folder. Then locate your site definition. If you look at the definition you will see that you can add a new attribute "dictionaryDomain":

              dictionaryDomain: The default domain to use when looking up dictionary phrases for the website. If a phrase does not exist in
                                this dictionary domain, Sitecore attempts to locate that phrase in the default dictionary domain - 
                                /sitecore/system/Dictionary in the current database. If the phrase cannot be found in the default dictionary
                                domain, Sitecore attempts to locate that phrase in the default dictionary domain in the Core database, if that
                                database exists.
                                You can override the site-specific dictionary domain by passing parameters to the Translate.Text() method.


So go ahead and locate your site entry and add the dictionaryDomain="My Site Dictionary":
        
On the code side, you can now retrieve the value by using either of the following:



3 May 2014

Backup using serialization before deploying packages

Well in previous post, we saw that we could generate the serialization prior installing any packages. Well the underlying goal was to actually backup the content... So let's extend a bit and see if we can do a bit more. What would be nice would be to delete the entire folder prior serialization and zipping the serialization folder once it is completed...

1- Get the serialization folder:
This is quite simple and you should be able to do it with the following code:
            string SerializationRootFolder = FileUtil.MapPath(SC.Configuration.Settings.SerializationFolder);


2- Clear the folder
So let's delete all files and subfolders... Obviously you will only be able to do that if you have the required file permission setup... Check your app pool if using network services... But for this exercies, let's say you have setup the permissions so you are allowed to delete files and folder...
        /// 
        /// Clear Serialisation folder
        /// 
        /// 
        private void ClearSerializationFolder(string SerializationRootFolder)
        {
            System.IO.DirectoryInfo downloadedMessageInfo = new DirectoryInfo(SerializationRootFolder);

            foreach (FileInfo file in downloadedMessageInfo.GetFiles())
            {
                file.Delete();
            }
            foreach (DirectoryInfo dir in downloadedMessageInfo.GetDirectories())
            {
                dir.Delete(true);
            }
        }

3- Start serializing the entire content tree

Well guess what, we do have this code from previous post
        private void BackupItemTree(string id)
        {
            Database db = SC.Configuration.Factory.GetDatabase("master");
            Item itm = db.GetItem(id);

            SC.Data.Serialization.Manager.DumpTree(itm);
        }

4- Zipping it
To zip the files, you can use different libraries available for .NET, but since Sitecore has a Zip library, why not using it...
        /// 
        /// Create the zip file of serialisation
        /// 
        /// 
        /// 
        private string CreateZipFile(string SerializationRootFolder)
        {
            string backupName = string.Format("backup_serialization_{0}.zip", DateTime.Now.ToString("yyyyMMddhhmmss"));

            var zipFile = Path.Combine(SerializationRootFolder, backupName);

            using (var fileWriter = new SC.Zip.ZipWriter(zipFile))
            {
                var files = GetAllFiles(SerializationRootFolder, SearchOption.AllDirectories);

                SC.Diagnostics.Log.Info(string.Format("Adding files ({0})", files.Count()), this);

                var length = SerializationRootFolder.Length;
                if (!SerializationRootFolder.EndsWith("\\"))
                {
                    length += 1;
                }

                foreach (var file in files)
                {
                    fileWriter.AddEntry(file.Remove(0, length), file);
                }
            }

            return zipFile;
        }

        /// 
        /// Get all files - you can include all subdirectories through search options
        /// 
        /// 
        /// 
        /// 
        protected string[] GetAllFiles(string path, SearchOption searchOption)
        {
            List files = new List();

            string searchPattern = "*.item";
            files.AddRange(Directory.GetFiles(path, searchPattern, searchOption));
            
            return files.ToArray();
        } 


OK, time to test that... SO let's install a package and watch the serialization folder: