5 passos para o Sitecore suportar Source Parameters e Datasources dinamicos em fields TreeList

Recentemente eu trabalhei em um projeto multi-site em Sitecoore com requisitos específicos. Eu precisava que o field Treelist suportasse queries com datasources dinamicos funcionando em paralelo com source parameters. Neste caso é importante mencionar que esse field era usado no contexto de um template customizado da media library, que nesse caso, não tinha contexto do site, então isso era necessário ser determinado dinamicamente.
Como ja explicado nesse post do Kamruz, queries e source parameters não funcionam em paralalo sem criar um novo field.

O resultado final que estava tentando alcançar, era o field Source declarado dessa forma:
DataSource=query:../../&ExcludeTemplatesForSelection=NotDesirableTemplate.
Mas há um problema com a query acima. Eu precisava de alguma forma determinar o caminho dinamicamente, já que como mencionado, seria necessário primeiro determinar o site e colocar o caminho baseado no site.

Ficou confuso?  Lembre: Estamos tentando ter o field TreeList suportando Sources dinamicos e ainda usar paremeters, por exemplo ExcludeTemplatesForSelection.
Abaixo eu descrevo 5 passos, para implementar esse requisito com algumas considerações importantes de se ter presente.

 

1. Criar um token para a query

DataSource=query:#mytoken#&ExcludeTemplatesForSelection=NotDesirableTemplate.

Seguinte nós precisamos fazer duas coisas: Extender o field TreeList e implementar uma forma de manipular o #mytoken#, para substituir o token com o caminho do site.

 

2. Criar um novo Field

Após logar no Sitecore e acessar o Desktop, mude para o banco Core e crie um novo List Type. Para fazer isso, navegue até o caminho: /sitecore/system/Field Types/List Types e crie um novo List Type. No meu exemplo eu chamei de ‘CustomTreeList’.
Depois preencha os campos Assembly e Class. Abaixo uma imagem de como ficou definido no meu caso:

3. Implement o novo TreeList Field

Agora precisamos implementar o novo field e o token definido no passo 1. Eu usei o código do Kamruz como base e adaptei para este requisito. No final ficou definido dessa forma:

 


namespace MyNamespace.CustomFields
{
    public class CustomTreeList : Sitecore.Shell.Applications.ContentEditor.TreeList
    {
        private string _ds = string.Empty;


        /// <summary>
        /// Override the TreeList DataSource property to support the mytoken on the Source
        /// </summary>

        public override string DataSource
        {
            get
            {
                if (_ds.StartsWith("query:"))
                {
                    if (Sitecore.Context.ContentDatabase == null || base.ItemID == null)
                        return null;
                    var current = Sitecore.Context.ContentDatabase.GetItem(base.ItemID);

                    if (_ds.Contains("#mytoken#"))
                    {                       
                        //uses the current media item to resolve what the tenant which it belongs to
                        var myTenant = Tenant.GetTenant(new ID(ItemID));
                        if (myTenant != null)
                        {
                            var sourceFolder = myTenant.GetChildren().FirstOrDefault(i=>; i.Template.ID.ToString() == Constants.Home.ID);
                            if (sourceFolder != null)
                            {
                                //found the Home item under the tenant item so replace the datasource with the Home Path
                                _ds = _ds.Replace("#mytoken#", sourceFolder.Paths.FullPath);
                            }
                        }
                        else
                        {
                            Log.Warn("Could not find tenant for item "+ItemID+ " : the path /sitecore/content is returned by default. This could be because the item is _standard values" , ItemID);
                            _ds = "/sitecore/content";
                        }

                    }

                    Item item = null;
                    try
                    {
                        item = Enumerable.FirstOrDefault<Item>((IEnumerable<Item>)LookupSources.GetItems(current, _ds));
                    }
                    catch (Exception ex)
                    {
                        Log.Error("Treelist field failed executing the query", ex, (object)this);
                    }

                    return item == null ? null : item.Paths.FullPath;
                }
                return _ds;
            }
            set { _ds = value; }
        }
    }

}

 

O que esse código esta fazendo é, pegar o #mytoken# e definido no Source e sobrescrever com o item Home do Site que ele pertence.
Por exemplo, se voce tem uma árvore de conteúdo que tem um item de midia que resolve para o site 1. após executar esse código, o resultado do Source ficaria assim definido:

