• Migrate GeeksWithBlogs to Orchard CMS

    Tags: Orchard, GeeksWithBlogs, Migration, Metro, BlogML

    As you can see I have migrated my blog to the Orchard CMS platform.

    I’ve used Orchard for a few projects and having gone through the pain enjoyment of learning Orchard, it was really the only platform I was going to choose for my blog!

    The theme I created myself, based on the Metro blog engine theme and the Mango Orchard theme.  I used Metro Studio to create the icons you see in the top left!

    This, as expected was not going to be a simple export and import job, GeeksWithBlogs don’t enable the export to BlogML feature that SubText has, as they told me it was too buggy!

    Orchard has a format all of it’s own so some serious Googling was the only plan.

    Seems I was not alone in my quest to migrate from GeeksWithBlogs, I don’t have any issue with them or the platform really, yes the themes are quite ugly and its quite dated, I just wanted more control.  MrHinsh has a codeplex project for this, but as I wasn’t going to WordPress I kept looking.

    I found this solution on codeplex, to export from MetaWebBlog to BlogML and this to import from BlogML into Orchard.

    The export ran and worked fine, except it didn’t include the tags (categories) into the post and it didn’t add any of the comments.  So I played with the code, found out that GeeksWithBlogs doesn’t support the MetaBlog method (GetComments).  With MetaBlog all comments have their own RSS url, so I wrote a little method to take the blog post ID go away and get the XML for the comments, parse that and build it into the BlogML output file.

    IEnumerable<BlogMLComment> GetCommentsForPost(Post post) {
        string commentsUrl = _blogUrl + "/Comments/commentRss/" + post.postid.ToString() + ".aspx";
    
        XNamespace dc = "http://purl.org/dc/elements/1.1/";
        XDocument doc = XDocument.Load(commentsUrl);
    
        var comments = from d in doc.Descendants("item")
                       select new BlogMLComment {
                           DateCreated = DateTime.Parse(d.Element("pubDate").Value),
                           UserName = d.Element(dc + "creator").Value,
                           Title = d.Element("title").Value,
                           Content = new BlogMLContent { Text = d.Element("description").Value }
                       };
    
        return comments ?? null;
    }
    

     

    That worked great (I’m not really sure if the comments RSS URL format is specific for GeeksWithBlogs or is a MetaWebBlog thing). 

    The full code I am happy to share, I did get in touch with the author of the original project requesting to be added as a developer so I could check in my enhancements.  I’ve heard nothing yet, the last checkin from them was in 2008 so perhaps it’s a gone and forgotten project!  I might create my own project or submit a patch to the work item.

    So I have my content all ready to go!

    With the import I immediately came across a problem which i raise here, the Orchard module is not working in v 1.4.  This was swiftly sorted by the author and I’m very grateful!  Well there are a few of us in the discussion that are!!

    There were still a few little things to work around with the import, it didn’t import into the selected blog for example, it created a new one…the way round this was to put in a slug!

    So that’s it, the journey is complete!  From GeeksWithBlogs to Orchard in only 4000 steps!

    Not quite…images, ahhhhh, images!!!  The source url on all image elements are still pointing to GWB.  I haven’t sorted this and not sure I ever will!   Now it’ll be a job to spin through the Orchard content to update them all, something I would like to do, but finding the time and inclination will be challenge all in itself!

    With regard to the Metro theme, I haven’t published that to the Theme Gallery, it’s really in no state to do so!  I am overriding many views to make little changes here and there.  The way the navigation works is a little hacky as well, because I wanted to keep the standard navigation functionality in the admin section (don’t know why now!) I have overridden the view to stick in my own CSS class based on the title of the navigation item.  Then in CSS i set the image.  If I don’t want to use an image I suffix the title with /t!  hmmmm. 

    image

    The tag cloud, if you are wondering is taken from here.  It does actually seem a little flakey…it sometimes doesn’t load – I may go back to the standard way!  It’s also a bugger to use on an iPad!

    I do want to create a mobile specific theme (it doesn’t work great on my phone!) – not sure how I go about switching themes on the fly in Orchard based on the browser, but I’m sure it’s possible.  One of the beauties of Orchard is it flexibility and extensibility!

    more...

  • PowerShell script to find where in a site collection a specific feature is enabled.

    Tags: PowerShell, SharePoint

    It's not an uncommon task to want to find out in what webs you have a specific feature enabled.  This little script does just that, taking 2 params (site collection url, feature name) and simple output the SharePoint web url.  Nothing fancy, but certainly a useful script to have in your toolbox!

    A good example of this is where you are using content publishing going from an Enterprise internal farm to a Foundation external farm, certain feature won’t be in the Foundation farm and will cause the Content Publisher to fail, it will be so kind as to tell you what feature is causing the problem, but not where it is!  Someone somewhere has enabled a feature they shouldn’t have!!!

    I’m sure some “one line wizards” could optimise this script – but that’s not my bag – I’m interested in getting the job done for our clients and providing a useful script we can use again and again.

    You could of course add in Disable-SPFeature command if you wanted to do that at the same time…

    #Powershell script to identify where in a site collection a specific feature is enabled
    #Version: 1.0
    #Author: Steve Clements | Perspicuity Ltd
    #Params:
    #$siteCollectionUrl - url of the site collection
    #$featureName - name of the feature you want to find
     
     
    param ([string] $siteCollectionUrl, [string] $featureName)
     
    if(!$siteCollectionUrl-or!$featureName) {
        Write-Host-ForegroundColorRed"You must provide a site collection url and feature name to find!"
        break
    }
     
    Write-Host-BackgroundColorDarkGreen"Feature '"$featureName"' exists in these sites"
     
    Get-SPSite$siteCollectionUrl | ForEach-Object {
        Get-SPWeb-Site$_ | ForEach-Object {
          $f=Get-SPFeature-Web$_ | Where {$_.DisplayName -eq$featureName}
      if($f) {
              Write-Host$_.Url
          }
        }
    }
     
    Write-Host-BackgroundColorDarkGreen"Done"
    

    more...

  • Gotcha: 403 and 401 errors in SharePoint using Anonymous access

    Tags: SharePoint

    Recently I came up against a problem where I was getting 401 Unauthorized and 403 Forbidden errors in SharePoint on an extended anonymous only site.

    It worked perfectly when using windows authentication, for any level of user.

    In the end I tracked the problem down to accessing SPContext.Current

    I found this article which states you should use SPSecurity.RunWithElevatedPrivileges when using the current context and it worked!!

    A little surprising, but not unexpected the SharePoint logs showed nothing about this problem.

    Check out the link above which gives a perfect little code snippet of how to resolve the problem.

    more...

  • Enable Publishing Approval workflow on all SP Web(s) in an Site collection with Powershell

    Tags: PowerShell, SharePoint

    What you need / What you've got?

    1. Pretty obvious, when a user edits/changes a page, you need someone to check its all good before it goes live! Aka Publishing Approval
    2. You can use the "Publishing site with workflow" template or enable it manually or set it up in your custom site definition.
    3. BUT...you've got an entire site collection full of web sites and you need to do it "en-mass" … step up Powershell.
      1. A common scenario could be you were working in Stage or Pre-go live and having Approval workflow just isn't practical or required. Now its live...you need and want it.

    What you need to do to SharePoint...

    1. Enable Moderation on the library (that is Content Approval in UI)
    2. Enable Minor Versions (that’s is 1.1, 1.2 etc)
    3. Create the Workflow History list and enable the Workflow History feature (hiding this list is optional, but I think its best, showing the list offers nothing to the user, you can access the same history looking at the workflow status screen, which is far more usefully presented)
    4. Create the Workflow Tasks list
    5. Associate the "Publishing Approval" workflow to the library
    6. Set the properties on the WorkFlow association
      1. This is your choice, but I have set AllowManual to true, AutoStartCreate and Change to false
    7. Add the association
    8. You would be forgiven in thinking that’s it, job done...but you will find that the even though the workflow is there, it doesn't the "major check-in" property enabled so the experience for the user is different i.e they have to choose the non-default option to fire the workflow.
    9. You need to set the property DefaultContentApprovalWorkflowId to the id of the Workflow association object, what tricked me up...even though in the UI the property is in the workflow with the other workflow properties, but this property is on the library.
    10. Still not done...now, the workflow is firing great, but every status is "Rejected". Why? Because you need to Update the workflow association data.
    11. For this I cheated and setup 1 workflow on the pages library, with the correct actions and Approvers group, then went into SharePoint Manager and grabbed the xml from there.
    12. Also I set the Enabled property to true and call WorkflowAssociations.Update(myAssociation) to set both new properties…
    13. Yes, that’s it...you are actually done now!!!

    The Magic

    This script is 99% influenced by this post http://bit.ly/AcOa2s, I’ve butchered it and added the extra stuff required… FYI: http://get-spscripts.com is a great resource for SharePoint PowerShell scripts...the scripts published are always of high quality, well commented and explained - bookmark it if you don't like thrashing.

    The crazy colours and rather OTT output is my fault!

    I’ve also put the params at the top – not overly user friendly, but this is perfect for my requirements…you can easily change that!

    $url="<site collection URL>"
    $list="Pages"
    $workFlowName="Publishing Approval"
    $wfassname="Page Approval"
     
     
     
    functionAddWorkflowToLibraries ($SiteCollection, $ListName, $WfName, $WfAssociationName)
    {
       #Get site object and create blank guid to store workflow template ID
        $site=Get-SPSite$SiteCollection
       [Guid]$wfTemplateId=New-ObjectGuid
        
        # Get every sub web
        Get-SPWeb-Site$site | ForEach-Object {
      Write-Host-ForegroundColorWhite-BackgroundColorDarkMagenta"WEB: "$_.Title$_.Url
            
            #Do the following if a list exists with the name specified by the user - e.g., Pages
            if ($_.Lists[$ListName]) {
                Write-Host"PROCESS:"$listName"list"
                
                #
                # Get list and set version properties
                #
                $list=$_.Lists[$ListName]
                $list.EnableModeration =$true
                $list.EnableMinorVersions =$true
                $list.Update()
                
               #Go through each workflow installed in the site to find the correct ID            
                foreach ($wfTemplatein$_.WorkflowTemplates) {
                    if ($wfTemplate.Name -eq$WfName) {
                       $wfTemplateId=$wfTemplate.Id
                   }
               }
                
                #
                #SETUP WORKFLOW HISTORY LIST
                #
                $wfTemplate=$_.WorkflowTemplates[$wfTemplateId]
                if($wfTemplate-eq$null) {
                    Write-Host"ERROR: Workflow '"$WfName"' with ID '"$wfTemplateId"' NOT found"-BackgroundColorRed-ForegroundColorWhite
                }
                else {
                    #Check if the site already has a workflow history list - if not, create it
                if(!$_.Lists["Workflow History"])
                    {
                        Write-Host"CREATE: Workflow History list"-ForegroundColorBlue
                        
                    $_.Lists.Add("Workflow History", "A system library used to store workflow history information that is created in this site.  It is created by the Publishing feature.",
                        "WorkflowHistory", "00BFEA71-4EA5-48D4-A4AD-305CF7030140", 140, "100")
                        
                        if (!$_.Features["00BFEA71-4EA5-48D4-A4AD-305CF7030140"]) {
                            Enable-SPFeature-IdentityWorkflowHistoryList-Url$_.Url
                    }
                        $wfHistory=$_.Lists["Workflow History"]
                        $wfHistory.Hidden =$true
                    $wfHistory.Update()
                    }
                    else
                {
                        Write-Host"FOUND: Workflow History list"-ForegroundColorgreen
                    $wfHistory=$_.Lists["Workflow History"]
                    }
                    
                    #
                    #Check if the site already has a workflow tasks list - if not, create it
                    #
                if(!$_.Lists["Workflow Tasks"]) {
                        $_.Lists.Add("Workflow Tasks", "This system library was created by the Publishing feature to store workflow tasks that are created in this site.", "WorkflowTasks", "BF611337-1591-49f4-BF42-CE7BE53852D8", 107, "100")
                        Write-Host"CREATE: Workflow Tasks list"-ForegroundColorBlue
                }
                    else {
                        Write-Host"FOUND: Workflow Tasks list"-ForegroundColorgreen
                    }
                $wfTasks=$_.Lists["Workflow Tasks"]
                    
                    #
                    #new up workflow association (extra associated data can be added if you have it)
                    #
                $wfAssociation= [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation($wfTemplate, $WfAssociationName, $wfTasks, $wfhistory)
                    $wfAssociation.AllowManual =$true
                    #$wfAssociation.AllowAsyncManualStart = $true
                    $wfAssociation.AutoStartChange =$false
                    $wfAssociation.AutoStartCreate =$false
                    
                    #
                    #Check to see if the association has already been added to the list
                    #
                [guid]$wfId=New-ObjectGuid
                [bool]$wfFound=$false
                    
                    foreach ($wfin$list.WorkflowAssociations) {
                        if ($wf.Name -eq$wfAssociation.Name) {
                            $wfId=$wf.Id
                            write-host"FOUND: Workflow"$wf.Name"already exists on"$list.Title"list in site"$_.Title-ForegroundColorgreen
                        $wfFound=$true
                    }
                    }
                    #If association is already there, ignore the add (and optionally delete it)
                if ($wfFound-eq$true) {
                        #Command to remove existing workflow from list before adding new one, if required
                    #$list.WorkflowAssociations.Remove($wfId)
                    write-host"REMOVE: Workflow"$wfAssociation.Name"from"$list.Title"in site"$_.Title-ForegroundColorMagenta
                }
                    #else, add it to the workflow association to the list
                else
                {
                        #Create the association
                    $list.WorkflowAssociations.Add($wfAssociation) | Out-Null
                        
                        #Set the association data (for approvers and approval steps
                        $wfAssociation.AssociationData =Get-AssociationData
                        #enable is not available until its associated.
                        $wfAssociation.Enabled =$true
                        
                        $list.WorkflowAssociations.Update($wfAssociation)
                        
                        $list.DefaultContentApprovalWorkflowId =$wfAssociation.Id
                        $list.Update()
                        write-host"CREATE: Workflow"$wfAssociation.Name"to"$list.Title"in site"$_.Title-BackgroundColorCyan-ForegroundColorRed
                }
                }
            }
            else {
                Write-Host-BackgroundColorDarkRed-ForegroundColorWhite"No"$listName" list found"
            }
        }
        
       #Dispose of Site object
        $site.Dispose()
    }
    functionGet-AssociationData() {
        return"<dfs:myFields xmlns:xsd=`"http://www.w3.org/2001/XMLSchema`" xmlns:dms=`"http://schemas.microsoft.com/office/2009/documentManagement/types`" xmlns:dfs=`"http://schemas.microsoft.com/office/infopath/2003/dataFormSolution`" xmlns:q=`"http://schemas.microsoft.com/office/infopath/2009/WSSList/queryFields`" xmlns:d=`"http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields`" xmlns:ma=`"http://schemas.microsoft.com/office/2009/metadata/properties/metaAttributes`" xmlns:pc=`"http://schemas.microsoft.com/office/infopath/2007/PartnerControls`" xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`"><dfs:queryFields /><dfs:dataFields><d:SharePointListItem_RW><d:Approvers><d:Assignment><d:Assignee><pc:Person><pc:DisplayName>Approvers</pc:DisplayName><pc:AccountId>Approvers</pc:AccountId><pc:AccountType>SharePointGroup</pc:AccountType></pc:Person></d:Assignee><d:Stage xsi:nil=`"true`" /><d:AssignmentType>Serial</d:AssignmentType></d:Assignment></d:Approvers><d:ExpandGroups>false</d:ExpandGroups><d:NotificationMessage /><d:DueDateforAllTasks xsi:nil=`"true`" /><d:DurationforSerialTasks xsi:nil=`"true`" /><d:DurationUnits>Day</d:DurationUnits><d:CC /><d:CancelonRejection>true</d:CancelonRejection><d:CancelonChange>true</d:CancelonChange><d:EnableContentApproval>true</d:EnableContentApproval></d:SharePointListItem_RW></dfs:dataFields></dfs:myFields>"
    }
     
    AddWorkflowToLibraries$url$list$workFlowName$wfassname
    

    more...

  • SharePoint 2010 set "Start this workflow to approve publishing a major version of an item" with Powershell

    Tags: SharePoint

    Took me a little while to figure this one out, you’ve assigned your workflow to a library and you can set all other other flags like “AllowStartChange” and “AllowStartCreate”, but cant see how to set “Start this workflow to approve publishing a major version of an item.”

    That’s because its a property on the library itself called “DefaultContentApprovalWorkflowId”

    So you might want to do something like this…where you have the WorkFlow Association object ($wfAssociation) and the list/library ($list).

    $wfAssociation = [Microsoft.SharePoint.Workflow.SPWorkflowAssociation]::CreateListAssociation(……
    $list.WorkflowAssociations.Add($wfAssociation) | Out-Null
    $list.DefaultContentApprovalWorkflowId = $wfAssociation.Id
    $list.Update()

    Resources:

    This is probably the best Associate workflow to list/library with Powershell example out there http://bit.ly/AcOa2s

    more...

  • Minify JS and CSS in Visual Studio Build Events

    Tags: .NET 4.0, .NET, ASP.net, VS 2010, SharePoint, Personal

    It’s pretty widely accepted (and common sense) that if you’re putting a site out into the wild – such a simple task as minifying CSS and JavaScript files can save a bunch of load time, especially on mobile devices.

    I’ve been playing about with this today and was quite happily using jsmin as described here until I noticed that in IE 7 and IE 8 the minified CSS didn’t work.

    The problem seems to be when using a background style, it takes out the space between the image url close bracket and the next style.

    background:url(image.png) no-repeat;
    background:url(image.png)no-repeat;

    Not a problem in IE 9 or chrome (haven’t tested others).

    So I set about researching the problem and found nothing…struggled a bit getting the search term right and clearly never mastered it as I found zip.

    Not to be put off, I was looking at the YUI compression tool from Yahoo, said to be better…but needs the java runtime and seems to complex to simply use, certainly for my simple taste!

    Then I found a YUI .net implementation on codeplex.  Excellent, its was even in NuGet.

    However…I wanted it as a build task to run in VS, I don’t really want to think about this stuff more than once.

    So I wrote a little command line wrapper for it.  Excellent.  Even got ILMerge involved to merge the YUI dll’s into my exe.  I know there are ways to use Reflection to load dll’s as embedded resources…but I didn’t fancy doing that!

    I did look at using CommandLine (also on codeplex) as I have done before, but ILMerge does not like that.  Don’t take my word for it, but it seems to be something to do with ILMerge is .net 2 and CommandLine uses some .net4 features.  It wasn’t happy…anyway, I just wrote a few lines of code to parse the command line args myself.

    Perfect!!!

    It’s not complex or very flexible, but is very suitable to my needs.

    You can pass in an array of files to minify and a target path.  That’s it.  It’ll merge the files if you pass more than one…you have no choice!

    > SCmin.exe –s C:\big.css C:\big1.css –t C:\small.css

    // This will take big.css and big1.css and put it into small.css

    Simples!

    Download the exe or full source.

      

    more...

  • Visual Web Part as a Sandboxed solution

    Tags: SharePoint

    You want the RAD wonderfulness of a visual web part, but it needs to be deployed as a Sandboxed solution.
    Problem?
    No, SharePoint powertools for visual studio to the rescue!
     
     
    There are a couple limitations, read the above page, nothing major. e.g.
    1. Javascript debugging is not supported
    2. Debugging asp.net code is not supported.
    3. Use of <% Assembly Src= is not supported
     
    I understand it does it by adding the markup as an embedded resource, but I haven't actually tried it yet!  To come!
     

    more...