DCSIMG
justguy's

Migrating an enterprise web site to a new CMS

Currently as a web development manager for a medium company, I'm in charge of migrating the current enterprise-size website to a new CMS.

CMS stands for Content Management System.

 

There are so many things to consider when planning such a project. Some of which are:

  • Usability
  • Accessability
  • SEO
  • Statistics
  • Language support
  • Design and branding
  • WWW standards
  • Content migration
  • Implementation and development of old and new components
  • UX (User eXperience)
  • Cross-browser support
  • Security

 Am I forgetting anything?

 

In my following post I'll try to find the time to discuss the issues I'll encounter.

Let's hope for the best ;)

Adi.

Posted by justguy | with no comments
תגים:

WPF: ComboBox with CheckBoxes as items (it will even update on the fly!)

Hi,

 

Recently I’ve come across something weird… I needed a ComboBox that will allow the user to select multiple items.
The the solution coming to mind is using CheckBoxes. I have found several examples, but neither one displayed the selected items with pretty commas (like this: image ).

 

I’ve decided the best solution willl be taking an example from MSDN and modifying it to suite my needs.

Steps: (actually took ALOT longer and was ALOT harder – learning curves and such)

  1. Created a UserControl.
  2. Added the ComboBox from the MSDN sample.
  3. Created 3 dependency properties:
    • Text – retrieves the text of the selected items
    • ItemsSource – the items to display (currently bound to Title and IsSelected)
    • DefaultText – the text to display if no items were checked
  4. Bound the ItemsSource properties of the UserControl and the ComboBox.
  5. Added a Click event to the CheckBox that refreshes the text field in the ContentPresenter.

Usage

<Window





xmlns:controls="clr-namespace:Controls;assembly=Controls"
<controls:ComboWithCheckboxes
    x:Name="cbLanguages"
    Height="22"
    DefaultText="Choose Subtitles..."
    ItemsSource="{Binding}"
    />

 

Result

image

image

image

Code

 

ComboWithCheckboxes.xaml

