Tuesday, August 14, 2012

Sitecore Tip O' The Evening

Using this in SC 6.5, pretty sure it works with all the current versions...

If you have a lookup field type and need to filter at the child level of the current parent by template name, use this: query:./parent::*[@@templatename='Activity']. Works like a charm. Basically this will allow you to have a mix of TemplateA and TemplateB under the same parent (more for content tree and SEO purposes than anything else) and be able to only allow the content editors to select from like templates in your lookup field.

Friday, August 10, 2012

Sitecore 6.5 Upgrade follow-up

Overall the upgrade went really well. From a public-facing perspective it was pretty seamless and our content editors are definitely happy that they can now use the browser of their choice to do content editing. The only caveat I have found so far is that when I was trying to import windows-encoded csv files using chrome on the mac and my custom tool the characters were getting jacked up. Running the same code with the same file in IE on windows had no problem.

The biggest setback we suffered in the upgrade process was that our site search stopped working cold. This probably should have been obvious given the amount of articles from Alex & Sitecore about the Lucene Search impending obsolescence but I missed it for the most part. I pretty much thought that 'depricated' meant 'will still work' but for whatever reason our search just gave up the ghost. I had config errors with the indexes I had previously defined in our web.config so just commented it out in the initial pass and then forgot about the whole deal. I guess I should have reviewed the first note in my previous upgrade post but we got busy and it slipped off the list. Finally circling back around to this topic, I've been hunting the web for good info. The best info I found is a combination of these two links:


The first helped by getting me a config file that works:
 <configuration xmlns:x="http://www.sitecore.net/xmlconfig/">  
  <sitecore>  
   <databases>  
    <database id="web" singleInstance="true" type="Sitecore.Data.Database, Sitecore.Kernel">  
     <Engines.HistoryEngine.Storage>  
      <obj type="Sitecore.Data.$(database).$(database)HistoryStorage, Sitecore.Kernel">  
       <param connectionStringName="$(id)" />  
       <EntryLifeTime>30.00:00:00</EntryLifeTime>  
      </obj>  
     </Engines.HistoryEngine.Storage>  
     <Engines.HistoryEngine.SaveDotNetCallStack>false</Engines.HistoryEngine.SaveDotNetCallStack>  
    </database>  
   </databases>  
   <search>  
    <configuration>  
     <indexes>  
      <index id="web" type="Sitecore.Search.Index, Sitecore.Kernel">  
       <param desc="name">$(id)</param>  
       <param desc="folder">web</param>  
       <Analyzer ref="search/analyzer" />  
       <locations hint="list:AddCrawler">  
        <master type="Sitecore.SharedSource.Search.Crawlers.AdvancedDatabaseCrawler,Sitecore.SharedSource.Search">  
         <Database>web</Database>  
         <Root>/sitecore/content</Root>  
         <IndexAllFields>true</IndexAllFields>  
         <fieldCrawlers hint="raw:AddFieldCrawlers">  
          <fieldCrawler type="Sitecore.SharedSource.Search.FieldCrawlers.LookupFieldCrawler,Sitecore.SharedSource.Search" fieldType="Droplink" />  
          <fieldCrawler type="Sitecore.SharedSource.Search.FieldCrawlers.DateFieldCrawler,Sitecore.SharedSource.Search" fieldType="Datetime" />  
          <fieldCrawler type="Sitecore.SharedSource.Search.FieldCrawlers.DateFieldCrawler,Sitecore.SharedSource.Search" fieldType="Date" />  
          <fieldCrawler type="Sitecore.SharedSource.Search.FieldCrawlers.NumberFieldCrawler,Sitecore.SharedSource.Search" fieldType="Number" />  
         </fieldCrawlers>  
         <!-- If a field type is not defined, defaults of storageType="NO", indexType="UN_TOKENIZED" vectorType="NO" boost="1f" are applied-->  
         <fieldTypes hint="raw:AddFieldTypes">  
          <!-- Text fields need to be tokenized -->  
          <fieldType name="single-line text" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="multi-line text" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="word document" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="html" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="rich text" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="memo" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="text" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <!-- Multilist based fields need to be tokenized to support search of multiple values -->  
          <fieldType name="multilist" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="treelist" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="treelistex" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <fieldType name="checklist" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
          <!-- Legacy tree list field from ver. 5.3 -->  
          <fieldType name="tree list" storageType="NO" indexType="TOKENIZED" vectorType="NO" boost="1f" />  
         </fieldTypes>  
        </master>  
       </locations>  
      </index>  
     </indexes>  
    </configuration>  
   </search>  
  </sitecore>  
 </configuration>  

