All posts by Jonas

Extending the CustomerContact object in Episerver Commerce

Here’s a quick method for adding your own properties to the CustomerContact object:

public virtual bool AddCustomerContactMetaField(string name, string friendlyName, string type)
        {
            var customerContactMetaClass = DataContext.Current.GetMetaClass(ContactEntity.ClassName);

            if(customerContactMetaClass.Fields[name] != null)
            {
                return false;
            }
            
            return customerContactMetaClass.CreateMetaField(name, friendlyName, type, new Mediachase.BusinessFoundation.Data.Meta.Management.AttributeCollection()) == null;
        }

You can then assign and read values to and from your property like this:

CustomerContact contact = CustomerContact.CreateInstance();

// Assign value
contact.Properties.Add("MyProperty", "value");

// Reading a value
string value = contact.Properties.GetValue<string>("MyProperty");

EPiServer 7/Search: Prevent content from being indexed

Let’s say you have a page or a page type that you do not want to get indexed by the EPiServer Search service. The solution is to implement the ISearchable interface:

bool AllowReIndexChildren { get; }
bool IsSearchable { get; }

It requires you to implement two boolean properties, AllowReIndexChildren and IsSearchable. By letting IsSearchable return false you’ll prevent the page from being indexed. You could either implement it as a page property, having the editor choose which pages should be excluded:

[Display(Name = "Exclude page from site search",
    Description = "Check this box to prevent page from being indexed by the site's search engine.",
    GroupName = SystemTabNames.Settings,
    Order = 100)]
public virtual bool IsSearchable
{
    get
    {
        bool isSearchable = this.GetPropertyValue(p => p.IsSearchable);
        return !isSearchable;
    }
    set
    {
        this.SetPropertyValue(p => p.IsSearchable, value);
    }
}

Or you could prevent the the entire page type from being indexed:

[ContentType(DisplayName = "Sneaky page that won't get indexed.", GUID = "00000000-0000-0000-0000-000000000000")]
public class SneakyPage : PageData, ISearchable
{
    public bool AllowReIndexChildren { get { return true; } }
    public bool IsSearchable { get { return false; } }
}

But what about content that is already in the index? Ticking the box to prevent indexing on a page that has previouly been index will only prevent updates to this item, not prevent it from showing in search results.

In order to make sure indexed items are removed add the code below to Global.asax

protected void Application_Start()
{
    DataFactory.Instance.PublishedContent += OnPublishedContent;
}

private void OnPublishedContent(object sender, ContentEventArgs contentEventArgs)
{
    ISearchable seachable = contentEventArgs.Content as ISearchable;
    if (seachable == null) {
        return;
    }

    if (seachable.IsSearchable) {
        return;
    }

    CultureInfo language = null;
    ILocalizable localizable = contentEventArgs.Content as ILocalizable;

    if (localizable != null) {
        language = localizable.Language;
    }

    string searchId = string.Concat(contentEventArgs.Content.ContentGuid, "|", language);
    IndexRequestItem indexItem = new IndexRequestItem(searchId, IndexAction.Remove);
    SearchHandler.Instance.UpdateIndex(indexItem);
}

Html Agility Pack: Get all heading tags

A project I’m currently working on involves parsing a lot of HTML using the Html agility pack. One problem I came across was finding all heading level tags between 1 and 6 and here’s my solution using Xpath:

string xpathQuery = "//*[starts-with(name(),'h') and string-length(name()) = 2 and number(substring(name(), 2)) <= 6]";
var tags = htmlDocument.DocumentNode.SelectNodes(xpathQuery);

If someone has a better or alternative solution please let me know in the comments.

Breadcrumbs based on WordPress menus

Here’s a quick and dirty function for generating breadcrumbs based on pages in a WordPress menu. Please note that I have not tested this with menu items based on post types other than pages.

function jki_get_nav_menu_id_for_post($post_id)
{
	$items = wp_get_nav_menu_items('Huvudmeny');
	
	$menu_item_id = -1;
	
	// Find menu item
	foreach ((array)$items as $menu_item) {
		if($menu_item->object_id == $post_id) {
			$menu_item_id = $menu_item->ID;
			break;
		}
	}
	
	return $menu_item_id;
}

function jki_get_nav_menu_item($item_id)
{
	$args = array( 'post_type' => 'nav_menu_item', 
	    'post_status' => 'publish', 
	    'output' => ARRAY_A, 
	    'p' => $item_id);
	
	$query = new WP_Query($args);
	$items = $query->get_posts();
	$items = array_map( 'wp_setup_nav_menu_item', $items );
	
	if(count($items) > 0) {
		return $items[0];
	}	
	else {
		return null;
	}
}