DataSource=query:/Sitecore/Content/Site1/Home&ExcludeTemplatesForSelection=NotDesirableTemplate.


Isso funciona, porém não acabamos ainda
.

 

Este é um field TreeList, o que significa que ele usa o Link Database. De maneira que se queira manter as funcionalidades relacionadas a links quebrados, atualização de links, etc, você precisa informar o Sitecore o que precisa acontecer quando esse field for feito o rebuild no painel de controle do Sitecore.
Outra coisa que será necessário fazer é garantir que esse field funciona com Content Search.

4. Atualizar o Link Database

Conforme mencionado, para manter compatibilidade com as funcionalidades do link database, voce precisa implementar um novo field que deriva do tipo CustomField e registrá-lo. A forma de fazer isso é bem explicada aqui. Registrando o field:

 



<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <fieldTypes>
            <!--Used to update the link database-->
            <fieldType name="CustomTreeList" type="MyNamespace.CustomFields.CustomTreeListType, MyNamespace" />
        </fieldTypes>
    </sitecore>
</configuration>


Implementação da Classe:

namespace MyNamespace.CustomFields
{
    public class CustomTreeListType : CustomField
    {
        public CustomTreeListType(Field innerField) : base(innerField)
        {
        }

        public CustomTreeListType(Field innerField, string runtimeValue) : base(innerField, runtimeValue)
        {
        }
       
        private void AddLink(ID id)
        {
            if (!Value.EndsWith("|") && Value.Length > 0)
            {
                Value += "|";
            }
            Value += id.ToString();
        }

        
        private void ClearLink(ID targetItemID)
        {
            Value = Value.Replace(targetItemID.ToString(), "");
            Value = Value.Replace("||", "|");
            Value = Value.TrimEnd('|');
            Value = Value.TrimStart('|');
        }

        private void ClearAllInvalidLinks()
        {
            foreach (var itemId in Value.Split('|'))
            {
                var targetItem = InnerField.Database.GetItem(new ID(itemId));
                if (targetItem != null)
                    ClearLink(targetItem.ID);
            }
        }
       
        public override void Relink(ItemLink itemLink, Item newLink)
        {
            Assert.ArgumentNotNull(itemLink, "itemLink");
            Assert.ArgumentNotNull(newLink, "newLink");
            Database database = Factory.GetDatabase(itemLink.TargetDatabaseName);
            if (database != null)
            {
                Item targetItem = database.GetItem(itemLink.TargetItemID);
                if (targetItem == null)
                {
                    ClearLink(itemLink.TargetItemID);
                }
                else
                {
                    if (Value.Contains(itemLink.TargetItemID.ToString()))
                    {
                        Value = Value.Replace(itemLink.TargetItemID.ToString(), newLink.ID.ToString());
                    }
                    else
                    {
                        AddLink(newLink.ID);
                    }
                }
            }
        }

        public override void RemoveLink(ItemLink itemLink)
        {
            Assert.ArgumentNotNull(itemLink, "itemLink");
            ClearLink(itemLink.TargetItemID);
        }

        public override void UpdateLink(ItemLink itemLink)
        {
            Assert.ArgumentNotNull(itemLink, "itemLink");
            Database database = Factory.GetDatabase(itemLink.TargetDatabaseName);
            if (database == null)
            {
                ClearLink(itemLink.TargetItemID);
            }
            else
            {
                ClearAllInvalidLinks();
                Item targetItem = database.GetItem(itemLink.TargetItemID);
                if (targetItem != null)
                    AddLink(targetItem.ID);
            }
        }

        public override void ValidateLinks(LinksValidationResult result)
        {
            Assert.ArgumentNotNull(result, "result");

            string itemIds = this.Value;
            if (string.IsNullOrEmpty(itemIds))
            {
                Log.Info("Validate links ID is empty", itemIds);
                return;
            }
                
            Log.Info("IDs = "+Value, result);
            foreach (var itemId in this.Value.Split('|'))
            {
                var targetItem = this.InnerField.Database.GetItem(new ID(itemId));
                if (targetItem != null)
                    result.AddValidLink(targetItem, targetItem.Paths.FullPath);
                else
                    result.AddBrokenLink(itemId);
            }
        }
    }
}


5. Mantendo compatibilidade com Content Search