<UserControl x:Class="Controls.ComboWithCheckboxes"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="22" Width="120"
    x:Name="UserControl"
    >
    
    <UserControl.Resources>
        <LinearGradientBrush x:Key="NormalBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#CCC" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="NormalBorderBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#CCC" Offset="0.0"/>
                    <GradientStop Color="#444" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="GlyphBrush" Color="#444" />

        <LinearGradientBrush x:Key="DarkBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#FFF" Offset="0.0"/>
                    <GradientStop Color="#AAA" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <LinearGradientBrush x:Key="PressedBrush" StartPoint="0,0" EndPoint="0,1">
            <GradientBrush.GradientStops>
                <GradientStopCollection>
                    <GradientStop Color="#BBB" Offset="0.0"/>
                    <GradientStop Color="#EEE" Offset="0.1"/>
                    <GradientStop Color="#EEE" Offset="0.9"/>
                    <GradientStop Color="#FFF" Offset="1.0"/>
                </GradientStopCollection>
            </GradientBrush.GradientStops>
        </LinearGradientBrush>

        <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />

        <SolidColorBrush x:Key="DisabledBackgroundBrush" Color="#EEE" />

        <SolidColorBrush x:Key="WindowBackgroundBrush" Color="#FFF" />

        <SolidColorBrush x:Key="SelectedBackgroundBrush" Color="#DDD" />

        <ControlTemplate x:Key="ComboBoxToggleButton" TargetType="ToggleButton">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition Width="20" />
                </Grid.ColumnDefinitions>
                <Border
                  x:Name="Border" 
                  Grid.ColumnSpan="2"
                  CornerRadius="2"
                  Background="{StaticResource NormalBrush}"
                  BorderBrush="{StaticResource NormalBorderBrush}"
                  BorderThickness="1" />
                <Border 
                  Grid.Column="0"
                  CornerRadius="2,0,0,2" 
                  Margin="1" 
                  Background="{StaticResource WindowBackgroundBrush}" 
                  BorderBrush="{StaticResource NormalBorderBrush}"
                  BorderThickness="0,0,1,0" />
                <Path 
                  x:Name="Arrow"
                  Grid.Column="1"     
                  Fill="{StaticResource GlyphBrush}"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Center"
                  Data="M 0 0 L 4 4 L 8 0 Z"/>
            </Grid>
            <ControlTemplate.Triggers>
                <Trigger Property="ToggleButton.IsMouseOver" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" />
                </Trigger>
                <Trigger Property="ToggleButton.IsChecked" Value="true">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" />
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
                    <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
                    <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                    <Setter TargetName="Arrow" Property="Fill" Value="{StaticResource DisabledForegroundBrush}" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
        
        <ControlTemplate x:Key="ComboBoxTextBox" TargetType="TextBox">
            <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
        </ControlTemplate>

        <Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
            <Setter Property="SnapsToDevicePixels" Value="true"/>
            <Setter Property="OverridesDefaultStyle" Value="true"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ComboBoxItem">
                        <Border 
                          Name="Border"
                          Padding="2"
                          SnapsToDevicePixels="true">
                            <ContentPresenter />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsHighlighted" Value="true">
                                <Setter TargetName="Border" Property="Background" Value="{StaticResource SelectedBackgroundBrush}"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>

    <ComboBox
        x:Name="CheckableCombo"
        SnapsToDevicePixels="True"
        OverridesDefaultStyle="True"
        ScrollViewer.HorizontalScrollBarVisibility="Auto"
        ScrollViewer.VerticalScrollBarVisibility="Auto"
        ScrollViewer.CanContentScroll="True"
        IsSynchronizedWithCurrentItem="True"
        MinWidth="120"
        MinHeight="20"
        ItemsSource="{Binding ElementName=UserControl, Path=ItemsSource}"
        DataContext="{Binding ElementName=UserControl, Path=DataContext}"
        >
        <ComboBox.ItemTemplate>
            <HierarchicalDataTemplate>
                <CheckBox Content="{Binding Title}"
                          IsChecked="{Binding Path=IsSelected, Mode=TwoWay}"
                          Tag="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}"
                          Click="CheckBox_Click"
                          />
            </HierarchicalDataTemplate>
        </ComboBox.ItemTemplate>

        <ComboBox.Template>
            <ControlTemplate TargetType="ComboBox">
                <Grid>
                    <ToggleButton 
                        Name="ToggleButton" 
                        Template="{StaticResource ComboBoxToggleButton}" 
                        Grid.Column="2" 
                        Focusable="false"
                        IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                        ClickMode="Press">
                    </ToggleButton>
                    <ContentPresenter
                        x:Name="Presenter"
                        IsHitTestVisible="False" 
                        Margin="3,3,23,3"
                        VerticalAlignment="Center"
                        HorizontalAlignment="Left">
                        <ContentPresenter.Content>
                            <TextBlock TextTrimming="CharacterEllipsis"
                                       Text="{Binding Path=Text,Mode=TwoWay,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" />
                        </ContentPresenter.Content>
                    </ContentPresenter>
                    <!-- Content="{TemplateBinding SelectionBoxItem}" -->
                    <TextBox x:Name="EditableTextBox"
                        Style="{x:Null}" 
                        Template="{StaticResource ComboBoxTextBox}" 
                        HorizontalAlignment="Left" 
                        VerticalAlignment="Center" 
                        Margin="3,3,23,3"
                        Focusable="True" 
                        Background="Transparent"
                        Visibility="Hidden"
                        IsReadOnly="{TemplateBinding IsReadOnly}"/>
                    <Popup 
                        Name="Popup"
                        Placement="Bottom"
                        IsOpen="{TemplateBinding IsDropDownOpen}"
                        AllowsTransparency="True" 
                        Focusable="False"
                        PopupAnimation="Slide">
                        <Grid 
                                  Name="DropDown"
                                  SnapsToDevicePixels="True"                
                                  MinWidth="{TemplateBinding ActualWidth}"
                                  MaxHeight="{TemplateBinding MaxDropDownHeight}">
                            <Border 
                                    x:Name="DropDownBorder"
                                    Background="{StaticResource WindowBackgroundBrush}"
                                    BorderThickness="1"
                                    BorderBrush="{StaticResource SolidBorderBrush}"/>
                            <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True" DataContext="{Binding}">
                                <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                            </ScrollViewer>
                        </Grid>
                    </Popup>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="HasItems" Value="false">
                        <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                    <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
                        <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
                        <Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
                    </Trigger>
                    <Trigger Property="IsEditable"
                   Value="true">
                        <Setter Property="IsTabStop" Value="false"/>
                        <Setter TargetName="EditableTextBox" Property="Visibility"    Value="Visible"/>
                        <Setter TargetName="Presenter" Property="Visibility" Value="Hidden"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </ComboBox.Template>
    </ComboBox>
</UserControl>

 

ComboWithCheckboxes.xaml.cs

usingSystem.Windows;

namespace Controls
{
    /// <summary>
    ///
Interaction logic for ComboWithCheckboxes.xaml
  
/// </summary>
  
public partial classComboWithCheckboxes
  
{
        #regionDependency Properties
        /// <summary>
        ///
Gets or sets a collection used to generate the content of the ComboBox
      
/// </summary>
      
public objectItemsSource
        {
            get{ return(object)GetValue(ItemsSourceProperty); }
            set
          
{
                SetValue(ItemsSourceProperty, value);

                SetText();
            }
        }

        public static readonlyDependencyPropertyItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(object), typeof(ComboWithCheckboxes), newUIPropertyMetadata(null));

