Monday, August 25, 2014

Sitecore Access Control: Adding New Users

If you have ever run into situations where you added a new user but then the user is not able to log in to the UI, it is because the new user is not assigned the role "sitecore content users".  You can either assign that role manually or assign the role "sitecore client authoring" which also includes the "sitecore content users" role.  There are other roles that includes the content users role as well.

Tuesday, July 29, 2014

Sitecore Quick Tip: Image control Resizing

If all else fails in trying to make images resize for the Sitecore sc:image web control, you can always give this a try.  Just change the CssStyle attribute instead of using the built-in width and height attributes.



<sc:Image runat="server" ID="logoImage" Field="header logo" DataSource="<%# DataSource %>" CssStyle="height:10px;width:auto"/>
 



Thursday, July 10, 2014

Sitecore Rant: Lucene Index of System Fields

Lucene Index of System Fields

On a current project, there is a need to index the system language fields.  By default, there is an index that takes care of all items in "/sitecore/system" called "system_index".  This contains everything.  I don't want that.  I just want a list of all the languages so I created a new index and added to the "Sitecore.ContentSearch.Lucene.Indexes.Sharded.Master.config" file.  Simple enough.  I also have to define the fields that I want to have indexed.  To do that we open the "Sitecore.ContentSearch.Lucene.DefaultIndexConfiguration.config" file and define the fields that we want to index.




Notice that we have two kinds of fields in this list.  We have the default fields on top and three custom fields at the bottom.  For all custom fields that are added post-install, the field names are as is.  For most system fields, there are usually underscores either at the beginning of the name or in the middle.  Sometimes there are two leading underscores.  This is used to indicate system fields that are part of the standard template. The field names for "code_page", "regional_iso_code" and "worldlingo_language_identifier" are default system fields and would make sense to have underscores connecting the words.  Also, they are obtained from the section of excluded fields down below:



Note: Keep in mind that unless you remove the entry from the list of excluded fields, the field will not get indexed even if you tell it to in the previous screenshot.

The regional iso code is a good example to explain my rant.  This basically says to the indexer to ignore this field with the ID.  If you inspect the template for the system language, you can indeed see the ID does match up with the field so you would naturally believe the field name is also correct.  WRONG!  If you run the indexer, the value for this field will always be null because there is no such field with this name.  After debugging and looping through all the fields using item.Fields, we can see that the field name is simply "Regional Iso Code" and not "Regional_iso_code". 

Why would Sitecore decide to use this kind of notation for this field and others while some default fields look like this?



I know that there cannot be spaces in the html element tags but if you use underscores for some fields and camel case for others, it would lead us to believe that the underscores are deliberate and indicate the real field names.  If not, then why not just use camel case for all fields definitions in the excluded list?

Also, there is no way to know what the field names are unless you iterate through item.Fields for all the field names.  If you try looking at the config file above, you will be misguided very often, at least in the section of excluded fields.  The correct index entry for all the fields would be:

Tuesday, May 27, 2014

Sitecore and Solr: Configure DataImportHandler for External Data Extraction

These instructions are based on a Sitecore 7.0 installation of SOLR and will use the folder structures based on it with Jetti, SOLR 4.5, multiple cores, etc.  It will also assume the installation of SOLR is up and running.

1) Edit the solrconfig.xml file for the current core
Example: \example\solr\core\conf\

2) Add new request handler:

----------- 
<!-- DataImporter -->
  <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
    <lst name="defaults">
      <str name="config">data-config.xml</str>
    </lst>
  </requestHandler>
------------

3) Create new file "data-config.xml" in the same folder or wherever the path is as specified in the requestHandler

Example of file content:

<?xml version="1.0" encoding="UTF-8" ?>
<dataConfig>
<dataSource name="ds1"
            type="JdbcDataSource"
            driver="com.microsoft.sqlserver.jdbc.SQLServerDriver"
            url="jdbc:sqlserver://server;databaseName=dbname"
            user="user"
            password="pwd"
            readOnly="true" />
    <document>
        <entity name="user"
            dataSource="ds1"
            query="select * from [dbo].[users]"
            >
            <field column="id" name="_id" />
            <field column="email" name="_email" />           
            <field column="first_name" name="_fname" />
            <field column="last_name" name="_lname" />       
        </entity>
    </document>
</dataConfig>

Datasource type is jdbcdatasource
Driver is com.microsoft.sqlserver.jdbc.SQLServerDriver
Url format is jdbc:sqlserver://server;databaseName=dbname
Also make sure column names do not have spaces or weird characters

4) Make sure the dataimport jar files are in place.  You can get the solr-dataimporthandler*.jar from the dist folder.

Copy the files into:
\example\solr-webapp\webapp\WEB-INF\lib\


5) Install SQL Server JDBC driver
http://msdn.microsoft.com/en-us/sqlserver/aa937724

Run the downloaded installer and you will be prompted to unzip the files to a location. 
Unzip to any location.