While the second got me the code necessary to fire off the rebuild. The blog link was passing a string of "web" to the Sitecore.Jobs.JobOptions constructor which was not quite correct. I just copied the Builder object into my project, rebuilt and boom! indexes are rebuilding.

For the actual searching, I pulled down the Advanced Database Crawler code from Trac (http://trac.sitecore.net/AdvancedDatabaseCrawler/browser/Branches/v2/) and integrated that way. Still testing and dialing in but so far this looks promising and was only a couple of hours work to get from old style to new style Sitecore search.


Monday, May 21, 2012

50 Home Crossfit Workouts

Ever since my trip to myrle and the wife's unexpected trip back east, I've been slacking on my exercise. Just saw this list of crossfit workouts which seem doable at home. Now I have no excuse! 50 Home Crossfit Workouts

Thursday, May 3, 2012

Sitecore CMS 6.5 migration notes (cont)

More fallout from our migration from Sitecore CMS 6.1 to Sitecore CMS 6.5. So far we've had some roadblocks but everything has been fairly minor and easily solvable.

Problem - Search indexes cannot be rebuilt:
Job started: RebuildSearchIndex|System.UnauthorizedAccessException: Access to the path 'E:\Development\S\SUZ\Sitecore 6.5.0 rev. 111230\data\indexes\__system\segments.gen' is denied.
  at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
  at System.IO.File.Delete(String path)
  at Lucene.Net.Store.FSDirectory.Create()
  at Sitecore.Search.Index.Reset()
  at Sitecore.Search.Index.Rebuild()
  at Sitecore.Shell.Applications.Search.RebuildSearchIndex.RebuildSearchIndexForm.Builder.Build()|Job ended: RebuildSearchIndex (units processed: )


Solution:
Give write permissions on the indexes directory to the application pool identity “IIS AppPool\sitecore_65” (or whatever your app pool is named).

Once this was done, we could then work on updating our indexes from the ‘old’ way to the ‘new’ way. Alex Shyba’s and Brian Peterson’s blog entries are a great resource for this. Since converting, the same search term is only returning 35 results on the dev site vs. 283 on teh current live site so there is definitely more work to do in order to get this wrapped up but it’s a start. My initial guess is that we were indexing more fields in our old, custom index than is happening with the default shared source module.




Problem - Only EN culture in web database:
Once everything above was fixed we started testing the other versions of the sites, namely our en-CA and fr-CA versions. Surprisingly, things didn’t show up properly at all. After some quick investigation, we discovered that there were absolutely no canadian versions of the items in the web db.

Solution:
Use the control panel to add the missing cultures so sitecore knows about them. Kind of weird that they all imported ok as content and then just wouldn’t publish. I would sort of expect to get a warning when importing that the target culture did not exist in sitecore.

Monday, April 9, 2012

Web.Config Fixed

Well, it turns out that a second set of eyes can do wonders for troubleshooting. I filed a ticket with Sitecore and Alexey easily pointed out that the web.config my new site was using was missing the proper XslExtensions mapping. While it was correct in the 6.1 instance, I had missed porting it over to the new 6.5 web.config and then was looking at the old file thinking i had put it in there. Oops, that’s pretty weak on my part. Now that the config has been properly updated things are loading properly and everything is looking good. Alexey also reminded me that this is not their preferred method for upgrading a Sitecore instance but at the end of the day, for my personal situation which is basically a small team and no budget to upgrade from point release to point release manually updating configs and databases, I think that if everything tests out it will be the way we move forward.

Another thing I figured out the hard way via debugging is that any custom handlers you have need to now be set up under in the web.config file when you move to 4.0 of the .Net framework on the server and also switch over to the Integrated pipeline. Not a big ordeal, just took a bit of logic and a second opinion to reason that one out.

Now everything is happy and we’re on to the QA phase.

Friday, April 6, 2012

Sitecore 6.1 -> Sitecore 6.5 CMS upgrade process


Having previously gone through the painful and incremental process of upgrading from Sitecore 5.3 to Sitecore 6.1 and then reading this article, I am attempting to upgrade from 6.1 to 6.5 via a clean install then content migration. Here are my notes as I went through this process.

  1. Read release notes
    1. http://sdn.sitecore.net/Resources/Sitecore%206/Sitecore%20CMS.aspx
  2. Install fresh Sitecore 6.5 CMS instance & dbs
    1. http://sdn.sitecore.net/SDN5/Products/Sitecore%20V5/Sitecore%20CMS%206/Update/6_5_0_rev_111230.aspx
    2. configure a second iis site to point to the new instance, config everything, etc.
  3. Export existing roles from 6.1 instance
    1. Sitecore
    2. Security Tools
    3. Role Manager
    4. Serialize All Roles
  4. Export existing users from 6.1 instance
    1. Sitecore
    2. Security Tools
    3. User Manager
    4. Serialize All Users
  5. I didn’t have any custom domains, but would guess you should do this for domains, if applicable.
  6. Copy the serialization folder from the 6.1 instance to the 6.5 instance
    1. /Sitecore/data/serialization
  7. Import users into 6.5 instance
    1. Sitecore
    2. Security Tools
    3. User Manager
    4. Revert All Users
  8. Import users into 6.5 instance
    1. Sitecore
    2. Security Tools
    3. Role Manager
    4. Revert All Roles
  9. note that this will NOT serialize user passwords and other caveats may apply
    1. http://sdn.sitecore.net/forum/ShowPost.aspx?PostID=28899
    2. solution is to write a job that will reset every password and email the user a notification
      1. http://sdn.sitecore.net/forum//ShowPost.aspx?PostID=39959
  10. profile info migration?
    1. Not sure if this is included with the user when serialized.
    2. I would guess not which would mean I’ll need to write some sort of utility to port it over from the old db to the new db.
    3. Maybe a DTS package?
  11. Update: Steps 3 - 10 above didn't get me anywhere and I think emailing every user to let them know their password changed on them could end up being a huge hassle. I instead used the Sql Server Import/Export Wizard to just migrate the asp.net membership tables from the 6.1 core db to the new 6.5 core db. I checked the content and structure of both sets fo tables and couldn't find any changes so it feel like this was safe. We tested in dev prior to going live as well.
  12. Create package for templates
  13. Create package for layouts/sublayouts/renderings
  14. Create package(s) for media
    1. allow plenty of room for the zip(s) (at least as much as the master db size)
    2. first attempt at single zip failed to import (was almost 5gb). got a “Could not read signature - no data!” error with no other explanation.
    3. tried to do a 4gb file and got “Specified argument was out of the range of valid values. Parameter name: count”
    4. later attempt with a 2gb file did work.
  15. Create package for content
  16. Copy above packages from 6.1 instance to 6.5 instance
    1. /Sitecore/data/packages
  17. Import all the packages in the following order:
    1. templates
    2. layouts
    3. media library
    4. content
  18. Do a full republish on everything
  19. Branch your SVN tree so you can keep working on the base code during migration. (Just in case this takes a while, I get pulled away for other stuff all the time.)

So now that your Sitecore content is up to snuff it’s code time. We had checked the Sitecore install into our SVN repository and had our code intermingled with it. Not sure if this is the best practice since I personally don’t like to check in anything that isn’t compiled from our code but this is where we’re at currently. The first attempt was to just map the layouts, xsl, et al., directories from the branched tree to the new Sitecore website in IIS but Sitecore wasn’t so happy with that.

Could not load type 'SiteCore_Custom.layouts.Main'.

Or so I thought. Turns out that I didn’t copy over my custom dlls to the Sitecore/Website/bin dir. Just reminds me of when my dad used to say a good craftsman doesn’t blame his tools. Lesson learned, pops. Fair enough, then i got this:

Method 'get_EnableCdn' in type 'System.Web.UI.ScriptManager' from assembly 'System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' does not have an implementation

Thisx was easily solved with the googles, which directed me to a StackOverflow article (http://stackoverflow.com/questions/5341324/method-get-enablecdn-in-type-system-web-ui-scriptmanager-from-assembly-syst). I should probably spend some time researching what that actually means (I think that section binds specific calls from older dlls to newer stuff but not totally sure), but I removed the code and winner winner chicken dinner!

With the older code essentially functional under the new Sitecore 6.5 install, it’s time to get down to nuts and bolts. Some stuff that was working prior to upgrade just started failing. There is also some weirdness when debugging in Visual Studio and it seems to enjoy spiking the CPU to 100% and not returning (but seems ok when not attached).

A weird issue I’m seeing is when i try to edit the presentation for one of our product nodes. Everything seems to edit properly but whenever i click OK to save i get the following exception:

Invalid column name 'TargetVersion'.

An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
System.Data.SqlClient.SqlException: Invalid column name 'TargetVersion'.

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

[SqlException (0x80131904): Invalid column name 'TargetVersion'.]
  System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) +404
  System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning() +412
  System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) +1363
  System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) +6387757
  System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async) +6389458
  System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +538
  System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe) +689
  System.Data.SqlClient.SqlCommand.ExecuteNonQuery() +327
  Sitecore.Data.DataProviders.Sql.DataProviderCommand.ExecuteNonQuery() +89