        /// <summary>
        ///
Gets or sets the text displayed in the ComboBox
      
/// </summary>
      
public stringText
        {
            get{ return(string)GetValue(TextProperty); }
            set{ SetValue(TextProperty, value); }
        }

        public static readonlyDependencyPropertyTextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(ComboWithCheckboxes), newUIPropertyMetadata(string.Empty));


        /// <summary>
        ///
Gets or sets the text displayed in the ComboBox if there are no selected items
      
/// </summary>
      
public stringDefaultText
        {
            get{ return(string)GetValue(DefaultTextProperty); }
            set{ SetValue(DefaultTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DefaultText.  This enables animation, styling, binding, etc...
      
public static readonlyDependencyPropertyDefaultTextProperty =
            DependencyProperty.Register("DefaultText", typeof(string), typeof(ComboWithCheckboxes), newUIPropertyMetadata(string.Empty));
        #endregion


        public
ComboWithCheckboxes()
        {
            InitializeComponent();
        }

        /// <summary>
        ///
Whenever a CheckBox is checked, change the text displayed
      
/// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
      
private voidCheckBox_Click(objectsender, RoutedEventArgse)
        {
            SetText();
        }

        /// <summary>
        ///
Set the text property of this control (bound to the ContentPresenter of the ComboBox)
      
/// </summary>
      
private voidSetText()
        {
            this.Text = (this.ItemsSource != null) ?
                this.ItemsSource.ToString() : this.DefaultText;

            // set DefaultText if nothing else selected
          
if(string.IsNullOrEmpty(this.Text))
            {
                this.Text = this.DefaultText;
            }
        }
    }
}

That’s it!

Adi.

Posted by justguy | 74 comment(s)
תגים:,

Base Content Type Hierarchy

 

Hi,

As you may know, Content Types are hierarchical and uniquely identified with an ID.
Read about it here: Base Content Type Hierarchy, Content Type IDs.

 

 

 

 

If you actually read the articles above, you know that inherited content types always start with the ID of the part and add a suffix.

I wrote this small class to help identify the type of a Content Type. Use it with String.StartsWith() :)

public static class ContentTypePrefix
{
    public static readonly string Item            = "0x01";

    // document
    public static readonly string Document        = "0x0101";
    public static readonly string XMLDocument     = "0x010101";
    public static readonly string Picture         = "0x010102";
    public static readonly string UntypesDocument = "0x010104";
    public static readonly string MasterPage      = "0x010105";
    public static readonly string WikiDocument    = "0x010108";
    public static readonly string BasicPage       = "0x010109";
    public static readonly string WebPartPage     = "0x01010901";
    public static readonly string LinkToDocument  = "0x01010A";
    public static readonly string DublinCoreName  = "0x01010B";

    public static readonly string Event           = "0x0102";
    public static readonly string Issue           = "0x0103";
    public static readonly string Announcement    = "0x0104";
    public static readonly string Link            = "0x0105";
    public static readonly string Contact         = "0x0106";
    public static readonly string Message         = "0x0107";
    
    // task
    public static readonly string Task            = "0x0108";
    public static readonly string WorkflowTask    = "0x010801";
    public static readonly string AdminTask       = "0x010802";

    public static readonly string WorkflowHistory = "0x0109";
    public static readonly string BlogPost        = "0x0110";
    public static readonly string BlogComment     = "0x0111";
    public static readonly string FarEastContact  = "0x0116";
    
    // folder
    public static readonly string Folder          = "0x0120";
    public static readonly string RootOfList      = "0x012001";
    public static readonly string Discussion      = "0x012002";
}
Saved you some work, huh?

 

 

 

 

Updated (same day)

Here’s an example of using some Linq to get the best matching base content type:

private static string GetBaseType(string id)
{
    string baseTypeName = string.Empty;
    List<FieldInfo> fields = new List<FieldInfo>(typeof(ContentTypePrefix).GetFields());

    // get the values of the static fields
    var fieldValues = from field in fields
                      select new { Name = field.Name, Value = (string)field.GetValue(null) };

    var baseTypes = from fieldValue in fieldValues
                    where id.StartsWith(fieldValue.Value)
                    orderby fieldValue.Value.Length descending
                    select fieldValue.Name;

    baseTypeName = baseTypes.First<string>();

    return baseTypeName;
}
Posted by justguy | 1 comment(s)

SharePoint list templates and base types

Hi!

I come across another strange request from the same client…
I had something to do with displaying all items from all document libraries in the site.

Trying to think caught me off guard, and I decided to go ahead and work with the CQWP (Content Query Web Part).
I found it too complicated and uncustomizable (the client has fantasies about web development).