function jki_wp_menu_breadcrumbs()
{	
	global $post;

	$menu_item_id = jki_get_nav_menu_id_for_post($post->ID);
	
	if($menu_item_id == -1) return;
	
	$current_menu_item = jki_get_nav_menu_item($menu_item_id);
	
	if($current_menu_item == null) return;
	
	$delimiter = '<span> // </span>';
	$crumbs = array();
	
	$crumbs[] = $delimiter . $current_menu_item->title;
	
	if($current_menu_item->menu_item_parent != 0) {
		$is_top = false;
		while($is_top == false) {
			$current_menu_item = jki_get_nav_menu_item($current_menu_item->menu_item_parent);
			$crumbs[] = $delimiter . '<a href="' . $current_menu_item->url . '">' . $current_menu_item->title . '</a>';
			
			if($current_menu_item->menu_item_parent == 0) {
				$is_top = true;
			}
		}
	}
	
	$crumbs = array_reverse($crumbs);
	
	echo "<div id=\"breadcrumbs\">\n";
	echo '<a href="/">Home</a>';
	foreach ($crumbs as $crumb) {
		echo $crumb;
	}
	echo "\n</div>\n";
}

Clean WordPress tag cloud

The default WordPress tag cloud includes nasty stuff like inline styling. I needed to be able to style the tags with a single class that included the tag weight in its name. It also needed the tags to be displayed in a list. The result turned the separate tag markup from

<a href="http://randomsite.com/tag/lolcats/" class="tag-link-5" title="3 tag" style="font-size: 32pt">Lolcats</a>

into

<li class="tag-10">
	<a href="http://randomsite.com/tag/lolcats/">Lolcats</a>
</li>

First we create a simple value object representing a tag.

class Custom_Tag
{
	var $name, $link, $class;
	
	public function Custom_Tag($name, $link, $class)
	{
		$this->name = $name;
		$this->link = $link;
		$this->class = $class;
	}
}

Then we go through all the tags and assign each a class depending on its weight. Those occurring the least will get the class tag-1 and those occurring most frequent will get the class tag-10. The function will return an array of Custom_Tag objects.

function custom_generate_tag_cloud($tags)
{
	if($tags) {
		$smallest_class = 1;
		$largets_class = 10;
		
		$counts = array();
		foreach ($tags as $key => $tag) {
			$counts[$key] = $tag->count;
		}
		
		$min_count = min($counts);
		$spread = max($counts) - $min_count;
		if ( $spread <= 0 )
			$spread = 1;
		
		$class_spread = $largets_class - $smallest_class;
	        if ( $class_spread < 0 )
	                $class_spread = 1;
	        $class_step = $class_spread / $spread;
		
		$cloud_tags = array();
		foreach ($tags as $key => $tag) {
			$count = $counts[$key];
			$class = 'tag-' . round($smallest_class + ( ( $count - $min_count ) * $class_step ));
			$cloud_tags[] = new Custom_Tag($tag->name, $tag->link, $class);
		}
	}
	
	return $cloud_tags;
}

Finally we collect the tag objects created in the function above and output some html. We also add a filter hook for “wp_generate_tag_cloud”.

function custom_tag_cloud($return, $tags, $args)
{
	$tag_array = custom_generate_tag_cloud($tags);
	$cloud_tags = array();
	
	$return = "<ul>\n";
	foreach ($tag_array as $tag) {
		$cloud_tags[] = '<li class="' . $tag->class . '"><a href="' . $tag->link . '">' . $tag->name . "</a></li>\n";
	}
	$return .= join('', $cloud_tags);
	$return .= "</ul>\n";
	
	return $return;
}

add_filter('wp_generate_tag_cloud', 'custom_tag_cloud', 10, 3);

Spotify-klient till iPhone

Spotify har äntligen färdigställt sin iPhone-klient och skickat den till app-store. Nu är det bara att hoppas att Apple inte sätter sig på tvären och nekar den inträde. Ska bil spännande att se om det krävs wifi-anslutning för att kunna använda den eller om den även fungerar med 3G. Om inte 3G stöds ser i alla fall möjligheten att kunna spara ner spellistor för offline-bruk ut att kunna bli lite av en tröst.

Läs mer här: http://www.spotify.com/blog/archives/2009/07/27/spotify-for-iphone/

Veckans spotify-lista v.4

En dag för sent är den här; vecka fyras heliga spotify-lista!

Live – White, Discussion
Live – Lakini’s Juice
Fever Ray – If I Had a Heart
Fever Ray – Now’s The Only Time I Know
Glasvegas – It’s My Own Cheating Heart That Makes Me Cry
Glasvegas – Daddy’s Gone
Detektivbyrån – E18
Detektivbyrån – Dansbanan
Markus Krunegård – Den som dör får se
Markus Krunegård – Idioter

Hittas som vanligt här.