[DataException: Error executing SQL command:  INSERT INTO [Links](   [SourceDatabase], [SourceItemID], [SourceLanguage], [SourceVersion], [SourceFieldID], [TargetDatabase], [TargetItemID], [TargetLanguage], [TargetVersion], [TargetPath] ) VALUES(  @database, @itemID, @sourceLanguage, @sourceVersion, @fieldID, @targetDatabase, @targetID, @targetLanguage, @targetVersion, @targetPath )]

[Exception: Invalid column name 'TargetVersion'.]
  Sitecore.Data.DataProviders.Sql.DataProviderCommand.ExecuteNonQuery() +342
  Sitecore.Data.DataProviders.Sql.SqlDataApi.Execute(String sql, Object[] parameters) +61
  Sitecore.Links.SqlLinkDatabase.AddLink(Item item, ItemLink link) +908
  Sitecore.Links.SqlLinkDatabase.UpdateItemVersionLinks(Item item, ItemLink[] links) +438
  Sitecore.Events.EventSubscribers.RaiseEvent(String eventName, Object[] parameters, EventResult result) +397
  Sitecore.Events.Event.RaiseEvent(String eventName, Object[] parameters) +390
  Sitecore.Events.Event.RaiseItemSaved(Object sender, ItemSavedEventArgs args) +325
  System.EventHandler`1.Invoke(Object sender, TEventArgs e) +0
  Sitecore.Data.Engines.EngineCommand`2.Execute() +267
  Sitecore.Data.Engines.DataEngine.SaveItem(Item item) +234
  Sitecore.Data.Managers.ItemProvider.SaveItem(Item item) +291
  Sitecore.Data.Items.ItemEditing.AcceptChanges(Boolean updateStatistics, Boolean silent) +229
  Sitecore.Shell.Framework.Commands.SetLayoutDetails.Run(ClientPipelineArgs args) +670

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
  System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) +0
  System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) +640
  System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +38
  System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +35
  Sitecore.Nexus.Pipelines.NexusPipelineApi.Resume(PipelineArgs args, Pipeline pipeline) +379
  Sitecore.Web.UI.Sheer.ClientPage.ResumePipeline() +285
  Sitecore.Web.UI.Sheer.ClientPage.OnPreRender(EventArgs e) +498
  Sitecore.Shell.Applications.ContentManager.ContentEditorPage.OnPreRender(EventArgs e) +25
  System.Web.UI.Control.PreRenderRecursiveInternal() +113
  System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +4201