Scratching my head a bit got me thinking about a nice web part I put together once:
Get a list, use the GetDataTable() mehod, use WriteXml() and parse the whole thing with XSL…

 

 

 

 

 

 

 

 

OK!

What about *all* the lists in the web site?
First, the user must select the list template to base the query on (localized):

SPListTemplateCollection listTemplates = SPContext.Current.Site.RootWeb.ListTemplates;
SortedList list = new SortedList();
List<int> templatesToIgnore = new List<int>() { 110, 0x75, 0x76, 0x2776 };
ListItem item = null;

listTypeDropDown = new DropDownList();

#region populate the list template drop down
foreach (SPListTemplate template in listTemplates)
{
    int type = (int)template.Type;
    
    if (templatesToIgnore.Contains(type))
    {
        continue;
    }

    item = new ListItem(template.Name, Convert.ToString(type, CultureInfo.InvariantCulture));
    list[item.Text] = item;
    item.Attributes.Add("BaseType", template.BaseType.ToString("D"));
    
    ListItem item2 = new ListItem(SPUtility.GetLocalizedString("$Resources:core,posts_schema_blg_title;",
        null, (uint)CultureInfo.CurrentUICulture.LCID), SPListTemplateType.Posts.ToString("d"));

    list[item2.Text] = item2;
    item2.Attributes.Add("BaseType", "0");
    ListItem[] array = new ListItem[list.Count];
    list.Values.CopyTo(array, 0);
    listTypeDropDown.Items.Clear();
    listTypeDropDown.Items.AddRange(array);

    //if (string.IsNullOrEmpty(baseType))
    //{
    //    baseType = this.contentByQueryWebPart.ServerTemplate;
    //}
}

Second, get all the lists based on that type:

/// <summary>
/// Retrieves all lists based of the specified template type in the provided web
/// </summary>
/// <param name="parentWeb"></param>
/// <param name="listTemplateType"></param>
/// <returns></returns>
/// <exception cref="ArgumentException" />
public static List<SPList> GetListsByType(SPWeb parentWeb, int listTemplateType)
{
    List<SPList> lists = new List<SPList>();

    if (parentWeb == null)
    {
        throw new ArgumentException("parentWeb cannot be null");
    }

    // look for lists based of the requrested template type
    foreach (SPList list in parentWeb.Lists)
    {
        if ((int)list.BaseTemplate == listTemplateType)
        {
            lists.Add(list);
        }
    }

    return lists;
}

Cheers!

Posted by justguy | with no comments
תגים:,

What happens when your client requires list item aggregation from sub sites…

Hi,

As you all know, client have an annoying tendency of getting annoying.
Yes, I am talking about the money-makers!

I came across this request:

  1. Aggregate list items (news, tasks) from sub sites to the root site.
    The condition for aggregation will be a site column named “Copy to Homepage”.
  2. Aggregated items will require content approval.
  3. The client should be able to implement the same behavior himself (on programmers on board).

1st thing’s 1st… damn!

Now let’s get to work…

 

Step 1 - Aggregate the items – SPItemEventReceiver

If you’re not familiar with the SPItemEventReceiver, Google it.

The event will check if the “Copy to Homepage” column is checked. If so, the following method will copy it:

public static void CopyMoveListItem(
            SPListItem sourceItem,
            SPFolder destinationFolder,
            bool deleteSourceItem)
        {

            // create a new item
            SPListItem targetItem = GetListById(destinationFolder.ParentWeb, destinationFolder.ParentListId).Items.Add(
                destinationFolder.ServerRelativeUrl,
                sourceItem.FileSystemObjectType
                );

            // loop over the soureitem, restore it
            for (int i = sourceItem.Versions.Count - 1; i >= 0; i--)
            {
                SPListItemVersion version = sourceItem.Versions[i];

                //set the values into the archive
                foreach (SPField sourceField in sourceItem.Fields)
                {
// add the field to the new item make sure the field exists in the target list if ((!sourceField.ReadOnlyField) && (sourceField.Type != SPFieldType.Attachments) &&
targetItem.Fields.ContainsField(sourceField.Title))
                    {
                        targetItem[sourceField.Title] = version[sourceField.Title];
                    }
                    else if (sourceField.Title == "Created" && sourceField.Title == "Created By" &&
                        sourceField.Title == "Modified" && sourceField.Title == "Modified By")
                    {
                        targetItem[sourceField.Title] = version[sourceField.Title];
                    }
                }
                
                // update the new item
                targetItem.Update();
            }

            // copy the attachments (they are not versioned)
            foreach (string attachmentName in sourceItem.Attachments)
            {
                SPFile file = sourceItem.ParentList.ParentWeb.GetFile(
                    sourceItem.Attachments.UrlPrefix + attachmentName);
                
                targetItem.Attachments.Add(attachmentName, file.OpenBinary());
            }
            
            targetItem.Update();

            // delete the source item if needed
            if (deleteSourceItem)
            {
                sourceItem.Delete();
            }
        }