In the unzipped folder look for \sqljdbc_3.0\enu\sqljdbc4.jar and copy to:

\example\solr-webapp\webapp\WEB-INF\lib\


6) Verify that everything has worked by browsing to you SOLR admin URL and selecting the core with the dataimporter.  Click the dataimport tab and execute.  If all is well, your indexes would be created and you can perform queries as usual.


Friday, May 16, 2014

Obtain Sitecore Context Item From Custom Control Field

Let's say you are not satisfied with the out-of-the-box Image field control or you just want to build a totally new cool custom field.  You would do this by inheriting from either an existing field or from the Sitecore.Web.UI.HtmlControls class.

public class Image : Sitecore.Shell.Applications.ContentEditor.Image
{ ... }

Since this is a field control, there is no Execute method like a Command that accepts a CommandContext input parameter, how do we extract the context item that contains the current field we are looking at?

You can try calling the base method GetItem() but this returns the Content Editor item and is not what you want.  You can use Intellisense to bring up a whole bunch of other base methods that has the word "Item" in the name but none of those help.  They either return the Content Editor item or the field control as an item but not exactly the context item that contains the current field you are looking at.

Fortunately there is Viewstate information.  This information is only available when you dig deeper into the base class Sitecore.Web.UI.HtmlControls.  If you inspect this class via a decompiler like dotPeek, you can see extensive references to the GetViewStateString or GetViewStateBool methods.  These methods obtain values saved as key/value pairs that are part of the Viewstate information.  The key that we are concerned with in this scenario is "ItemId".

Simply put, to obtain the context item id, we have to put this property in our custom field control class:

public string ItemID
{
            get
            {
                return base.GetViewStateString("ItemID");
            }
            set
            {
                Assert.ArgumentNotNullOrEmpty(value, "value");
                base.SetViewStateString("ItemID", value);
            }
}

Then, to resolve the actual Sitecore item, you can call something like:

Sitecore.Data.Database.GetDatabase("master").GetItem(ItemID)
 
 

Friday, May 9, 2014

Sitecore: Multi-site Setup with Bing Analytics Files (BingSiteAuth.xml)

Recently, we ran into a dilemma involving a Sitecore multi-site environment.  As we know, this means, one instance of Sitecore, one content tree, one code base, one root directory, but multiple sites reading from different nodes in the tree.  Since we only have one code base running on one instance of IIS, how can we possibly have different files for each domain in the root level?

Simply put, Bing analytics requires that each website has a file called "BingSiteAuth.xml" be dropped into the root directory of each website.  This file contains a unique ID number that the Bing search engine uses to identify your site as legitimate.  In a normal single website instance with or without Sitecore, we can just copy this file into the root directory.  Easy enough, problem solved.  But in a multi-site Sitecore setup with one code base shared among all your sites, how can this be achieved?  All your sites would need to have a file with the same file name but since all the sites share a common set of files, all you can have is one copy of that file shared by all your sites.  This is not just a Sitecore dilemma, but a dilemma for all CMS sites that have the multi-site setup option.  Of course if Bing altered their way to make sure every file have unique names, that would be great so you can have 50 files with different names for 50 different sites.  But this is not the case, so we have to find a way to get around it.

One way to achieve this is by creating an Http Handler.  As we all know, an Http Handler basically tells IIS what to do when a file with a specific name or extension is encountered.  For our purposes, we would need to create a handler for the "BingSiteAuth.xml" file name.  Let's begin.

1) Create a new handler class that inherits from System.Web.IHttpHandler:



2) You must implement ProcessRequest and IsReusable methods to satisfy the interface.

3) In ProcessRequest, this is where you do the bulk of the work.  You need to detect the current website and output some text.  We have decided not to stream a file, but rather save the file information in a text field in each of the website home content items.  But since this is a standard Http Handler and not something in Sitecore, we do NOT have access to the Sitecore context because it is not yet resolved at the time the handler is fired.  What do we do now?  Rest assured that even though the context is not available, we can use Sitecore factory methods to pull up site information in multiple steps.  All you have to do is figure out if the "sc_site" parameter is set.  If it is, we have the site name.  If not, we could use the domain name to perform the lookup.  Of course, all this information has to match the <sites> defined in the web.config file.



At this point, we should have the site information.  Now we need to get at the starting node of this site and check the field that contains the text for the BingSiteAuth.xml file.  If the field is not defined or there is no value for the field, we throw a 404 exception.



4) Now we have the working handler but we have to register he handler.  To do that we have to append an entry to the web.config section in two places:

<system.webServer><handlers>

and

<system.web><httpHandlers>



Keep in mind that the same entry is used in both places except the version inside httpHandlers does not have the name attribute.

5) Make sure to modify the template of the home item to include a field called "BingSiteAuth".



6) Rebuild your code and try browsing to:

http://www.mycoolsite.com/BingSiteAuth.xml.  You should see a file with the exact information stored in the field above.