I don’t see anything non-sitecore in the stack so maybe not. Oddly enough, re-opening the presentation shows it saves the changes and I can publish successfully. My initial thought is that we have some custom event handler in the pipeline that needs to get updated for 6.5 but will look at that later.

The reason I’m playing with the presentation is because one of the xlst’s on the page is not rendering. In the Sitecore logs I see this:

ERROR A rendering error occurred: Xsl file could not be processed (details: System.Xml.Xsl.XslTransformException: Cannot find a script or an extension object associated with namespace 'http://www.sitecore.net/xslext'.

While that looks like it is a Sitecore issue, we actually named our custom XSL extensions using that namespace and in retrospect that was probably not a great idea but still fairly easy to track down. After re-tracing how the extensions get loaded (using the xslExtensions section in web.config) and printing out the executing dll location to verify it did indeed have the extensions built in, I’m kind of stumped. Kind of fatigued right now so I’m just going to post this as is and do a follow up once I have a fix for teh xsl extensions issue. I don’t think re-writing all the xlst’s as sublayouts is a viable option since that would take a huge effort.

Monday, March 5, 2012

Crossfit time!

For Christmas I got the wife a last-second (literally) groupon gift certificate and a few weeks later we saw a sweet deal for our local crossfit gym. It was basically pay for 2 classes and get 10 free or something like that. Since we've been meaning to get back into shape and she has a bum knee I got the goods. Have gone two times now (Thurs then Sat) and it is pretty tough. Either that or I am in really bad shape. Or a combination of those, probably. I haven't puked or anything but really had to dial back the proscribed exercise reps in order to complete in a timely fashion. Felt super sore on Friday and some but not as much on Sunday. Today I'm feeling pretty good and even rode the 'ol DR-Z into work. Going to try and keep it up because I already feel much better and can tell it is a super-solid workout. Plus, everyone there seems pretty chill. It isn't like going to some meathead gym and getting shamed for lifting so little. Maybe in a few more weeks I'll be keeping up with the rest of the people there but for now I'm just going to push myself to do as much as I can and forget about them.

Also, of greater interested to the technical community as opposed to the athletic community, I just switched over to IntenseDebate as the commenting platform for this blog. Going to give it a test run and see what it does. Please comment so I can see how this shit works.

Wednesday, February 8, 2012

Kid Cabniks


Last weekend the wife headed out of town for a group birthday celebration with some friends in San Diego. Since the kids, mostly Sass, had been begging for a 'cabnik' to hang over their beds I figured that would give us a nice weekend project. We ended up only needing two trips to the Depot for parts and supplies. I found this blog that laid out the basic idea of the thing then I let the kids pick out their own shelf configurations. Sass went with the standard config and Matty decided he only wanted 2 cubbies but i managed to talk him into 4 so that he can store more cars in it. Not going to bore anyone with the details of the build, but I cut everything up on the table saw and then the kids helped assemble and sand. Neither wanted to paint but it fits in pretty well with their unfinished wood beds. The only shortcut I took was in mounting the doors. Probably should have chiseled out a notch so they fit flush, but by that time the kids patience and mine were pretty much used up. They look fine and the kids don't really care so overall I'm pleased we knocked it out in one weekend. The kids love them.

Sass' Cabnik

Matty's cabnik

Once they were on the wall the kids decorated with stickers and put some of their prized possessions in them.

Done and Done.

Back in the saddle

Wow, apparently it has been a while since I posted. Trying to get back into this as part of a holistic self-improvement program. Got a lot of stuff I want to figure out and learn about and this seems like a fair place to put it. If it is of interest to you, excellent. If not, there are plenty of other shitty blogs and sites on the infinite interwebs for you to check out.

Here is a sampler of (hopefully) upcoming topics:

  • Responsive Design
  • Home Improvement Projects
  • Restoration of the Ol' Suzuki GS450E
  • Hop-ups of the New Suzuki DR-Z400S
Enjoy!

- w