Notes:

  • This code is based on something I found once. If you recognize your code, let me know. I’ll give you the credit :)
  • GetListById returns the parent list of the item. It’s too simple for blog posts.
  • When adding the code to the SPItemEventReceiver, I’d cosider using DisableEventFiring Method and EnableEventFiring Method.
    If you modify an item, you might cause the event to fire again for the update.


That got me thinking about letting the ItemAdded method where the target list is located…
I used the web.config

 

Step 2 – Content Approval

This is a simple step that you can Google up…
Here’s an example.

 

Step 3 – User Friendly GUI for Event Receivers

The biggest issue in this entire escapade is allowing non-programming users to attach event receivers to new lists.

Then I came across Gaetan Bouveret’s SharePoint Events Manager.

 

 

Enough with the writing!

Adi.

Posted by justguy | 1 comment(s)
תגים:,

Debugging IIS: Identifying the Worker Process Process ID

Hi,

When working on a shared environment with several web applications, attaching to IIS processes may cause a problem.
Developers have a nasty habit of attaching to all IIS process, thus making it impossible for other users to work on the server.

Finding the required process ID is quite simple, enabling you to attach only to the process you require.

 

Here goes:

Run iisapp.vbs from command prompt (Start –> Run –> cmd).
You’ll get something like this:

image

Note you get the application name and the processs ID (PID).

Now you attach to the process you need and leave the rest alone:

image

 

Important note:
When doing a IISRESET or recycling the application pool, the process ID changes.

Posted by justguy | with no comments

SharePoint Data View - ddwrt Namespace: IfNew() and Friends

Ever wonder how that “New!” (new.gif) image appears next to list items?

SharePoint Data Views use the ddwrt XSL namespace generated automatically like so:

<xsl:if test="ddwrt:IfNew(string(@Created))">
    <img src="/_layouts/1033/images/new.gif" alt="New" />
</xsl:if> 
BTW, items are considered "new" if it was posted within the past 2 days…

Read more: MSDN: SharePoint Data View Web Part Extension Functions in the ddwrt Namespace

Posted by justguy | 1 comment(s)

Empty application.master

Hi,

I you ever need it, this may prove very time consuming…

<%@Master language="C#"%>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<HTML id="HTML1" dir="<%$Resources:wss,multipages_direction_dir_value%>" runat="server">
<HEAD id="HEAD1" runat="server">
    <SharePoint:CssLink runat="server"/>
    <SharePoint:ScriptLink language="javascript" name="core.js" runat="server" />
    <SharePoint:CustomJSUrl runat="server" />
    <SharePoint:SoapDiscoveryLink runat="server" />
</HEAD>
<BODY>
  <form id="Form1" runat="server" onsubmit="return _spFormOnSubmitWrapper();">
        <asp:ContentPlaceHolder id="PlaceHolderAdditionalPageHead" runat="server"/>
        <asp:ContentPlaceHolder id=PlaceHolderPageTitle runat="server"/>
        <asp:ContentPlaceHolder id="PlaceHolderMain" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderGlobalNavigation" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderGlobalNavigationSiteMap" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderSiteName" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderSearchArea" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTopNavBar" runat="server" />
        <asp:ContentPlaceHolder ID="WSSDesignConsole" runat="server" />
        <asp:ContentPlaceHolder ID="SPNavigation" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderPageImage" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTitleLeftBorder" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTitleAreaClass" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTitleBreadcrumb" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderPageTitleInTitleArea" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderMiniConsole" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTitleRightMargin" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderTitleAreaSeparator" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderLeftNavBarDataSource" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderCalendarNavigator" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderLeftNavBarTop" runat="server"/>
        <asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderLeftActions" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderLeftNavBarBorder" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderBodyLeftBorder" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderBodyAreaClass" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderPageDescriptionRowAttr" runat="server"/>
        <asp:ContentPlaceHolder id="PlaceHolderPageDescription" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderPageDescriptionRowAttr2" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderBodyRightMargin" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderUtilityContent" runat="server" />
        <asp:ContentPlaceHolder id="PlaceHolderFormDigest" runat="server" /> 
  </form>
</BODY>
</HTML>
Posted by justguy | with no comments

SharePoint Shared Services Personalization Permissions Check

Hi,

I needed to know if a user has permissions to manage user profiles (create, delete, etc.).
Some reflecting showed me that all relevant methods were internal. “Damn” was the word I used.

I have come across this post by Gary Lapointe, and have decided I need something a little simpler:

