Saturday, September 21, 2013

Interesting reason of problem with changed SessionID per request




   3: "">
   5: <html xmlns="">
   6: <head runat="server">
   7:     <title></title>
   8: </head>
   9: <body>
  11:     <%
   3:         this.lbl.Text = HttpContext.Current.Session.SessionID;
  13:     </form>
  14: </body>
  15: </html>



Rule for URL rewrite module for redirecting from no www to www address without using domain name

In one of my previous articles (see Several useful SEO rules via IIS URL rewrite module) about URL rewrite IIS module I showed how to add a rule which will redirect users from address with no www prefix to the address with www:

   1: <rule name="redirect_from_nowwww_to_www" enabled="true" stopProcessing="true">
   2:   <match url=".*" />
   3:     <conditions>
   4:       <add input="{HTTP_HOST}" pattern="^example\.com$" />
   5:     </conditions>
   6:   <action type="Redirect" url="{R:0}"
   7: appendQueryString="true" redirectType="Permanent" />
   8: </rule>

It works, but it contains domain name ( which makes it specific to particular site. You can’t just copy and paste it to another web.config: in this case you will need to change the values to new host header.

Here is the more universal implementation of the same rule:

   2:   <match url=".*" />
   3:   <conditions>
   4:     <add input="{HTTP_HOST}" pattern="^(www\.)(.+)$" negate="true" />
   5:   </conditions>
   8: </rule>

Here I used possibility to add negative conditions (host doesn’t match to www pattern on line 4) and possibility to use server variables in actions ({HTTP_HOST} in action url on line 6). It will work like 1st rule, but can be used as is on any site accessed by http (in order to make it work for https, change http to https on line 7. Note also that in real implementation lines 6 and 7 should be on the single line).

Friday, September 20, 2013

One reason of empty wp-admin page in wordpress blog

If you faced with problem that after login to your wordpress blog, dashboard (/wp-admin) is empty, then the most suggestions you will find will be to check extra lines and spaces in wp-config.php and functions.php. First of all try to enable additional logging by setting WP_DEBUG to true in wp-config.php:

   1: define('WP_DEBUG', true);

It may show the following warnings on wp-login.php page:

Cannot modify header information - headers already sent by (output started at …/wp-config.php:1) in wp-login.php on line xxx

If you tried another suggestions and they didn’t help, try to save wp-config.php in ANSI encoding (regular Windows Notepad allows it). It may be so that it was saved as Unicode and in this case it will have several hidden symbols at beginning, which you won’t be able to remove in most common editors.

Saturday, September 14, 2013

Problem with not crawled managed metadata fields in Sharepoint 2013

In this post I would like to describe one interesting problem with search crawler and managed metadata fields in Sharepoint 2013. In some situations you may face with the problem that your taxonomy fields are not crawled. Let’s assume that we provisioned the site and created several managed metadata fields using the following declarations:

   1: <Field Type="Note"
   2:   DisplayName="MyManagedMetadataField_0"
   3:   MaxLength="255"
   4:   Group="My Fields"
   5:   ID="{4FCE0732-EF53-4b43-B678-3D2FC28D9A29}"
   6:   StaticName="MyManagedMetadataField_0"
   7:   Name="MyManagedMetadataField_0"
   8:   Hidden="TRUE"
   9:   ShowInViewForms="FALSE"
  10:   Description="" />
  11: <Field ID="{8D3A1B9D-A50E-47e4-8778-53E8717E4FC6}"
  12:   SourceID=""
  13:   Type="TaxonomyFieldType"
  14:   DisplayName="My Managed Metadata field"
  15:   ShowField="Term1033"
  16:   Required="FALSE"
  17:   EnforceUniqueValues="FALSE"
  18:   Group="My Fields"
  19:   StaticName="MyManagedMetadataField"
  20:   Name="MyManagedMetadataField"
  21:   Hidden="FALSE"
  22:   Mult="TRUE">
  23:   <Default></Default>
  24:   <Customization>
  25:     <ArrayOfProperty>
  26:       <Property>
  27:         <Name>IsPathRendered</Name>
  28:         <Value xmlns:q7="" p4:type="q7:boolean" xmlns:p4="">
  29:           true
  30:         </Value>
  31:       </Property>
  32:       <Property>
  33:         <Name>TextField</Name>
  34:         <Value xmlns:q6="" p4:type="q6:string" xmlns:p4="">
  35:           {4FCE0732-EF53-4b43-B678-3D2FC28D9A29}"
  36:         </Value>
  37:       </Property>
  38:     </ArrayOfProperty>
  39:   </Customization>
  40: </Field>
Here as always we provision 2 fields: hidden Note field and managed metadata field itself, which has reference to the Note field. After that we create some content in the doclib or list using content type which this field (this is important step: without at least 1 item with non-empty values in managed metadata fields, crawled and managed properties for them won’t be created) and run full crawl of our site. During the crawling if everything went properly Sharepoint creates 2 crawled properties and 1 managed property for each managed metadata field (actually it creates them also for other field types, but in this article we are talking only about managed metadata):


As shown on the picture above the following crawled and managed are created automaically:

Crawled property Mapped managed property
ows_{field name} -
ows_taxId_{field name} owstaxId{field name}

One crawled field (ows_MyManagedMetadataField) is not mapped to managed property initially (after first full crawl), another (ows_taxId_MyManagedMetadataField) has mapping to managed property (owstaxIdMyManagedMeatadataField), which is also created automatically during crawl. After that you should create 2nd managed property and map it to the crawled property without mapping. In all your queries, content by search web parts, search result sources, display templates, etc. you should use this second managed property which you map manually, not the one which was created automatically (for manually created managed property you may set it’s properties like Searchable, Queryable, etc. how you need, while for automatically created property Sharepoint set them and it is better to not change it). After that run full crawl again.

This is how search schema should look like if everything went correct. However if you provisioned managed metadata fields using the code shown above, you will have the following problem: after crawling crawled properties won’t be created at all or only 1 crawled property, which doesn’t have mapping to managed property, will be created. This fact it self is not critical. The problem however is that after that your KQL queries which filter the data based on managed metadata fields won’t return any data. It indicates that something went wrong during the crawling.

The problem is caused by the way how managed metadata field is provisioned. As you can see above it has the following name: MyManagedMetadataField_0, i.e. it uses format {managed metadata field name}_0. But as it turned out in order to have managed metadata fields correctly crawled it should use another format:

{managed metadata field name}TaxHTField

i.e. it should be named MyManagedMetadataFieldTaxHTField:

   1: <Field Type="Note"
   2:   DisplayName="MyManagedMetadataFieldTaxHTField"
   3:   MaxLength="255"
   4:   Group="My Fields"
   6:   StaticName="MyManagedMetadataFieldTaxHTField"
   7:   Name="MyManagedMetadataFieldTaxHTField"
   8:   Hidden="TRUE"
   9:   ShowInViewForms="FALSE"
  10:   Description="" />

This is not documented fact, but this is how Sharepoint provisions its own managed metadata fields (e.g. Enterprise keywords). If you will check Sharepoint search assemblies in reflector for “TaxHTField” you will see usages of this suffix in the code, i.e. search crawler really demands on it. This is the only change in the above example needed for making MyManagedMetadataField properly crawlable. Now you know how to properly provision managed metadata fields declaratively :).

