1 Comments

Recently we’ve started to using Octopus deploy in an AWS EC2 environment.  The challenge we are having is automatic tentacle registration on Auto Scale.  After a couple of hours, we’ve found the solution.   Noticed some people are asking the same questions on Octopus deploy support portal .  But didn’t give a clear solution, therefore I decided to share founds we had.

 

The solution we are having is to install and register the tentacle with commend line.

Step 1 Install tentacle

image

Step 2 configure tentacle

image

Step 3 octo promote-release

image

Hopefully, this is helpful.

0 Comments

Requirement

Newsletter subscription is a common requirement for most for website. Never really think about this function properly, until now client wants to send a scheduled newsletters to all subscribed users.  It sounds simple, isn’t it?  However, when you started to think about a bit in deep, you will probably start to ask questions like below:

  • Who are these users? 

The user must be contacts.  A contact represents an individual who interacts with or may potentially interact with the website.

  • How do I know whether or not the contact subscribed newsletter?

Then you started to realize you need a attributes on contact profile, which means you will most likely need to create a custom contact facet.

  • How to create a dynamic contact list?

Once a contact subscribed newsletter on the website, the contact need to be automatically added into your newsletter subscription contact list.  The answer is simply to use segment list.  Then you realized “damn, I need to create a custom rule”  for segmenting contacts based on the custom facets.

 

Solution

To interoperate the requirements into technical requirements, I drew a  kind data flow diagram, and highlighted custom functions we need to achieve the above requirement.

  1. custom contact facet
  2. programmatically update contact in xDB
  3. Add custom contact facet into contact index.
  4. custom rule condition

 

 

image

 

Implementation

In this section, I will demo the implementation. The demo code is supported Sitecore 8.1 (upgrade-3 or above).

custom contact facet

Sitecore well documented how to create a custom contact facet.   Based on the document I created SubscriptionInfo facet.

Here is the example:

  • Create an Interface

image

  • Create a Facet class

image

  • Register in Analytic Model

image

 

programmatically update contact

Once you’ve create custom facet, next step is to update contact.

Here is the example:

image

Submitting a newsletter subscription, then you can verify the data in xDB

image

 

Add custom contact facet into contact index

In order to use the segment condition, you will need to add the custom facet into index.  Luckily, Sitecore provide a good guide about how to index these custom contact facets during aggregation process.  All you will need to do is to extend the contactindexable.loadfields pipeline.

Here is the example:

image

custom rule condition

Last thing left is to create a custom segment condition, which allows you to check whether or not the contact subscribe the newsletter.

image

 

Once you completed all above, you can now go to List manager and create your segment

image

 

Contacts will be dynamically added into your contact list, which allows newsletters to be sent to the segmented contacts easily.

image\

image

Conclusion

It’s not complicate right?  Hopefully, you will enjoy creating your own facets and rock and roll =P.

References

  1. https://doc.sitecore.net/sitecore_experience_platform/setting_up__maintaining/xdb/contacts/index_custom_contact_facets
  2. https://doc.sitecore.net/sitecore_experience_platform/setting_up__maintaining/xdb/contacts/contacts
  3. https://doc.sitecore.net/sitecore_experience_platform/digital_marketing/the_list_manager/creating_lists/create_a_contact_list_or_a_segmented_list

0 Comments

Today, one of the colleague asked me a question about how to post a form with a complex object.  It is actually a bit tricky. This is because of the nature of HTML form [1].  When a form is submitted for processing, some controls have their name paired with their current value and these pairs are submitted with the form.  Those controls for which name/value pairs are submitted are called “successful controls”

 

HTML defines the following control types:

checkbox– When a form is submitted, only “on” checkbox controls can become “successful control”

Radio button – Radio button are like checkbox.

Input– the input text becomes the control’s current value.

hidden controls– Authors may create controls that are not rendered but whose values are submitted with a form. it generally sued for storing information between client/server exchanges. The Input element is used to create a hidden control.

 

The problem my colleague had is that  he wants to pass the view model to his controller by submitting the form.   However, the model is a complex model, after submitting the form. Only those simple type property has values, but value of the property like ICollection is null.  As explained above,  This is because that it is not a name/value pair value.  For solving this issue, we think  what if we convert the complex object into a serialized object and set it as a hidden control value.  So, after submitting the form, the value will be passed to the server.