/// <summary>
///
Checks if the provided user has the permissions set in the rightMast
/// </summary>
/// <example>
///
bool canManageProfiles = DoesUserHaveSSPRights(ServerContext.Current, "DOMAIN\\myusername", SharedServiceRights.ManageUserProfiles | SharedServiceRights.UsePersonalFeatures);
/// </example>
public static bool DoesUserHaveSSPRights(ServerContext context, stringusername, SharedServiceRights rightMask)
{
    return(GetUserSSPRights(context, username) & rightMask) == rightMask;
}

/// <summary>
///
Gets the user's Personalization Services rights
/// </summary>
public static SharedServiceRights GetUserSSPRights(ServerContext context, string username)
{
    Type sharedServiceAccessControlListType = Type.GetType("Microsoft.Office.Server.Infrastructure.SharedServiceAccessControlList, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");

    MethodInfo sharedServiceAccessControlListMethod =
        sharedServiceAccessControlListType.GetMethod("GetInstance",
            BindingFlags.NonPublic |
            BindingFlags.Public |
            BindingFlags.Instance |
            BindingFlags.InvokeMethod |
            BindingFlags.FlattenHierarchy |
            BindingFlags.Static,
            null,
            new Type[] { typeof(ServerContext) },
            null
          
);

    object acl = sharedServiceAccessControlListMethod.Invoke(
        null,
        new object[] { context }
        );

     PropertyInfo itemProp = acl.GetType().GetProperty("Item",
         BindingFlags.NonPublic |
         BindingFlags.Instance |
         BindingFlags.InvokeMethod |
         BindingFlags.GetProperty |
         BindingFlags.Public
         );
   
    SharedServiceAccessControlEntry aclEntry =
        (SharedServiceAccessControlEntry)itemProp.GetValue(
            acl,
            new object[] { username }
            );

    // check if the user has any permissions
  
if(aclEntry != null)
    {
        returnaclEntry.Rights;
    }
    else
  
{
        // the user is not even in the list
      
return SharedServiceRights.None;
    }
}

 

 

Comments:

Add a reference to Microsoft.Office.Server.

Using:

using Microsoft.Office.Server;
using Microsoft.Office.Server.Infrastructure;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Reflection;

Posted by justguy | 2 comment(s)

Checking if a Publishing page is in edit mode

… and avoiding ASP.NET validator errors such as:

This page contains content or formatting that is not valid. You can find more information in the affected sections.

or

Input string was not in a correct format.

 

Hi,

After being asked by a customer to create a web part diaplying a form with validation on input fields, I have come acoross a problem.
It turns out that ASP.NET validators can prevent you from checking in or editing Publishing pages.

Searching for solution I have come across a few:

Check if the web part is in edit or design mode and only add the validator if not:

if (this.WebPartManager.DisplayMode != WebPartManager.EditDisplayMode &&
    this.WebPartManager.DisplayMode != WebPartManager.DesignDisplayMode)
{
    // in display mode
}
This solution applies only if you have a single web part on the page, because you can only check if the current web part is in display/edit/design mode.
Another solution is using the EditModePanel. It allows you to add contols to the page in display or edit mode.
For example:

// add the EditBox to the page in edit mode
EditModePanelpanel = newEditModePanel();
TextBoxtextBox = newTextBox();

panel.Controls.Add(textBox);
panel.PageDisplayMode = PageDisplayMode.Edit;
this.Controls.Add(panel);


This solution didn’t work. Some replection shows me that the panel uses this function (which is obfuscated): public staticSPControlModeGetContextualFormModeFromPostedForm()
This function comes from the internal class ConsoleUtilities.
The solution that worked for me was quite simple:

// check if the form is in display mode
bool inDisplayMode = SPContext.Current.FormContext.FormMode == SPControlMode.Display;

 

Well, good luck ;)

Posted by justguy | 5 comment(s)

FBA in SharePoint – Everything you’ve ever wanted to do - continued

Hi,

This is my 2nd post on this subject.

This post will get down and dirty with code samples. The first post contains some background info and links to tutorials and guides. I recommand you have a look unless you’re a no-fear-willing-to-spand-days-making-this-work like me :)

To user user profiles with FBA users, you’ll have to extend

 

Chapter 3 – UserProfileManager and FBA (provider issues)

First thing’s first: THERE ARE 2, YES, 2, UserProfileManager CLASSES. The first (Microsoft.SharePoint.Portal.UserProfiles) is obsolete. The second (Microsoft.Office.Server.UserProfiles)  is not.

I’ll use the one in the Microsoft.Office namespace. Note there are differences between them as well as between the corresponding UserProfile classes.

User Profiles in MOSS

User profiles are a nice feature in MOSS. They allow you to store meta data about the users in your portal importing it from other sources (SharePoint 2007: Importing User Profile Information by Sahil Malik).