Estamos quase finalizando! A última parte é armazenar o novo field de maneira que ele funcione com Content Search. Do contrário, você terá problemas como esse here.
Como explicado na resposta do Richar Seal, a maneira defazer os resultados returnarem é criando um novo arquivo de configuração que muda o valor de StorageType=Yes. No meu caso, fico assim definido:
FieldTypeName=”CustomTreeList” and StorageType=”Yes.”

 


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
    <sitecore>
        <contentSearch>
            <indexConfigurations>
                <defaultLuceneIndexConfiguration>
                    <fieldMap>
                        <fieldTypes hint="raw:AddFieldByFieldTypeName">
                            <!--needed to set the storagetype=Yes so the field shows within the index-->
                            <fieldType fieldTypeName="customtreelist" storageType="YES" indexType="TOKENIZED" vectorType="NO" boost="1f" type="System.String" settingType="Sitecore.ContentSearch.LuceneProvider.LuceneSearchFieldConfiguration, Sitecore.ContentSearch.LuceneProvider" patch:after="*[1]" />
                        </fieldTypes>
                    </fieldMap>
                </defaultLuceneIndexConfiguration>
            </indexConfigurations>
        </contentSearch>
    </sitecore>
</configuration>

Espero que este post seja util para vocês!

Advertisements

Sitecore 8: O atribute Hostname retorna vazio mesmo contendo um valor na configuração do site. O que pode ser?

Recentemente eu trabalhei em um projeto de Sitecore onde eu estava criando uma lista de sites dinamicamente assim:

sites = SiteManager.GetSites().Select(s => new SiteInfo(s.Properties)).ToList();

em mode de debug eu conseguia ver os valores do atributo hostname definidos corretamente para a configuração do site em questão. Mas ao construir o objeto ele não carregava o valor do hostname.

Eu verifiquei a pagina showconfig e vi a seguinte definição para o site website:

 

<site name="website" enableTracking="true" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content/brands/clientname" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="50MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="25MB" filteredItemsCacheSize="10MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" cacheRenderingParameters="true" renderingParametersCacheSize="10MB" language="en" patch:source="Client.Brands.ClientName.Project.ClientName.config" itemwebapi.mode="Off" itemwebapi.access="ReadOnly" itemwebapi.allowanonymousaccess="false" enableItemLanguageFallback="false" enableFieldLanguageFallback="false" hostname="clientname.dev.local|local.clientname.com"/>

Conforme respondido aqui, eu precisei mudar o atributo hostname para usar a letra maiúscula N assim: hostName, porque os atributos das definições dos sites respeitam maiúsculo e minúsculo. Apos realizar essa mudança ele começou a carregar corretamente o valor do atributo hostname. Você pode ler mais sobre os atributos das definições de sites aqui

 

Sitecore 8: Hostname not being retrieved for the website definition. What could be wrong?

Recently I have been working on a Sitecore project and I was building a list of sites dynamically like this:

sites = SiteManager.GetSites().Select(s => new SiteInfo(s.Properties)).ToList();

The problem is that at debug mode I could see the values of the hostname attributes for the website definition properly set. But it would not populate the property with the value from the config. in other words the value would return blank for the hostname attribute.

I checked the showconfig page and got the following definition:

<site name="website" enableTracking="true" virtualFolder="/" physicalFolder="/" rootPath="/sitecore/content/brands/clientname" startItem="/home" database="web" domain="extranet" allowDebug="true" cacheHtml="true" htmlCacheSize="50MB" registryCacheSize="0" viewStateCacheSize="0" xslCacheSize="25MB" filteredItemsCacheSize="10MB" enablePreview="true" enableWebEdit="true" enableDebugger="true" disableClientData="false" cacheRenderingParameters="true" renderingParametersCacheSize="10MB" language="en" patch:source="Client.Brands.ClientName.Project.ClientName.config" itemwebapi.mode="Off" itemwebapi.access="ReadOnly" itemwebapi.allowanonymousaccess="false" enableItemLanguageFallback="false" enableFieldLanguageFallback="false" hostname="clientname.dev.local|local.clientname.com"/>

As pointed out here (thanks @MarasM) I needed to change the hostname to use the capital N as in hostName because the site attributes are case sensitive. Changing it made it to start working so I decided to post this to help out anyone who faces this issue.

You can read more about site attributes here.