Thus, we added the code as below :

@using(Html.BeginForm(…))

{

     @Html.Hidden("reportViewModel", JsonConvert.SerializeObject(Model))

}

Then, the action parameter is expecting a string. However, we still want to use a strong type object.  In order to do that, we end up with creating a custom model binder. Thanks to Mike Stall.

 

Follow the steps below to create custom model binder:

Step 1 – Create Model Binder

public class ReportViewModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

{
     string key = bindingContext.ModelName;
     ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);

if (val != null)
{
        string s = val.AttemptedValue as string;

        if (s != null){

               return JsonConvert.DeserializeObject<ReportViewModel>(s);
      }
             }
             return null;
         }
}

Step 2 – Add [ModelBinder] attribute on the parameter’s signature

public FileContentResult ReportSpreadshee([ModelBinder(typeof(ReportViewModelBinder))]ReportViewModel reportViewModel){

}

After all the efforts, we finally can pass the complex object with post method, when submitting a form.  Please feel free to make a comment, if you have an optimised solution.

References

[1]https://www.w3.org/TR/REC-html40/interact/forms.html#successful-controls

[2]https://blogs.msdn.microsoft.com/jmstall/2012/04/20/how-to-bind-to-custom-objects-in-action-signatures-in-mvcwebapi/

0 Comments

Sitecore provides many recommendation and tips about optimization of CD/CM.  For sure, you can find some of them online by googling them.  However, they are separated and it’s time consumed to search and read all the documentation.  So, I think it would be handy, if I can summarize them into one big checklist.

Security

  Protect Admins Pages

  1. Cache (/Sitecore/admin/cache.aspx)
  2. Database Browser (/stecore/admin/dbbrowser.aspx)
  3. Serialization (/stecore/admin/serialization.aspx)
  4. Show Config (/sitecore/admin/showconfig.aspx)
  5. Size Status (/sitecore/admin/sizestatus.aspx)
  6. Stats (/sitecore/admin/stats.aspx)
  7. Unlock Admin (/sitecore/admin/unlock_admin.aspx)
  8. Installation wizard (/sitecore/admin/UpdateInstallationWizard.aspx)

  Turn on custom Errors

Update production web.config

<customErrors mode="RemoteOnly" />
  Reset admin password.
    It would be embarrassing to go live with password "b" =)
  Ensure replace develop license file before going live

Performance

  Configure Keep-Alive

<agent type="Sitecore.Tasks.UrlAgent" method="Run" interval="00:15:00">
        <param desc="url">/sitecore/service/keepalive.aspx</param>
        <LogActivity>true</LogActivity>
</agent>

  Disable WebDAV

Sitecore recommends disabling WebDAV on the production content delivery servers to reduce the number of log files being created. Also, Sitecore recommends disabling WebDAV on the content management servers if the WebDAV functionality is not being used.

  1. Disable logging

    <!--<appender name="WebDAVLogFileAppender" type="log4net.Appender.SitecoreLogFileAppender,
    Sitecore.Logging">
     <file value="$(dataFolder)/logs/WebDAV.log.{date}.txt" />
     <appendToFile value="true" />
     <layout type="log4net.Layout.PatternLayout">
     <conversionPattern value="%4t %d{ABSOLUTE} %-5p %m%n" />
     </layout>
     </appender>--> 
      
    <!--<logger name="Sitecore.Diagnostics.WebDAV" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="WebDAVLogFileAppender"/>
      </logger>-->
  2. Disable webserver

    <!--<remove name="WebDAVModule" />-->
     <!--
    <add name="WebDAVRoot" path="*" verb="OPTIONS,PROPFIND" modules="IsapiModule"
    scriptProcessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll"
    resourceType="Unspecified" preCondition="classicMode,runtimeVersionv2.0,bitness32" />
     <add name="WebDAVRoot64" path="*" verb="OPTIONS,PROPFIND" modules="IsapiModule"
    scriptProcessor="%windir%\Microsoft.NET\Framework64\v2.0.50727\aspnet_isapi.dll"
    resourceType="Unspecified" preCondition="classicMode,runtimeVersionv2.0,bitness64" />
     <add verb="*" path="sitecore_webDAV.ashx"
    type="Sitecore.Resources.Media.WebDAVMediaRequestHandler, Sitecore.Kernel"
    name="Sitecore.WebDAVMediaRequestHandler" />
    -->
  3. Disable httphandler

     <!--
     <add verb="*" path="sitecore_webDAV.ashx"
    type="Sitecore.Resources.Media.WebDAVMediaRequestHandler, Sitecore.Kernel" />
     -->

  Disable Performance Counters

