29 November 2015

Test if the server is reachable via ports without telnet in linux

nc -zv 127.0.0.1 80
Multiple ports:
nc -zv 127.0.0.1 22 80 8080
Range of ports:
nc -zv 127.0.0.1 20-30
http://superuser.com/questions/621870/test-if-a-port-on-a-remote-system-is-reachable-without-telnet

30 June 2015

"java.lang.outofmemoryerror java heap space" in Bamboo setup


Been the process of setting up Bamboo environment for the client, keep getting and error of java heap space when fires up the bamboo build. Found the solution from Atlassian wiki page, take the essence of the solution from long documentation and put it down my blog for the future reference.


Setting properties for Windows services from the command line
  1. Identify the name of the service that Bamboo is installed as in Windows (Control Panel > Administrative Tools > Services):
  2. Open a command prompt from Start > Run > type 'cmd' > Enter.
  3. Change directory to the bin directory of your Bamboo installation directory.
  4. Run:
    tomcat7w //ES//%SERVICENAME%
    (info) In the above example, it would be tomcat7w //ES//Bamboo
  5. Click on the Java tab to see the list of current start-up options:
https://confluence.atlassian.com/display/BAMBOO/Configuring+Bamboo+on+start-up

07 June 2015

Using Telstra API to send SMS in ASP.NET

Telstra (https://dev.telstra.com/) is pushing their own API and as a part of the plan, it is giving away 1000 free SMS per month with the limit of 100 per day.

After a simple register and set up, wait about 1 hour for get "MyAPP" approved, it is ready to go.



It requests an access token with your own "key" and "secret" first which expires in 1 hour. Then we need to pass in this token with all the request we are going to make.
The documentation is simple, straight forward and all the sample code is hosted on github. I write a quick C# to test it out and it works well!

public string GetAccessToken()
{
    const string consumerKey = "{yourkey}";
    const string consumerSecret = "{secret}";
    string url =string.Format("https://api.telstra.com/v1/oauth/token?client_id={0}&client_secret={1}&grant_type=client_credentials&scope=SMS", consumerKey, consumerSecret);

    using (var webClient = new System.Net.WebClient())
    {
        var json = webClient.DownloadString(url);
        var obj = JObject.Parse(json);
        return obj.GetValue("access_token").ToString();
     }
}
public string SendSms(string token, string recipientNumber, string message)
{
     try
     {
        using (var webClient = new System.Net.WebClient())
        {
            webClient.Headers.Clear();
            webClient.Headers.Add(HttpRequestHeader.ContentType, @"application/json");
            webClient.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);

            string data = "{\"to\":\"" + recipientNumber + "\", \"body\":\"" + message + "\"}";
            var response = webClient.UploadData("https://api.telstra.com/v1/sms/messages", "POST", Encoding.Default.GetBytes(data));
            var responseString = Encoding.Default.GetString(response);
            var obj = JObject.Parse(responseString);
            return obj.GetValue("messageId").ToString();
                    // Now parse with JSON.Net
        }
     }
     catch (Exception e)
     {
        Console.WriteLine(e.Message);
     }
        return string.Empty;
 }

04 June 2015

Install MongoDB in Debian