User information commonly stored in user profiles includes: first and last name, birthday, manager name, company, department, address etc. User profiles are stored in a SSP making them available throughout the farm.

You can add and change the user profile properties using the management interface:

Central Administration –> Shared Services Provider –> User Profiles and Properties

For more info, try Google or Live! search.

 

Retrieving User Profile Info

In order to retrieve user profile info, you’ll have to have the SSP up and running. If it’s not, you’ll get errors trying to user a UserProfileManager object.

  1. Create a new UserProfileManager object:

    UserProfileManager userProfileManager = new UserProfileManager();

    This will create profile manager that will work in the context of the current site and the assigned SSP.
    You can retrieve user profile information from other SSPs in the farm using this function:
    /// <summary>
    /// Creates a <see cref="UserProfileManager"/> object for a specific site collection
    /// 
    /// Note the functions contains no error handling what so ever
    /// </summary>
    /// <param name="siteURL">The URL of the site</param>
    /// <returns>The <see cref="UserProfileManager"/> object for the site collection</returns>
    public UserProfileManager GetUserProfileManagerForAnotherSite(string siteURL)
    {
        SPSite anotherSite = new SPSite(siteURL);       // reference the other site collection
        ServerContext serverContext = ServerContext.GetContext(anotherSite);        // get the server context
        UserProfileManager userProfileManager = new UserProfileManager(serverContext);      // create the profile manager object
    
        return userProfileManager;
    }
    This function will be useful for bridging the gap between a site using Windows Authentication and a site using FBA.
    You could access user profile info from either site using the correct context of UserProfileManager.
  2. To create a user profile, use: 
          
    UserProfile
    profile = userProfileManager.CreateUserProfile();    // create a profile for the current user
    or
    UserProfile profile = userProfileManager.CreateUserProfile("domain\\username");    // create a profile for the provided user
    or
    UserProfile profile = userProfileManager.CreateUserProfile("Provider name:username");    // create a profile for the provided user (FBA)
    Where:
    Provider name is the name of the default membership provider defined in the web.config.
    Example:
                      <membership defaultProvider="FBADemoMember">
    Username is the name of the user from the membership database.










    Note that unlike domain users that use ‘\’ as a separator, FBA user require ‘:’.
  3. Get the user profile for the current user:

    UserProfile
    profile = userProfileManager.GetUserProfile(false);

    The user’s profile can be null if there was no profile created. GetUserProfile can create the user profile if needed when you set bCreateIfNoExist to true:
          UserProfile profile = userProfileManager.GetUserProfile(true); 

    GetUserProfile will allow you to retrieve user profiles for user by users referenced by username:
    UserProfile profile = userProfileManager.GetUserProfile("domain\\username");    // retrieve the user profile for a domain user
    UserProfile profile = userProfileManager.GetUserProfile("Provider name:username");    // retrieve the user profile for a FBA user
  4. As you can imagine, deleting a user profile will be quite simple. I’ll skip the code sample.
  5. Setting values in user profiles or retriving them is as easy a using any other collection. The only challenge is using the correct property names (not display names, but internal names). To help you out, you can use PropertyConstants for the OOO stuff.

    Examples:

    string firstName = (string)profile[PropertyConstants.FirstName].Value;
    string lastName = (string)profile[PropertyConstants.LastName].Value;
    DateTime hireDate = (DateTime)profile[PropertyConstants.HireDate].Value;
    string myCustomProperty = (string)profile["MyCustomPropertyName"].Value;    // retrieve the value for a custom property
    Choice fields have multiple values, and you can take a look at this post: Setting MOSS User Profile properties by Dan Matthews.

Note you can access user profile information using the user profile service. Here’s a nice post (InfoPath team blog) demostrating the use with InfoPath.

What’s next?

The sky’s the limit… just kidding…

Here’s a sample method I came up with demonstrating a sample use for user profiles:

/// <summary>
///
Looks for an anniversary, meaning any date in the user's profile matching today's month and day
/// </summary>
/// <param name="profile">
The user profile to check</param>
/// <returns>
true if found, false if not</returns>
public bool DoesTheUserHaveAnythingToday(UserProfile profile)
{
    bool result = false;    // assume the user has nothing today
    // get the profile manager
   
UserProfileManager userProfilem = profile.ProfileManager;

    // go over all the properties looking for dates
   
foreach (Property prop in userProfilem.Properties)
    {
        // is it possbible for a prop to be null?
       
if (prop != null)
        {
            try
           
{
                if (prop.Type == "datetime" || prop.Type == "datenoyear" || prop.Type == "date")
                {
                    DateTime date = (DateTime)profile[prop.Name].Value;

                    // check if the day and month match today
                  
if (date.Month == DateTime.Today.Month && date.Day == DateTime.Today.Day)
                    {
                        // found a date so stop looking
                      
result == true;
                        break;
                    }
                }
                else
              
{
                    // this property is not a date value
              
}
            }
            catch
          
{
                // nothing to do... move on to the next property
          
}
        }
    }

    return result;
}

 