Performance counters create a minor overhead and is recommended to be enabled only when running in troubleshooting mode

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

  Disable Memory Monitor

Sitecore recommends disabling the Memory Monitor in production environments, and only enabling it for troubleshooting memory related issues.

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

  Ensure sufficient cache size

SEO

  Title Tag

The <strong>title</strong> tag is required in all HTML documents and it defines the title of the document. This tag displays the page title in browsers toolbar and in the search-engine results (SERPs). It also provides a title for the page when it is added to favorites. A descriptive <strong>title</strong> tag is important in helping search engines determine the web page's relevancy for certain keywords.

  Meta Description

The meta description tag is meant to be a short and accurate summary of your page content. This description can affect your search engine rankings and can also show up directly in search engine results (and affect whether or not the user clicks through to your site).


  <h1> Headings Status

This indicates if any H1 headings are used in your page. H1 headings are HTML tags than can help emphasize important topics and keywords within a page.

  <h2> Headings Status

This indicates if any H1 headings are used in your page. H1 headings are HTML tags than can help emphasize important topics and keywords within a page.


  Check Robots.txt

Check if your website is using a robots.txt file. Search engines send out tiny programs called spiders or robots to search your site and bring information back so that your pages can be indexed in the search results and found by web users. If there are files and directories you do not want indexed by search engines, you can use the "robots.txt" file to define where the robots should not go.

These files are very simple text files that are placed on the root folder of your website: www.yourwebsite.com/robots.txt.

There are two important considerations when using "robots.txt":

- the "robots.txt" file is a publicly available file, so anyone can see what sections of your server you don't want robots to use;

- robots can ignore your "robots.txt", especially malware robots that scan the web for security vulnerabilities;


  Check Sitemap

a sitemap is an XML file that lists URLs for a site along with additional metadata about each URL (when it was last updated, how often it usually changes, and how important it is, relative to other URLs <g class="gr_ gr_9 gr-alert gr_gramm undefined Grammar multiReplace" id="9" data-gr-id="9">in</g> the site) so that search engines can more intelligently crawl the site.

  Image Alt Test

Check images on your webpage for required alt attributes. If an image cannot be displayed (wrong source, slow connection, etc), the alt attribute provides alternative information. Using keywords and human-readable captions in the alt attributes is a good SEO practice because search engines cannot really see the images. For images with a decorative role (bullets, round corners, etc) you are advised to use an empty alt or a CSS background image.



0 Comments

Yesterday I got a new computer from IT, and they installed most of the essential tools/software for me which is good.  But when I tried to login into SQL Server, I had trouble with the login.  I spent an hour to fix it and I think it’s worth to share.

Background

Firstly, I tried to login with the “Windows Authentication account”,but failed shown me the error as below.  then I tried using “sa” account same error.

image

 

Troubleshooting

I think it might be because of the wrong password, then I emailed to IT to confirm the “sa” password. They sent me password but still the same password.  Just making sure it’s not a probably with the password, I found a tool (http://www.top-password.com/guide/reset-sql-server-password.html) to reset “sa” password.  SQL Server Password Changer is super easy to use.  All you need to do is only 3 steps really:

  • Step 1: Stop service via Sql Server Configuration Manager

image

  • Step 2:Open master.mdf filer under “C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER2014\MSSQL\DATA” Folder, and then change password

image

  • Step 3:Restart the service that we turned off in Step 1.

Unfortunately, after resetting the password, it was still the same error.  I was convinced that it’s not caused by wrong password.  Then I think it can be because of that the mix mode hasn’t been enabled. At the end,  I found a solution to enable the max mode without login to SQL.  Thanks to stackoverflow for this solution. 

Use 2 instead of 1 for mixed authentication. You'll have to restart SQL Server after changing this setting. You can't do that from T-SQL. From the command prompt, it's something like net stop mssqlserver then net start mssqlserver.

EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', 
    N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 1

 

After enabling the mixed mode,  I can login with “SQL Server Authentication account” successfully, although I still don't know why the “windows account” doesn’t work.  But at least i can continue my work.