Problemas ao sincronizar o Unicorn. Qual era e o como resolvi

Recentemente eu trabalhei em um projeto e eu tive um problema ao tentar sincronizar o Unicorn. Por conta disso resolvi escrever este post para compartilhar o aprendizado.

O Problema:

Eu tentava sincronizar, reserializar as minhas configurações mas sempre aparecia esta tela:

screen1

As definições das minhas configurações eram as seguintes(note a parte em negrito):

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”&gt;

<sitecore>

<unicorn>

<configurations>

<configuration name=”Feature.Navigation” description=”Feature Navigation” dependencies=”Foundation.Serialization” patch:after=”configuration[@name=’Foundation.Serialization’]”>

<targetDataStore physicalRootPath=”$(sourceFolder)\feature\navigation\serialization” type=”Rainbow.Storage.SerializationFileSystemDataStore, Rainbow” useDataCache=”false” singleInstance=”true” />

<predicate type=”Unicorn.Predicates.SerializationPresetPredicate, Unicorn” singleInstance=”true”>

<include name=”Feature.Navigation.Templates” database=”master” path=”/sitecore/templates/Feature/Navigation” />

<include name=”Feature.Navigation.Renderings” database=”master” path=”/sitecore/layout/renderings/Feature/Navigation” />

 <include name=”Feature.Navigation.Media” database=”master” path=”/sitecore/media library/Feature/Navigation” />

</predicate>

<roleDataStore type=”Unicorn.Roles.Data.FilesystemRoleDataStore, Unicorn.Roles” physicalRootPath=”$(sourceFolder)\feature\navigation\serialization\Feature.Navigation.Roles” singleInstance=”true”/>

<rolePredicate type=”Unicorn.Roles.RolePredicates.ConfigurationRolePredicate, Unicorn.Roles” singleInstance=”true”>

<include domain=”modules” pattern=”^Feature Navigation .*$” />

</rolePredicate>

</configuration>

</configurations>

</unicorn>

</sitecore>

</configuration>

A Solução

Após revisar as configurações, notei que elas pareciam estar corretas então eu revisei todos os includes definidos nas configurações. Ao fazer esse exercício eu percebi que na seção Media, havia um caminho que não existia na arvoré de conteúdo do Sitecore. Estava faltando a pasta Navigation no caminho sitecore/media library/Feature.

Quando eu criei esta pasta o Unicorn passou a sincronizar normalmente. Em outras palavras, se você não estiver conseguindo sincronizar, revise as suas configurações e os caminhos definidos no Sitecore, e veja se eles todos estão corretos.

Talvez isso seja útil e evite perdas de tempos no futuro para alguém 🙂

screen

Sync issue with Unicorn. What was it and how I fixed it.

Recently I have worked on a project and I had an issue syncing Unicorn and I decided to write this post to share what was it and how I fixed it.

The Problem:

I tried syncing, reserializing Unicorn but I would still keet getting this screen:

screen1

This is what my configuration looked like(Note the bolded section):

<configuration xmlns:patch=”http://www.sitecore.net/xmlconfig/”&gt;

<sitecore>

<unicorn>

<configurations>

<configuration name=”Feature.Navigation” description=”Feature Navigation” dependencies=”Foundation.Serialization” patch:after=”configuration[@name=’Foundation.Serialization’]”>

<targetDataStore physicalRootPath=”$(sourceFolder)\feature\navigation\serialization” type=”Rainbow.Storage.SerializationFileSystemDataStore, Rainbow” useDataCache=”false” singleInstance=”true” />

<predicate type=”Unicorn.Predicates.SerializationPresetPredicate, Unicorn” singleInstance=”true”>

<include name=”Feature.Navigation.Templates” database=”master” path=”/sitecore/templates/Feature/Navigation” />

<include name=”Feature.Navigation.Renderings” database=”master” path=”/sitecore/layout/renderings/Feature/Navigation” />

 <include name=”Feature.Navigation.Media” database=”master” path=”/sitecore/media library/Feature/Navigation” />

</predicate>

<roleDataStore type=”Unicorn.Roles.Data.FilesystemRoleDataStore, Unicorn.Roles” physicalRootPath=”$(sourceFolder)\feature\navigation\serialization\Feature.Navigation.Roles” singleInstance=”true”/>