Disclaimer: I didn’t run this code so… :)

Posted by justguy | 2 comment(s)

FBA in SharePoint – Everything you’ve ever wanted to do

Hi,

Recently I’ve had an interesting challenge (yes, challenge): building a MOSS portal based on Form Authentication.
I call it a challenge, because it was quite difficult to accomplish. One of the reasons was lack of documantation.

The solution… :)

Chapter 1 – Why FBA??

If sounds funny when you say if ignoring the dots that should be place between the letters. I know!

Anyway, FBA is another means of authentication. The most common is Active Directory (Windows authentication). SharePoint requires a domain for the installation and management. The authentication is accomplished by querying a domain controlller (DC) in the domain.

Another method is Passport. Read about it on your own free time.

Form Based Authentication means the authentication is done query a database (or text files or wherever). The user can create the form the user uses to enter the credentials.
Have a look at this simple example in MSDN.

This method enables you to create your own authentication method, create your own login page, or set up a simple login control on the home page of your site. You could also add cookie support for automatic authentication (i.e., “Remember me”).
You could use ASP.NET’s authentication. That’s what SharePoint does, so we don’t have a lot of choices here.

 

Chapter 2 – What do we need?

I’ll address the issue regarding MOSS installations. WSS is not as problematic.

  1. Install MOSS :P
  2. Go to Google and run a search for FBA SharePoint.
    You’ll find several posts, like:

    Configuring a Office SharePoint Server 2007 Publishing Site with Dual Authentication Providers and Anonymous Access (Andrew Connell)

    Office SharePoint Server 2007 - Forms Based Authentication (FBA) Walk-through - Part 1 (Dan Attis)

    Forms Base Authentication Tools and Utils for SharePoint 2007 (CodePlex) – this is a full FBA management solution for SharePoint.
  3. If you’re going to use user profiles with the FBA site, you’ll have to extend the Shared Services Provider (SSP) to work with the FBA provider.
    Check out Dan Attis’ post: Office SharePoint Server 2007 - Forms Based Authentication (FBA) w/MySites Walk-through - Part 2

 

This is it for now.

 

Futuristic posts (when I find some free time)::

Chapter 3 – UserProfileManager and FBA (provider issues)

Chapter 4 – Anonymous access to FBA sites

Posted by justguy | 1 comment(s)

Combining a WCF Service with a Web Service (or SharePoint Designer doesn't work with WCF)

Recently I've started working with WCF services for all the right reasons.
I noticed SharePoint Designer cannot consume WCF services. I'm guessing it has something to do with SOAP versions.

After a short investigation I came up with a hybrid: a service you can use as a WCF Service and a Web Service (.asmx and .svc).
I have suggested this solution to Microsoft Gold Support and got a "OK. We'll look into this."

How do we do it?

  1. Create/Find a WCF Service.
  2. Create a new Web Service.
  3. Copy the Web Wervice's we.config into the WCF's web.config (not app.config).
    Make sure you don't lose the WCF tag (under configuration):

    <system.serviceModel>
    ...
    </system.serviceModel>

  4. Copy the .asmx from the Web Service to the WCF project and modify it accordingly:

    <%@ WebService Language="C#" CodeBehind="Service1.cs" Class="WCFService1.Service1" %>

  5. If you don't already have one, create a .svc file for the WCF Service:
    
    

    <%@ServiceHost Service="WCFExample.Service1" %>

  6. The service class has to inherit WebService and implement the service interface:




    public class Service1 : System.Web.Services.WebService, IService1

  7. Add the [WebMethod] attribute (reference System.Web.Services) to the functions you want to expose using the Web Service.
  8. Host the entire thing in the IIS.
  9. Cross your fingers...
    To access the web serivce, use the .asmx, and for the WCF, use the .svc.

Good luck!
 Edit: Found this article in MSDN: How to: Migrate ASP.NET Web Service Code to the Windows Communication Foundation
 P.S. Sorry for the small fonts at the end of the post. I cannot install Live Write, and HTML <font> tags bug me.
Posted by justguy | with no comments

My first post (2nd try)

Hi,

 I've been told a blog is the thing. So here it is.

 My name is Adi Levinshtein, and I've been working in the IT industry for the past 4.5 years.
During the past 2.5 years I've been working with ASP.NET and specializing in Microsoft SharePoint.
Currently I'm a team leader and a consultant working for Bynet Software.

I'll try to keep things interesting, so nothing about my cat... :P

 Cheers!

Posted by justguy | 4 comment(s)