As a part of Sitecore 8 setup, we need to install MongoDB in Debian.

  1. Check Debian version. The MongoDB 3.xx requires at least Debian 7.1



  2. So we need to upgrade to Debian 7.1 at least. Here is how to do it.

    Edit your /etc/apt/sources.list file and change all instances of squeeze to wheezy. Once you have finished, your /etc/apt/sources.list should resemble the following:

       /etc/apt/sources.list


      
    deb http://ftp.us.debian.org/debian/ wheezy main
    deb-src http://ftp.us.debian.org/debian/ wheezy main
    
    deb http://security.debian.org/ wheezy/updates main
    deb-src http://security.debian.org/ wheezy/updates main
    
    # wheezy-updates, previously known as 'volatile'
    deb http://ftp.us.debian.org/debian/ wheezy-updates main
    deb-src http://ftp.us.debian.org/debian/ wheezy-updates main




    How to use "vi" http://www.howtogeek.com/102468/a-beginners-guide-to-editing-text-files-with-vi/
  3. Go through the update process.

    Enter the following command to update your package lists:
    apt-get update

    Keep getting the error of " GPG error: http://ftp.us.debian.org wheezy Release: The following signatures couldn't be verified because the public key is not available: Try this and ignore the warning.
    apt-get install debian-archive-keyring
  4. More info: http://serverfault.com/questions/337278/debian-how-can-i-securely-get-debian-archive-keyring-so-that-i-can-do-an-apt-g
  5. Enter the following command to grab the latest versions of key system utilities:
  6. apt-get install apt dpkg aptitude
  7. After the package updates have completed, upgrade your system by entering the following command. The upgrade will download and install numerous packages. This step may take a while to complete.
    apt-get dist-upgrade


  8. After "reboot", check the debian version again. 
  9.            cat /etc/debian_version









  • Install MongoDB

    The Debian package management tools (i.e. dpkg and apt) ensure package consistency and authenticity by requiring that distributors sign packages with GPG keys.

    Import the public key used by the package management system.

    Issue the following command to add the MongoDB public GPG Key to the system key ring.
    apt-key adv --keyserver keyserver.ubuntu.com --recv 7F0CEB10

    Create a /etc/apt/sources.list.d/mongodb-org-3.0.list file for MongoDB.

    Create the list file using the following command:
    echo "deb http://repo.mongodb.org/apt/debian "$(lsb_release -sc)"/mongodb-org/3.0 main" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list


    If your Debian installation does not have lsb_release installed, you may install it using sudoapt-get install lsb-release.

    Reload local package database.

    Issue the following command to reload the local package database:
    apt-get update
    root@debian-x64:~# apt-get update
    Ign http://58.162.66.11 squeeze Release.gpg
    Ign http://58.162.66.11/linux/debian-6.0.1a-x64/ squeeze/main Translation-en
    Ign http://58.162.66.11/linux/debian-6.0.1a-x64/ squeeze/main Translation-en_AU
    Hit http://58.162.66.11 squeeze Release
    Ign http://58.162.66.11 squeeze/main Sources/DiffIndex
    Ign http://58.162.66.11 squeeze/main amd64 Packages/DiffIndex
    Hit http://58.162.66.11 squeeze/main Sources
    Ign http://58.162.66.11 squeeze/main amd64 Packages
    Hit http://58.162.66.11 squeeze/main amd64 Packages
    Ign http://repo.mongodb.org squeeze/mongodb-org/3.0 Release.gpg
    Ign http://repo.mongodb.org/apt/debian/ squeeze/mongodb-org/3.0/main Translation-en
    Ign http://repo.mongodb.org/apt/debian/ squeeze/mongodb-org/3.0/main Translation-en_AU
    Ign http://repo.mongodb.org squeeze/mongodb-org/3.0 Release
    Ign http://repo.mongodb.org squeeze/mongodb-org/3.0/main amd64 Packages
    Err http://repo.mongodb.org squeeze/mongodb-org/3.0/main amd64 Packages
    404 Not Found
    Get:1 http://ftp.au.debian.org squeeze-updates Release.gpg [836 B]
    Hit http://security.debian.org squeeze/updates Release.gpg
    Ign http://security.debian.org/ squeeze/updates/main Translation-en
    Ign http://security.debian.org/ squeeze/updates/main Translation-en_AU
    Ign http://ftp.au.debian.org/debian/ squeeze-updates/main Translation-en
    Ign http://ftp.au.debian.org/debian/ squeeze-updates/main Translation-en_AU
    Hit http://security.debian.org squeeze/updates Release
    Hit http://security.debian.org squeeze/updates/main Sources
    Hit http://security.debian.org squeeze/updates/main amd64 Packages
    Get:2 http://ftp.au.debian.org squeeze-updates Release [113 kB]
    Err http://ftp.au.debian.org squeeze-updates Release
    Fetched 837 B in 5s (158 B/s)
    Reading package lists... Done
    W: A error occurred during the signature verification. The repository is not updated and the previous index files will be used. GPG error: http://ftp.au.debian.org squeeze-updates Release: The following signatures couldn't be verified because the public key is not available: NO_PUBKEY 8B48AD6246925553
    W: Failed to fetch http://repo.mongodb.org/apt/debian/dists/squeeze/mongodb-org/3.0/main/binary-amd64/Packages.gz 404 Not Found
    W: Failed to fetch http://ftp.au.debian.org/debian/dists/squeeze-updates/Release
    W: Some index files failed to download, they have been ignored, or old ones used instead.

    gpg --keyserver pgpkeys.mit.edu --recv-key 8B48AD6246925553
    gpg -a --export 8B48AD6246925553 | apt-key add -

    Install the MongoDB packages.

    You can install either the latest stable version of MongoDB or a specific version of MongoDB.
    export LANGUAGE=en_US.UTF-8
    export LANG=en_US.UTF-8
    locale-gen en_US.UTF-8
    export LC_ALL=en_US.UTF-8
    dpkg-reconfigure locales
    
    

    Install the latest stable version of MongoDB.

    Issue the following command:
    sudo apt-get install -y mongodb-org 

    Install a specific release of MongoDB.

    Specify each component package individually and append the version number to the package name, as in the following example:


    3.0.2 mongodb-org-server=3.0.2 mongodb-org-shell=3.0.2 mongodb-org-mongos=3.0.2 mongodb-org-tools=3.0.2
    If we get this error as below, go to


    [FAIL] Starting database: mongod failed!
    invoke-rc.d: initscript mongod, action "start" failed.
    dpkg: error processing mongodb-org-server (--configure):
     subprocess installed post-installation script returned error exit status 1
    Setting up mongodb-org-mongos (3.0.2) ...
    Setting up mongodb-org-tools (3.0.2) ...
    dpkg: dependency problems prevent configuration of mongodb-org:
     mongodb-org depends on mongodb-org-server; however:
      Package mongodb-org-server is not configured yet.

    dpkg: error processing mongodb-org (--configure):
     dependency problems - leaving unconfigured
    Errors were encountered while processing:
     mongodb-org-server
     mongodb-org
    E: Sub-process /usr/bin/dpkg returned an error code (1)

    Make sure remove "/tmp/mongodb-27017.sock" if there is any.
    Also, go to "/var/log/mongodb/mongod.log" to see the logs.
    If the log showing insufficient space related error.
    Put "smallfiles=true" in the etc/mongod.conf to get over it for now and raise up with CAV to give more spaces for the related path.


  • 24 May 2015

    LockObtainFailedException and SwitchOnRebuildLuceneIndex in Sitecore 7

    "LockObtainFailedException" is a common error in Sitecore 7. For most of the cases, it will occur when you try to access the index file while the index is still building. The error will looks like something like below,


    In Sitecore 7, it introduces the "SwitchOnRebuildLuceneIndex". (Sitecore.ContentSearch.LuceneProvider.SwitchOnRebuildLuceneIndex) It will create a "xxx_sec" .e.g "sitecore_index_web_sec)" index folder and build a new set of indexes for Sitecore to switch on when rebuilding index is happening.
    
    
    

    I recommend Sitecore set this as default provider.

    22 May 2015

    Stop Words with Custom Search Analyzer in Sitecore 7

    As we know, at least in Lucene.NET 3.x.x version, the "stop words" are hand coded in this class "Lucene.Net.Analysis.StopAnalyzer". It does making the customizing the stop words a bit of tricky, especially for specific index since for the most of cases, you only need to customize one of the custom index to suit clients need. Here is a way how we can achieve that.

    By default, Sitecore use "Lucene.Net.Analysis.Standard.StandardAnalyzer" and you can find it in wbe.config.(/configuration/sitecore/search/analyzer)


    We create a new custom Analyzer inherit from "standardAnalyzer" and override the constructor. Here is the place I assign my own custom stop words to "STOP_WORDS_SET"
    public class CustomAnalyzer : Lucene.Net.Analysis.Standard.StandardAnalyzer
    {
            private static ISet STOP_WORDS_SET = GetStopWords();//{{"by","by"}}; <-- Makes "by" a 
           
            public CustomAnalyzer()
                : base(Lucene.Net.Util.Version.LUCENE_30, STOP_WORDS_SET)
            {      
            }
    
            private static ISet GetStopWords()
            {
                string[] strArray = GetWords();
                CharArraySet set = new CharArraySet(strArray.Length, false);
                set.AddAll((IEnumerable)strArray);
                return (ISet)CharArraySet.UnmodifiableSet(set);
            }
    }
    

    After, under "/configuration/sitecore/search/analyzer" in web.config, we wire the custom analyzer as below,
    
          
            Lucene_30
          
    

    Don't forget to register it from you custom index file
    
    
    
    


    Done!!!

    14 May 2015

    Access to the registry key 'Global' is denied in Sitecore

    My ECM would not send any email. By looking into the logs, I can see there is some issue when initializing the CPUAvgPerformance as below.

    ManagedPoolThread #16 18:39:46 ERROR EmailCampaign: Error on Initializating CPUAvgPerformance with exception: System.UnauthorizedAccessException: Access to the registry key 'Global' is denied.
       at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
       at Microsoft.Win32.RegistryKey.InternalGetValue(String name, Object defaultValue, Boolean doNotExpand, Boolean checkSecurity)
       at Microsoft.Win32.RegistryKey.GetValue(String name)
       at System.Diagnostics.PerformanceMonitor.GetData(String item)
       at System.Diagnostics.PerformanceCounterLib.GetPerformanceData(String item)
       at System.Diagnostics.PerformanceCounterLib.get_CategoryTable()
       at System.Diagnostics.PerformanceCounterLib.CounterExists(String category, String counter, Boolean& categoryExists)
       at System.Diagnostics.PerformanceCounterLib.CounterExists(String machine, String category, String counter)
       at System.Diagnostics.PerformanceCounter.InitializeImpl()
       at System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, String instanceName, Boolean readOnly)
       at System.Diagnostics.PerformanceCounter..ctor(String categoryName, String counterName, String instanceName)
       at Sitecore.Modules.EmailCampaign.Core.Dispatch.CpuAvgPerformance..ctor()
       at Sitecore.Modules.EmailCampaign.Core.Dispatch.TimeSummary..ctor()
    


    How to fix it?


    1. Go to "Computer Management - System Tools - Local Users and Groups - Groups - Performance log Users" and add the "User" running your AppPool into this group


    2. If you are using "ApplicationPoolidentity" running your AppPool. use "IIS AppPool\AppPoolName"

    Note: After that, you need to "Restart the IIS" or "Stop/Start" AppPool. Recycle AppPool will not help.

    Helps?


    08 May 2015

    Hidden Items - Missing layout System and templates in Sitecore Content Editor

    You wake up in the morning and login Sitecore content editor as Sitecore Administrator. WOW, where is my "layout", "templates" and "system" node?




    It turns out easy to fix unless you did something really wrong with security last night. 
    Go to "View" and check "Hidden Items"




    It works and that is half a hour debugging time I never get back



    06 May 2015

    Set up validation rules to prevent user from saving in Page Editor Mode using Sitecore

    I recently encountered a scenario that in page editor mode, the client want to prevent content user from saving when the validation rules breaks which is not a default behavior. Here is how we can easily achieve it in sitecore.

    Only CriticalError and FatalError will give your the pop up to tell you something is not right in page editor mode. The difference is FatalError will prevent you from saving and CriticalError will not but only with pop up warning message.





    It is very easy to set up!


    04 May 2015

    Limit editing HTML source for rich text editor field in Sitecore

    I recently get a requirement about limiting the way of editing ram html in Rich Tex Editor. After a few digging around the Sitecore, here is how I figured.


    There are two places in Rich Text editor you can change the html markup in Content Editor.

    1st place

    2nd place


    For 1st place, goes to Core database "/sitecore/system/Field types/Simple Types/Rich Text" and in "Security Editor" to deny "read" for both items as highlighted for the "Sitecore Roles" you prefer.





    For 2nd place, it is a bit of tricky. We first need to find out what is our default profile is by going to web.config.

    <setting name="HtmlEditor.DefaultProfile" value="/sitecore/system/Settings/Html Editor Profiles/Rich Text Default" />

    After that, we located this profile in "Core" database and locate 
    "/sitecore/system/Settings/Html Editor Profiles/Rich Text Default/Buttons/HTML View"


    So we need to do the same by deny "read" from "Security editor" for certain roles as well.


    Done!

    28 April 2015

    Refresh the page after deletion in Page Editor mode using Sitecore

    (Working for Sitecore 7 and 6)

    Recently, I have been working on Page Editor using Edit Frame in Sitecore. It works pretty well but one issue. As default, after delete item in Page Editor, the page will not refresh automatically which leave the Deleted item still appearing on the page. Apparently, it confuses Content Author and it is a bad User experience. I have found how to get over this issue nicely.

    I have tried to create and add the custom webedit command after the command section to reload the parent page.
    
    

    It is not working since the " <uiDeleteItems>" section in web.config will fire up after. It means if the item have any link associated with, it will pop up the "remove link" window which get refreshed by my code and cannot go further.

    So I put my custom command code after "<processor method="Execute" mode="on" type="Sitecore.Shell.Framework.Pipelines.DeleteItems,Sitecore.Kernel">" in the section of
    "<uiDeleteItems>" in web.config

    Code:

    public class CustomDeleteItems : ItemOperation
        {
            public void Reload(ClientPipelineArgs args)
            {
                SheerResponse.Eval("window.parent.location.reload();");
            }
        }
    


    It is a progress but not enough. We need a a way to tell it only works for "Page Editor" mode since you don't want the page to be refreshed in Content Editor. I could not find a new way to do so it ends up with a lot of decompiling the .dll and it ends up with

    public class CustomDelete : WebEditCommand
        {
            /// 
            /// Executes the command in the specified context.
            /// 
            /// 
            /// The context.        public override void Execute(CommandContext context)
            {
                Assert.ArgumentNotNull((object)context, "context");
    
                NameValueCollection parameters = new NameValueCollection();
                parameters["id"] = context.Parameters["id"];      
                Context.ClientPage.Start((object)this, "Run", parameters);
            }
    
            /// 
            /// Queries the state of the command.
            /// 
            /// 
            /// The context.
            ///                     /// 
            /// The state of the command.
            /// 
            /// 
            public override CommandState QueryState(CommandContext context)
            {
                Assert.ArgumentNotNull((object)context, "context");
                SiteContext site = Context.Site;
                bool flag = false;
                if (site != null && Context.PageMode.IsPageEditorEditing)
                    flag = true;
                if (!flag && WebUtil.GetQueryString("mode") != "edit")
                    return CommandState.Disabled;
                Item obj = context.Items.Length > 0 ? context.Items[0] : (Item)null;
                if (obj == null || !obj.Access.CanDelete() || obj.Appearance.ReadOnly)
                    return CommandState.Disabled;
                return base.QueryState(context);
            }
    
            /// 
            /// Runs the pipeline.
            /// 
            /// 
            /// The arguments.        protected void Run(ClientPipelineArgs args)
            {
                Assert.ArgumentNotNull((object)args, "args");
                Item obj = Client.ContentDatabase.GetItem(args.Parameters["id"]);
                if (obj != null)
                {
                    if (WebEditCommand.IsStartItem(obj))
                    {
                        SheerResponse.Alert("The operation cannot be applied to the site start item.");
                    }
                    else
                    {
                        if (!SheerResponse.CheckModified())
                            return;
                      
                        Start("uiDeleteItems", args, obj.Database, new Item[1] { obj }, new NameValueCollection())["message"] = "item:deleted(id=" + (object)obj.ID + ")";
                    }
                }
                else
                    SheerResponse.Alert("The item could not be found.\n\nYou may not have read access or it may have been deleted by another user.");
            }
    
            private static NameValueCollection Start(string pipelineName, ClientPipelineArgs args, Database database, Item[] items, NameValueCollection additionalParameters)
            {
                Assert.ArgumentNotNull((object)pipelineName, "pipelineName");
                Assert.ArgumentNotNull((object)args, "args");
                Assert.ArgumentNotNull((object)database, "database");
                Assert.ArgumentNotNull((object)items, "items");
                Assert.ArgumentNotNullOrEmpty(pipelineName, "pipelineName");
                NameValueCollection nameValueCollection = new NameValueCollection();
                string str1 = string.Join("|", Enumerable.Select((IEnumerable)items, (Func)(item => item.ID)));
                string str2 = items[0].Language.ToString();
                nameValueCollection.Add("database", database.Name);
                nameValueCollection.Add("items", str1);
                nameValueCollection.Add("language", str2);
                nameValueCollection.Add("ispageediting","1");
    
                args.Parameters = nameValueCollection;
                if (additionalParameters != null)
                {
                    foreach (string index in additionalParameters.AllKeys)
                        args.Parameters[index] = additionalParameters[index];
                }
                Context.ClientPage.Start(pipelineName, args);
                return nameValueCollection;
            }
        }
    
    



    public class CustomDeleteItems : ItemOperation
        {
    
            /// 
            /// Executes Pipeline Postaction.
            /// 
            /// 
            /// The arguments.
            ///                     public void Reload(ClientPipelineArgs args)
            {
                //Sitecore.Client.ContentDatabase..
                if (args.Parameters["ispageediting"] == "1")
                {
                    SheerResponse.Eval("window.parent.location.reload();");
                }
            }
        }
    

    It works perfectly!

    WFFM Web form for marketer fail to submit in Sitecore

    I have setup a standard form using WFFM in Sitecore 8.2. It always fail when submit. Error as below, Error: Exception: System.Data.DataE...