<rolePredicate type=”Unicorn.Roles.RolePredicates.ConfigurationRolePredicate, Unicorn.Roles” singleInstance=”true”>

<include domain=”modules” pattern=”^Feature Navigation .*$” />

</rolePredicate>

</configuration>

</configurations>

</unicorn>

</sitecore>

</configuration>

The Solution

So after reviewing the configurations and validating that they seemed to be correct, I reviewed all the include mappins. So when I got to the Media section I noticed that the path defined on the configuration file didn’t exist within Sitecore’s content tree. It turned out that it was missing the Navigation folder under /sitecore/media library/Feature.

When I created it, unicorn started syncing properly again.  In other words, if you are not being able to sync double check if your configs and the paths underneath Sitecore  are all valid. This was the problem in my case. Hopefully this will save anyone sometime in the future.

screen

Non-default language is being used in Sitecore and is causing it to throw exceptions. What to do?

Recently I have been involved in a Sitecore project where in the implementation of the project a non-default language was created under system/languages called es-BR(Sitecore’s brazilian portuguese definition is pt-BR. I highly suggest that you use the standard notation but if you don’t have that option keep reading 😉 ).

When the publish mechanism got triggered Sitecore would throw the following exception:

Message: Item cannot be renamed to ‘es-BR’ as it is not a valid language name.

Source: Sitecore.Kernel

   at Sitecore.Diagnostics.Assert.IsTrue(Boolean condition, String message)

   at System.EventHandler`1.Invoke(Object sender, TEventArgs e)

   at Sitecore.Data.Engines.EngineCommand`2.RaiseEvent[TArgs](EventHandler`1 handlers, Func`2 argsCreator)

   at Sitecore.Data.Engines.EngineCommand`2.RaiseExecuting(Boolean& cancelled)

   at Sitecore.Data.Engines.EngineCommand`2.CanExecute()

   at Sitecore.Data.Engines.EngineCommand`2.Execute()

   at Sitecore.Data.Engines.DataEngine.SaveItem(Item item)

   at Sitecore.Data.Managers.ItemProvider.SaveItem(Item item)

   at Sitecore.Data.Items.ItemEditing.AcceptChanges(Boolean updateStatistics, Boolean silent)

   at Sitecore.Data.Items.ItemEditing.EndEdit(Boolean updateStatistics, Boolean silent)

   at Sitecore.Data.Items.EditContext.Dispose()

   at Sitecore.Publishing.PublishHelper.CopyToTarget(Item sourceVersion)

   at Sitecore.Publishing.PublishHelper.PublishVersionToTarget(Item sourceVersion, Item targetItem, Boolean targetCreated)

   at Sitecore.Publishing.Pipelines.PublishItem.PerformAction.ExecuteAction(PublishItemContext context)

   at Sitecore.Publishing.Pipelines.PublishItem.PerformAction.Process(PublishItemContext context)

   at (Object , Object[] )

   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)

   at Sitecore.Publishing.Pipelines.PublishItem.PublishItemPipeline.Run(PublishItemContext context)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.Process(PublishContext context)

Interestingly the same error would be throw if you would try to delete it from the content tree. While I was investigating how I could address this issue I noticed that the language would not appear on Sitecore’s Control Panel /Globalization menu, but it would show on the content tree.

What I had to do to fix that was to ensure that the language was registered in the Windows system. You could use the Language Registration tool available in the Sitecore’s marketplace. After that I had to add the node to \App_Config\LanguageDefinitions.config file:

<language id=”es” region=”BR” codepage=”65001″ encoding=”utf-8″ charset=”iso-8859-1″ icon=”flags/16×16/flag_Brazil.PNG” />

 

Uma linguagem não padrão está sendo utilizada no Sitecore e estourando exceções. O que fazer?

Recentemente eu estive envolvido em um projeto de Sitecore, no qual na implementação do projeto foi definida uma linguagem não padrão, no caminho /system/languages. A linguagem era “es-BR”(o idioma padrão para português do Brasil é pt-BR e não es-BR. Eu pessoalmente recomendo utiliizar sempre as linguagens padrões, porém se esse não for o seu caso continue lendo 😉 )

Quando o mecanismo de publiicação era disparado, o Sitecore estourava a seguinte exceção:

Message: Item cannot be renamed to ‘es-BR’ as it is not a valid language name.

Source: Sitecore.Kernel

   at Sitecore.Diagnostics.Assert.IsTrue(Boolean condition, String message)

   at System.EventHandler`1.Invoke(Object sender, TEventArgs e)

   at Sitecore.Data.Engines.EngineCommand`2.RaiseEvent[TArgs](EventHandler`1 handlers, Func`2 argsCreator)

   at Sitecore.Data.Engines.EngineCommand`2.RaiseExecuting(Boolean& cancelled)

   at Sitecore.Data.Engines.EngineCommand`2.CanExecute()

   at Sitecore.Data.Engines.EngineCommand`2.Execute()

   at Sitecore.Data.Engines.DataEngine.SaveItem(Item item)

   at Sitecore.Data.Managers.ItemProvider.SaveItem(Item item)

   at Sitecore.Data.Items.ItemEditing.AcceptChanges(Boolean updateStatistics, Boolean silent)

   at Sitecore.Data.Items.ItemEditing.EndEdit(Boolean updateStatistics, Boolean silent)

   at Sitecore.Data.Items.EditContext.Dispose()

   at Sitecore.Publishing.PublishHelper.CopyToTarget(Item sourceVersion)

   at Sitecore.Publishing.PublishHelper.PublishVersionToTarget(Item sourceVersion, Item targetItem, Boolean targetCreated)

   at Sitecore.Publishing.Pipelines.PublishItem.PerformAction.ExecuteAction(PublishItemContext context)

   at Sitecore.Publishing.Pipelines.PublishItem.PerformAction.Process(PublishItemContext context)

   at (Object , Object[] )

   at Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args)

   at Sitecore.Publishing.Pipelines.PublishItem.PublishItemPipeline.Run(PublishItemContext context)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.ProcessEntries(IEnumerable`1 entries, HashSet`1 publishedReferredItems, PublishContext context, Boolean processReferencedItems)

   at Sitecore.Support.Publishing.Pipelines.Publish.ProcessQueue.Process(PublishContext context)