Saturday, September 7, 2013

Problem with cut titles in search results in Sharepoint 2013

During implementation of search-driven site on Sharepoint 2013 we faced with the following problem: for some pages titles were cut on search results page:


I.e. for most pages titles were ok, but for some of them they were cut as shown on the picture above, e.g. “Find the right solution” were cut to “he right”, “We as business partner” to “as busine”, etc. These titles are not exactly the same as on the real site, but they should show the basic problem. There were no visible consistent pattern for the cutting: for some titles several full words were included, for another words were cut and shown part didn’t contain the keywords added by user in the search box.

Investigation showed that the problem comes from standard Item_CommonItem_Body.html display template. Title is shown with the following code inside it:

   1: var title = Srch.U.getHighlightedProperty(id, ctx.CurrentItem, "Title");
   2: ...
   3: var titleHtml = String.format(
   4: '<a clicktype="{0}" id="{1}" href="{2}" class="ms-srch-item-link" ' +
   6: $htmlEncode(clickType), $htmlEncode(id + Srch.U.Ids.titleLink), $urlHtmlEncode(url),
   7: $htmlEncode(ctx.CurrentItem.Title), showHoverPanelCallback, appAttribs,
   8: Srch.U.trimTitle(title, maxTitleLengthInChars, termsToUse));
   9: ...
  10: <div id="_#= $htmlEncode(id + Srch.U.Ids.title) =#_" class="ms-srch-item-title"> 
  11:     <h3 class="ms-srch-ellipsis">
  12:         _#= titleHtml =#_
  13:     </h3>
  14: </div>

I.e. at first it gets the highlighted title using Srch.U.getHighlightedProperty() method (line 1), and then trims it for adding into the “a” tag using Srch.U.trimTitle() method (line 8). The first thing which came to my mind was that trimTitle method worked incorrectly. This method (as well as getHighlightedProperty) is defined in Search.ClientControls.js file, which is located in 15/template/layouts folder. I added temporary traces into it using console.log and found that wrong title is returned already from getHighlightedProperty method. Here is the code of this method:

   1: Srch.U.getHighlightedProperty =
   2: function Srch_U$getHighlightedProperty(key, result, property) {
   3:     var $v_0 = null;
   4:     if (!Srch.U.e(key)) {
   5:         if (!((key) in Srch.ScriptApplicationManager.get_current().$23_1)) {
   6:             Srch.ScriptApplicationManager.get_current().$23_1[key] =
   7:                 Srch.U.$5H(result['HitHighlightedProperties']);
   8:         }
   9:         var $v_1 = Srch.ScriptApplicationManager.get_current().$23_1[key];
  10:         if (!Srch.U.n($v_1)) {
  11:             if (property === 'Title') {
  12:                 property = 'HHTitle';
  13:             }
  14:             else if (property === 'Path') {
  15:                 property = 'HHUrl';
  16:             }
  17:             else {
  18:                 property = property.toLowerCase();
  19:             }
  20:             $v_0 = $v_1[property];
  21:         }
  22:     }
  23:     return $v_0;
  24: }

key parameter passed to this method is the id of the item shown in the search results, result is ctx.CurrentItem and property in our case is “Title”. Using traces I checked that it goes to line 12 (property = 'HHTitle') and then reads property “HHTitle” from object, which is returned from the following call: Srch.U.$5H(result['HitHighlightedProperties']) (lines 6-7). I.e. the problem is caused by incorrect calculation of the highlighted title stored in ctx.CurrentItem[‘HitHighlightedProperties’].

Test showed that current browser language affects this method, i.e. title may be cut for one language, but shown properly for another. As a workaround for this problem you may change the code of Item_CommonItem_Body.html display template to the following:

   2: 'title="{3}" onfocus="{4}" {5}>{3}</a>',
   4: $htmlEncode(ctx.CurrentItem.Title), showHoverPanelCallback, appAttribs);

I.e. use $htmlEncode(ctx.CurrentItem.Title) which shows original title, instead of highlighted title. However with this approach your titles won’t be highlighted anymore.

This behavior looks like a bug in search components. If you faced with the same problem or if you know another solutions and what causes it, please share it in comments.