Interessante que o mesmo erro estourava quando se tentava deletar o item da arvore de conteúdo, e estava se tornando um problema para os editores de conteúdo. Enquanto eu investigava o problema eu notei que a linguagem não aparecia no item “Globalization” do painel de controle do Sitecore, mas aparecia na árvore de contúdo.

O que eu tive que fazer para resolver este problema foi registrar a linguagem no Windows. Para fazer isso voce pode utilizar a ferramenta Language Registration, disponível  na marketplace da Sitecore. Após registrar este idioma eu tive que adicionar a entrada do idioma no arquivo App_Config\LanguageDefinitions.config:

<language id=”es” region=”BR” codepage=”65001″ encoding=”utf-8″ charset=”iso-8859-1″ icon=”flags/16×16/flag_Brazil.PNG” />

A partir dai foi possível excluir o idioma sem problemas.

Sitecore – 7 passos em como redirectionar um domínio meudominio.com para www.meudominio.com

De tempos em tempos eu me deparo com o seguinte requisito:

“Eu quero que os links do meu site rodando em Sitecore passem a redirecionar de meudominio.com para http://www.meudominio.com

Como se pode fazer isso? Na verdade existem algumas maneiras. Eu vou descrever uma delas que é fácil e funciona bem.

Seguem 7 passos de como realizar isso. Mas antes de mais nada você vai precisar o módulo IIS Rewrite para que isso funcione. Você pode fazer o download aqui.

 

Existem duas partes que necessitam ser configuradas:

There are two part that need to be properly configured:

  1.  O site definition do Sitecore
  2. Criar uma regra usando o módulo IIS rewrite que cuidará da parte de redirecionamento.
  3. Create a rule using this module to take care of the redirection piece

1 – atualizar o atributo hostname  do site definiton para resolver ambas urls.

Na sua solução abra o arquivo SiteDefiniion.config. Por padrão ele fica localizado na pasta App_config/Include.

Observação: esta configuração assume que voce removeu o .example do arquivo SiteDefinition.config. Se você tiver um arquivo customizado para a definição de configurações do site, é neste arquivo que voce deve realizar esta alteração.

Localize neste arquivo o site que você quer alterar e atualize o atributo hostName para resolver ambos http://www.meudominio.com e meudominio.com. Para isso voce pode adicionar duas entradas separadas pelo caracter “|” ou adicionar um “*” e deixar o Sitecore resolver(mais recomendado).

Opção 1: hostName=”meudominio.com|www.meudominio.com”

Opção 2: hostName=”*meudominio.com”.

Depois de realizar esta configuração o Sitecore já estará resolvendo estes domínios, porém o IIS não.

 

2 – No IIS voce precisa ter um binding que resolva http://www.meudominio.com para o site. Para fazer isso abra o site no IIS e clique em bindings conforme abaixo:

b1_step1

 

 3 – Crie um binding que resolva http://www.meudominio.com

Clique em Add e configure o hostname com o valor http://www.meudominio.com e clique OK. Você verá a seguinte tela:

b1_step2

 

4 – Abra a seção do URL rewrite do seu site.

Selecione o seu site no IIS e procure pelo botão URL Rewrite e clique nele.

5 – Clique em Add Rules(s)

b1_step3

– Clique em canonical domain name conforme abaixo:

b1_step4

7 – Selecione o host primário neste caso http://www.meudominio.com. Depois disso clique OK.

b1_step5

 

É isso. Agora todo o tráfego da sua instância de Sitecore vindo de meudominio.com será redirecionado para http://www.meudominio.com

 

 

 

Sitecore – 7 steps on how to enforce a website’s url from domain.com to www.domain.com

From time to time I have seen a request like this:

“I want all the links from my Sitecore website to start redirecting from myhostname.com to www.myhostname.com.”

How do can this be done? There are actually a few ways you can do that. I’m showing you on this post only one which is easy and works just great.

Here are the 7 steps you can use to achieve that.

But before anything else we will need the very handy IIS Rewrite module in order for this to work. You can download it here.

There are two part that need to be properly configured:

  1. Sitecore’s site definition
  2. Create a rule using this module to take care of the redirection piece

Step 1 – Update the hostname attribute of the site definition to resolve both urls.

On your solution open the file SiteDefitition.config. By default it is located under App_Config/Include folder.

Ps: This assumes that you have removed the .example from the SiteDefinition.config. Also if you have a custom Sitedefinitions.config file this is where you should be updating this code.

Look for your site definition and update the hostName property of the site to resolve both to www.myhostname.com and myhostname.com. To do that you can either add both entries  to the hostName separated by a “|” character or just use the “*” and let Sitecore handle the rest(most recommended).

Option 1: hostName=”myhostname.com|www.myhostname.com”

Option 2: hostName=”*myhostname.com”. This syntax means Sitecore will resolve anything that comes before myhostname.com, including “www.” which makes www.myhostname.com a hostname that it understands and resolves.

At this point Sitecore will resolve both urls myhostname.com and www.myhostname.com. But IIS won’t.

 

Step 2 – Locate your site definition on IIS and open it’s bindings

On IIS you need to have a binding that resolves www.myhostname.com for the site. To do that open your website on IIS and click Bindings as shown bellow:

b1_step1

 

Step 3 – Create a binding that resolves www.myhostname.com

Click the Add button and set the hostname as www.myhostname.com and click OK. You will see a window like this:

b1_step2

 

Step 4 – Open the URL Rewrite section of your site.

On the IIS section of your website definition look for the URL  Rewrite button and double click it.

 

Step 5 – Click on Add Rules(s)

b1_step3

Step 6 – Create a Url Rewrite canonical url

On the Add Rule(s) window select the Canonical domain name section as shown bellow:

b1_step4

Step 7 – Select the primary host name

Make sure the hostname www.myhostname.com is selected. After that click OK.

b1_step5

 

That’s it! Now all the traffic on your Sitecore instance coming from myhostname.com will get redirected to www.myhostname.com

 

 

 

Sitecore Developer Tab: What is it and how to enable it?

Sitecore has a very handy tab on the Content Editor which by default comes hidden: the Developer Tab.

What is the Developer Tab?

It’s a tab which has a few shortcuts for create templates,  go to item template’s definitions, serialize items, rebuild indexes, among others which are really useful when implementing a new website. See bellow the tab’s buttons:

post1

Ok great but how do I enable it?
It’s easy! Just right click on the dark gray bar where all tabs are and click Developer as shown bellow:
post2

When you do that you will notice that the Developer tab shows beside the “My Toolbar”.
post3

That’s it. Enjoy!
Click here to see the same post in portuguese