Hi,

After changing the EditForm.aspx & DispForm.aspx of a a list I’m working on something went terribly wrong:

All the links directing to the view item form / edit form are pointing to the root site without the form itself.

Meaning – instead of having a link to:

 http://myportal/mysubsite/Lists/MyList/DispForm.aspx?ID=1

I now have links pointing:

http://myportal/?ID=1

Same goes for editing item – when clicking ‘Edit Item’ the page redirects to the main page.

 

Searching for the cause came up with several ideas,for example, which none of them worked for me so far.

(The main cause is SPD)

 

So…

 

If you ever came across such a case and have a solution to offer – Please let me know.

 

Thank you!

Update:

The display item form values in SPD are correct as far as SPD can tell.

Update #2 – Solution:

here

Posted by dorong | 1 comment(s)

ptLogo

Job description

The candidate will be responsible for the SharePoint at Playtech

Implementation and maintenance tasks in the corporate Portal

Providing services to the corporate reference and management

Managing work with suppliers

Requirements

At least 3 years experience in large scale SharePoint implementation.

Proven experience in managing and maintaining enterprise portal based MOSS2007

Practical knowledge on SharePoint Designer

Knowledge of basic Web technologies HTML, CSS, etc.

Email me your CV if it interests you.

Posted by dorong | with no comments

After setting up a new web application for a new portal I faced a crawling problem of the new site collection.

Description:

Right after starting the crawler it stops, giving 2-3 error messages, all with the same syntax:

Error in the Site Data Web Service. (Value does not fall within the expected range.)

 

The crawl log was the only place the error was logged, with no additional information regarding the cause of the problem.

 

Searching the web came up with very little solutions, while only one of them seemed to apply to our case:

 

1. stsadm -o export -url http://intranet/sites/it -filename D:\it.back.11.21.08.cmp -versions 2
2. Delete the site collection
3. stsadm -o import -url http://intranet/sites/it -filename D:\it.back.11.21.08.cmp -updateversions 2 –nofilecompression

(See here)

 

While option 1 and 3 are reasonable, deleting the site collection didn’t apply since the portal was already in production stage. (along with the fact faze 1 didn’t pass either due to some technical issues… so I had to look for a better way to solve the issue)

After opening a support ticket, Microsoft’s support team came up with a nicer solution:

First of all, the cause behind this problem:

Corrupted SIDs in the UserInfo table

Solution:

1. Run the following sql query using your moss content database:

Select tp_login, tp_systemid, tp_deleted from userinfo where len(ltrim(rtrim(tp_systemid))) <25 and tp_deleted = 0

2. Give some kind of permissions to the root site of the site collection to all the users returned by the query, except system users:

NT AUTHORITY\authenticated users
NT AUTHORITY\local service
NT AUTHORITY\system
SHAREPOINT\system

4. After the query returns no results (accept the system accounts) start a full crawl.

 

Notes:

1. As for stage 2, you better test 2-3 users just to make sure that after you give them permissions to the root site they are infect removed from the query search results.

2. By give permission I mean any type of permission, “read” is enough. While giving permissions to a user in the root level the SID is updated and fixed (somehow…).

3. I had a local problem with some of the users which appeared in the returned result – I couldn’t add them to the site collection because they were deleted from the Active Directory (former employees) so simply had to delete them through the “People and Groups” of the site collection.

Posted by dorong | 4 comment(s)
תגים:, , ,

A few days ago we started migrating a portal site from one web application to another on the same machine.

As part of the migration process I needed to copy lists from the old site to the new site. Some lists, contained Person list columns, with single or multiple selection.

While the migration went smoothly during the export/import process itself the result was weird – all the user names were altered on the new portal.

 

Reason:

 

1. Person list columns store the user name (as text) and the user id from the profiles list.

2. Each web application has it’s own profiles – with different user id’s given to each user.

 

Instead of starting to migrate the profiles from one portal to another I simply wrote a small utility which fix the Person columns of selected lists (imported ones) by taking the user id, getting the email of the user from the old portal, finding the new user id on the new portal and updating the list item:

 

public partial class Form1 : Form
{
    private List<MigrationField> _migrationfields;
    private SPSite _sourcesite;
    private SPList _targetlist;
    private SPSite _targetsite;

    public Form1()
    {
        InitializeComponent();
        Source.Text = Settings.Default["SourceSite"].ToString();
        Target.Text = Settings.Default["TargetSite"].ToString();
    }
    private void btnVerifyColumns_Click(object sender, EventArgs e)
    {
        _sourcesite = new SPSite(Source.Text);
        _targetsite = new SPSite(Target.Text);
        _targetlist = _targetsite.RootWeb.GetList(TargetListUrl.Text);
        _migrationfields = new List<MigrationField>();
        Columns.Items.Clear();
        Log.Items.Clear();
        foreach (SPField field in _targetlist.Fields)
        {
            if (field.TypeAsString.Equals("User") || field.TypeAsString.Equals("UserMulti"))
            {
                Columns.Items.Add("Field Name: " + field.Title + ", Multi:" + field.TypeAsString.Equals("UserMulti"));
                _migrationfields.Add(new MigrationField { FieldName = field.Title, IsMulti = field.TypeAsString.Equals("UserMulti") });
            }
        }
        btnMigrate.Enabled = true;
    }

    private string GetEmailFromSource(string userid)
    {
        if (!String.IsNullOrEmpty(userid))
            return _sourcesite.RootWeb.SiteUsers.GetByID(Convert.ToInt32(userid)).Email;
        return String.Empty;
    }

    private SPUser GetUserFromTarget(string emailaddress)
    {
        if (!string.IsNullOrEmpty(emailaddress))
            return _targetsite.RootWeb.SiteUsers.GetByEmail(emailaddress);
        return null;
    }

    private void btnMigrate_Click(object sender, EventArgs e)
    {
        foreach (SPItem item in _targetlist.Items)
        {
            try
            {
                foreach (MigrationField field in _migrationfields)
                {
                    if (item[field.FieldName] != null)
                    {
                        switch (field.IsMulti)
                        {
                            case true:
                                string[] userids = item[field.FieldName].ToString().Split(';');
                                var userscollection = new SPFieldUserValueCollection();

                                for (int i = 0; i < userids.Length; i += 2)
                                {
                                    SPUser user = GetUserFromTarget(GetEmailFromSource(userids[i].Replace("#", "")));

                                    if (user != null)
                                        userscollection.Add(new SPFieldUserValue(_targetsite.RootWeb, user.ID,
                                                                                 user.Name));
                                }
                                item[field.FieldName] = userscollection;
                                item.Update();
                                break;
                            case false:
                                string email =
                                    GetEmailFromSource(item[field.FieldName].ToString().Substring(0,
                                                                                                  item[
                                                                                                      field.
                                                                                                          FieldName]
                                                                                                      .
                                                                                                      ToString().
                                                                                                      IndexOf
                                                                                                      (';')));
                                SPUser newuser = GetUserFromTarget(email);
                                if (newuser != null)
                                {
                                    item[field.FieldName] = newuser;
                                    item.Update();
                                }

                                break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Items.Add("itemid " + item.ID + ": " + ex.Message);
            }
        }
        MessageBox.Show("Done migrating");
    }
}
public class MigrationField
{
    public string FieldName { get; set; }
    public bool IsMulti { get; set; }
}
Posted by dorong | 2 comment(s)
תגים:, , , ,

A common thought is that in private browsing of IE 8 or incognito in chrome are meant to protect users from leaving their tracks behind.

From Microsoft:

InPrivate Browsing

When checking e-mail at an Internet café or shopping for a gift on a family PC, you don't want to leave any trace of specific web browsing activity. InPrivate Browsing in Internet Explorer 8 helps prevent your browsing history, temporary Internet files, form data, cookies, and usernames and passwords from being retained by the browser, leaving no evidence of your browsing or search history.“

From Google:

Explore Google Chrome features: Incognito mode (private browsing)

For times when you want to browse in stealth mode, for example, to plan surprises like gifts or birthdays, Google Chrome offers the incognito browsing mode.

  • Webpages that you open and files downloaded while you are incognito aren't recorded in your browsing and download histories.
  • All new cookies are deleted after you close the incognito window.”

Is it?

 

1. Go to “C:\Documents and Settings\{user-name}\Application Data\Macromedia\Flash Player\#SharedObjects\{some-hash-code}” and delete the some-hash-code folder.

2. Browse In-private using IE8 or Incognito using Chrome to some flash site (youtube for example)

3. Close the browser

4. See if the files were deleted…

 

 

(The same behavior was detected for Fire Fox’s private mode)

 

So, what happened?

It might be a bug or maybe some Macromedia workaround – In any case you should be more carful while browsing those flash sites. The files left behind are flash-cookies, sometimes saved in folders which renamed after the domain who created them…

And final note regarding IE8 In-Private browsing:

(since I’m not fully familiar with the parallel options of chrome & fire fox I’m not relating to them in this paragraph)

In-Private browsing feature is mainly focusing on protecting the user from being tracked by the server side, and not being tracked on the client side.

You should use In-Private browsing while trying to avoid sharing your information, such as browsing behavior, with 3rd party web sites of web sites you visit often in regular mode.

The discussed "”bug/feature/workaround” only make this argument more valuable.

 

* I would like to thank Shahar Bar for letting me know about the issue a while ago.

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

Using regular returnValue methods is not working anymore for IE8.

While looking for the cause I came across a very nice and clean solution for doing that:

IE8 ModalDialog returnValue

Posted by dorong | with no comments

Since yesterday I keep getting a red screen when I use chrome telling me YNET may harm my computer.

The reason is elements from www.qdigital.co.il on the page which are marked as malware for some reason.

ynet

Posted by dorong | 4 comment(s)
תגים:

In case you came across a situation where you suddenly can’t browse your MOSS 2007 application locally, but still can access remote MOSS servers while others can access your application remotely:

KB963027

The solutions are:

1. Uninstall the update

2. Use IE6…

3. http://support.microsoft.com/kb/963027/ – read for more details

For more information see also:

http://blog.mpecsinc.ca/2009/04/sbs-2008-ie-update-kb963027-breaks.html

http://spiderwool.blogspot.com/

http://msmvps.com/blogs/bradley/archive/2009/04/16/sbs-2008-ie-update-kb963027-breaks-companyweb-access-from-the-server.aspx

Update: Another useful link:

http://support.microsoft.com/kb/896861/en-us

I would like to thank our people at Netwise: Avi Haimovich & Rani Of for the useful research of that matter.

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

When trying to use ACT 5.5 Internet Explorer Compatibility Tool I got the following message:

“The file size exceeds the limit allowed and cannot be saved”

You can read about the problem here and download the hotfix needed in order to fix it here.

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

While browsing using Internet Explorer 8, I received several times  xss filter notice saying: “Internet Explorer has modified this page to help prevent cross-site scripting” resulting an empty page, usually after trying to submit a form.

Although this is a security issue handled by IE in order to protect the user, sometimes it is blocking legit sites trying to do legit operations.

If you are a website owner and you received complains regarding this issue you can check your web site using ACT 5.5 AKA “Microsoft Application Compatibility Toolkit 5.5” which can be downloaded directly from here.

This is the result when using the Internet Explorer Compatibility Test Tool an my example web site:

image

As you can see the tool allows me to detect what happened and why during my browsing session inside my web site. (The tool also works on remote sites and can be used for solving other compatibility issues as well)

As for my example, I used the one I found on MSDN – here.

Resolution:

1. Fix your code and make sure you don’t perform illegal operations such as cross site scripting.

2. Ask you users to disable the xss filter on their machines (BAD!)

3. Set this response header: “X-XSS-Protection: 0” in order to disable this feature (BAD!)

You can set the response header directly from the web.config by adding this section:

<system.webServer> 

<httpProtocol>
    <customHeaders>
      <clear />
      <add name=" X-XSS-Protection" value="0" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Please make sure you read the MSDN article first so you’ll know exactly what the risks you are taking by doing so.

Posted by dorong | 2 comment(s)
תגים:, ,

I managed to wrap both search provider into a feature so it will be easier to deploy them. The new feature includes a simple WebPart which displays link buttons for installing the providers.

Installation process can be viewed here:

 

The entire solution can be downloaded here.

In order to compile these files need to be copied into your Program Files/Netwise.

(The wsp file in located inside wsp/Debug folder inside the project)

Posted by dorong | with no comments

This post is following the previous one.

In this post I will demonstrate the second search provider for MOSS – the documents search provider for IE8.

I will be using the same solution, using the same method so this time I will only demonstrate the changes between the two. (The HTML is the same HTML from the previous post)

The service

The interface looks like this:

[ServiceContract]
public interface IDocumentSearchService
{
    [OperationContract]
    [WebGet(UriTemplate = "/Search?q={word}",
        BodyStyle = WebMessageBodyStyle.Bare)]
    [XmlSerializerFormat]
    SearchSuggestion Search(string word);
}

Same as before, a single method with a single parameter.

The Implementation:

public class DocumentSearchService : IDocumentSearchService
{
    private XmlDocument xmlIconDoc;
    #region IDocumentSearchService Members
    public SearchSuggestion Search(string word)
    {
        xmlIconDoc = new XmlDocument();
        xmlIconDoc.Load(@"C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\XML\DOCICON.XML");
        var oFrt = new FullTextSqlQuery(new SPSite(ConfigurationManager.AppSettings["server"]));
        oFrt.QueryText = string.Format(ConfigurationManager.AppSettings["DocumentsSearchQuery"].Replace('@', '"'), word);
        oFrt.ResultTypes = ResultType.RelevantResults | ResultType.SpecialTermResults;
        oFrt.StartRow = 0;
        oFrt.RowLimit = 10;
        oFrt.TrimDuplicates = true;
        ResultTableCollection lstResults = null;
        lstResults = oFrt.Execute();
        var ds = new DataSet();
        ds.Tables.Add("results");
        ds.Tables["results"].Load(lstResults[ResultType.RelevantResults]);
        var results = new SearchSuggestion();
        results.Query = word;
        foreach (DataRow row in ds.Tables["results"].Rows)
        {
            {
                results.Section.Add(new Item
                                        {
                                            Text =  row["title"] + " , " + row["Author"] ,
                                            Description =
                                                StripTagsRegex(row["hithighlightedsummary"].ToString()),
                                            Url = row["path"].ToString(),
                                            Image = new Image() 
                                            { Source = GetIcon(row["FileExtension"].ToString()),
                                                Height=16, Width=16 }
                                        });
            }
        }
        return results;
    }
    static string StripTagsRegex(string text)
    {
        return Regex.Replace(text, "<.*?>", string.Empty);
    }
   
    private string GetIcon(string p)
    {
        try
        {
            if (xmlIconDoc.SelectSingleNode("//DocIcons/ByExtension/Mapping[@Key='" + p.ToLower() + "']") != null)
                return ConfigurationManager.AppSettings["server"] +
                    "/_layouts/images/" +
                    xmlIconDoc.SelectSingleNode("//DocIcons/ByExtension/Mapping[@Key='" +
                    p.ToLower() +
                    "']").Attributes["Value"].Value;
            else
                return ConfigurationManager.AppSettings["server"] +
                    "/_layouts/images/" +
                    xmlIconDoc.SelectSingleNode("//DocIcons/Default/Mapping").Attributes["Value"].Value;
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
         
    }
    #endregion
}

 

As you can see this implementation is a little bit trickier.

The query itself runs the same (different query of course), so does the building of the XML response.

Two main differences are:

1. I’m using StripTagsRegex method to remove tags from the search results because some of them are retuned with internal tagging. (the hithighlightedsummary field)

As for the Image, I’m calling the GetIcon method which uses MOSS internal xml file for mapping file extensions and icons. I’m doing it this way so the user will have the exact same experience he gets while searching inside the portal.

The file is usually located at:

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\XML\DOCICON.XML

Depends on how you installed the server.

This is a part of the file, just so you’ll know how the mapping goes:

image 

(This file is usually manually altered when installing new IFilters)

The XML defining the service

<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>Documents Search</ShortName>
  <Url 
    type="text/html" 
    template="http://doron-g/searchcenter/Pages/Results.aspx?k={searchTerms}" 
    />
  <Url 
    type="application/x-suggestions+xml" 
    template="http://localhost:52985/SearchProviders/DocumentSearchService.svc/Search?q={searchTerms}" 
    />
  <Image 
    height="16" 
    width="16" 
    type="image/icon">http://www.netwise.co.il/Images/favicon.ico
  </Image>
</OpenSearchDescription>

Same as before, two URL’s, one for redirecting when the user hits Enter and one for the service itself.

The client side script can be found in the previous post.

Adding the provider:

Clicking the second button opens the add search providers dialog:

image

(Notice the Name label - “Document Search”)

After adding the new provider it is now available in the search providers box, along with our people search provider and any other provider we have installed:

image

Selecting the documents search provider and typing a phrase will result a list of documents on the server which contains the phrase:

image

Hitting Enter will redirect me to the default search results page on my portal:

image

Clicking one of the search results in the providers search results will directly open the file without the need to actually navigate to the portal:

image

The entire solution can be downloaded from here.

Posted by dorong | 2 comment(s)

In this post, and the one following it I will present two new search providers for MOSS 2007.

Search provider is an old feature of common browsers, IE8 brings it into a new level with the extended visibility it provides for search results.

You can read more about IE8 search providers IE8 search provider.

The two search providers I will present will be:

1. People search provider for searching people in the portal.

2. Documents search provider for searching documents in the portal.

As we all know MOSS comes with a build-in search capabilities which includes a search engine and a very nice UI for search queries and search results. These two new providers are intended to extend these capabilities so users can search the portal before they actually access it.

My example is depending on Guy Burstein twitter search provider and I thank him for his direction in writing this post.

The example was developed as a regular service which has to be installed on the same machine where the portal is. It can be also deployed as a windows service exposing the service as WCF or as feature installed inside MOSS. In order to keep it simple I will only present the initial version.

Search providers are combined of 3 main parts:

1. The service

2. The XML defining the service

3. Client side script for installing the provider

 

The service

In order to search people inside the portal we must have a portal with indexed defined profiles. I won’t demonstrate how this is accomplished because it’s out of this post’s scope. In my example I simply imported the profiles from our Active Directory.

The service itself contains 2 parts:

1. Interface

2. Implementation of that interface as a service

The interface looks like this:

[ServiceContract]
public interface IPeopleSearchService
{
    [OperationContract]
    [WebGet(UriTemplate = "/Search?q={friend}",
        BodyStyle = WebMessageBodyStyle.Bare)]
    [XmlSerializerFormat]
    SearchSuggestion Search(string friend);
}

As you can see, a very simple interface with a single method the receives a single variable which is the phrase we will be searching.

The Implementations:

public class PeopleSearchService : IPeopleSearchService
{
    #region IPeopleSearchService Members
    public SearchSuggestion Search(string friend)
    {
        var oFrt = new FullTextSqlQuery(new SPSite(ConfigurationManager.AppSettings["server"]));
        oFrt.QueryText = string.Format(ConfigurationManager.AppSettings["query"].Replace('@', '"'), friend);
        oFrt.ResultTypes = ResultType.RelevantResults | ResultType.SpecialTermResults;
        oFrt.StartRow = 0;
        oFrt.RowLimit = 10;
        oFrt.TrimDuplicates = true;
        ResultTableCollection lstResults = null;
        lstResults = oFrt.Execute();
        var ds = new DataSet();
        ds.Tables.Add("results");
        ds.Tables["results"].Load(lstResults[ResultType.RelevantResults]);
        var results = new SearchSuggestion();
        results.Query = friend;
        foreach (DataRow row in ds.Tables["results"].Rows)
        {
            try
            {
                if (row["pictureurl"] != null && row["pictureurl"].ToString() != string.Empty)
                {
                    results.Section.Add(new Item
                                            {
                                                Text = row["title"] + " , " + row["department"],
                                                Description = row["workphone"] + " , " + row["workemail"],
                                                Url = row["path"] + Environment.NewLine + Environment.NewLine,
                                                Image =
                                                    new Image
                                                        {Height = 48,
                                                            Width = 48,
                                                            Source = row["pictureurl"].ToString()
                                                        }
                                            });
                }
                else
                {
                    results.Section.Add(new Item
                                            {
                                                Text = row["title"] + " , " + row["department"],
                                                Description = row["workphone"] + " , " + row["workemail"],
                                                Url = row["path"].ToString(),
                                                       Image =
                                                    new Image { Height = 48,
                                                        Width = 48,
                                                        Source = ConfigurationManager.AppSettings["server"]+"/_layouts/images/no_pic.gif" 
                                                    }
                                                
                                            });
                }
            }
            catch (Exception ex)
            {
                results.Section.Add(new Item {Text = ex.Message, Description = ex.StackTrace, Url = ""});
            }
        }
        return results;
    }
    #endregion
}

As you can see I was using Guy’s search suggestions class to generate the response using search results coming from the query search results. (The query is defined in the web.config so it can be easily altered during development) You can change the fields you ask for in the query so you can present different content in the search results.

I used title and department for the Text field and workphone + workemail for the Description field and the path as Url so clicking a search result will navigate to the personal page of the person.

Image is set by the pictureurl in case the search result contains a pictureurl, otherwise the default image is presented.

(Catch(Exception ex) returns the exception for debugging but it should be removed)

 

The XML defining the service

<?xml version="1.0" encoding="UTF-8"?>
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/">
  <ShortName>People Search</ShortName>
  <Url 
    type="text/html" 
    template="http://doron-g/SearchCenter/Pages/peopleresults.aspx?k={searchTerms}" 
    />
  <Url 
    type="application/x-suggestions+xml" 
    template="http://localhost:52985/SearchProviders/PeopleSearchService.svc/Search?q={searchTerms}" 
    />
  <Image 
    height="16" 
    width="16" 
    type="image/icon">http://www.netwise.co.il/Images/favicon.ico
  </Image>
</OpenSearchDescription>

The XML is the standard XML for search providers, which contains two URL’s – one for navigation in case the user hits Enter in the search box which navigates him to my portal’s default people’s search results page and one for the service which will be addressed while the user is typing inside the search box. (The service we created earlier)

Client side script

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript">
        function Register() 
{window.external.AddSearchProvider('PeopleSuggestion.xml');}
function RegisterDocumentSearch() 
{window.external.AddSearchProvider('DocumentSuggestion.xml');}
</script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
     <button onclick='Register();'>People Search</button>
     <br />
     <button onclick='RegisterDocumentSearch();'>Document Search</button>
    </div>
    </form>
</body>
</html>

The script is the standard script for adding search providers - window.external.AddSearchProvider with the XML location as a parameter.

(This file contains both providers, the documents search provider will be presented in the next post)

The entire solution looks like this: (For both services)

image

When browsing to Default.aspx we have two buttons, one for each search provider:

image

Clicking the first button opens the add search providers dialog:

image

(Notice the Use search suggestions from this provider check box – unique for IE8)

After adding the provider it is now available for me on the search providers box:

image

Selecting it and typing my name results like this:

image

Searching other people, some with image and some without:

image

Clicking a search result leads me directly to the person’s page:

image

Hitting enter in the search box leads me to my portal’s people search results page:

image

 

I would like to thank Yuval Korin for his help in creating this example.

Posted by dorong | 4 comment(s)

As already known, settings forms of MOSS 2007 can usually be found at:

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\12\TEMPLATE\LAYOUTS

Each form (*.aspx file) can be accessed from any site on the portal inside /_layouts folder.

for example:

http://doron-g/_layouts/settings.aspx

and

http://doron-g/SiteDirectory/wiki/_layouts/settings.aspx

Each showing the same form, exposing enabled featured for each site.

In the following posts I will demonstrate how to add new forms to the _layouts folder and how to update (change) an existing form, both intended to extend share point elements to our needs.

In this first example I will upgrade the ‘Title, Description and Icon’ site settings form to be ‘Site Type, title, Description and Icon’ so each site will now have a new setting field called ‘Site Type’. (in this example it will be drop down list for the simplicity of things, but it can be any thing you like – from a drop down list to a complex user control)

The settings page is located in this address (can be accessed from the Site Settings):

http://doron-g/_layouts/prjsetng.aspx

image

CAUTION: The next steps will include changes in the prjsetng.aspx file – I advise you to keep copies of every file you change manually.

Open the prjsetng.aspx file with any text editor and add the required code for generating the new “Site Type” section in the form:

<wssuc:InputFormSection
	Title="Site Type"
	Id="idSiteType"
	runat="server"
	>
	<Template_Description>
		<asp:literal runat="server" text="Select site type" />
		 
	</Template_Description>
	<Template_InputFormControls>
		<wssuc:InputFormControl LabelText="Site type:" runat="server">
			<Template_Control>
				<TABLE border="0" cellpadding="0" cellspacing="0" dir="ltr">
				<TR nowrap>
					<TD class="ms-authoringcontrols">
				
<asp:DropDownList runat="server" ID="SiteType" Width="265">
<asp:ListItem Text="Normal Site" Value="1"></asp:ListItem>
<asp:ListItem Text="Special Site" Value="2"></asp:ListItem>
<asp:ListItem Text="Very Special Site" Value="ccc"></asp:ListItem>
</asp:DropDownList>
					</TD>
				</TR>
				</TABLE>
			</Template_Control>
		</wssuc:InputFormControl>
	</Template_InputFormControls>
</wssuc:InputFormSection>

This will generate the required drop down list with 3 options for our new site type property.

Now we need to add the required code behind for saving the new field as part of the site properties.

In order to do that we need to override the existing class which is handling the form with a new class of our own.

To identify the current class we need to examine the original .aspx file’s header:

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%> 
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.ApplicationPages.ProjectSettingsPage" MasterPageFile="~/_layouts/application.master"      %>

These two lines tell us which class is used from which assembly. In this case ProjectSettingsPage from Microsoft.ShrePoint.ApplicationPages.

Our new custom ProjectSettingsPage class will look like this (inside a simple class library project)

    public class ProjectSettingsPage : Microsoft.SharePoint.ApplicationPages.ProjectSettingsPage
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (!this.Page.IsPostBack)
            {
                if (base.Web.Properties.ContainsKey("SiteType"))
                {
                    
                    this.SiteType.SelectedValue = base.Web.Properties["SiteType"];
                }  
            }
        }
         public TextBox TxtSiteType { get; set; }
         public DropDownList SiteType { get; set; }
         protected void BtnUpdateWebNew_Click(object sender, EventArgs e)
         {
             if (!base.Web.Properties.ContainsKey("SiteType"))
             {
                
                 base.Web.Properties.Add("SiteType", this.SiteType.SelectedValue);
             }
             else
             {
                 
                 base.Web.Properties["SiteType"] = this.SiteType.SelectedValue;
             }
            
             base.Web.Properties.Update();
             BtnUpdateWeb_Click(sender, e);
         }
     
    }

This code is simple asp.net code which saves the new property as site property and / or initialize the control with a default value (As seen in the OnLoad function).

BtnUpdateWebNew_Click is for the OnClick event which will be generated by the OK button which saves the changes made.

In order to connect the existing button with the new method another change is required in the .aspx file:

 <wssuc:ButtonSection runat="server">
	   <Template_Buttons>
		  <asp:Button UseSubmitBehavior="false" runat="server"
		   class="ms-ButtonHeightWidth" OnClick="BtnUpdateWebNew_Click" 
		   Text="<%$Resources:wss,multipages_okbutton_text%>" id="BtnCreate" 
		   accesskey="<%$Resources:wss,okbutton_accesskey%>"/>
	   </Template_Buttons>
 </wssuc:ButtonSection>

All we need to do is change the OnClick attribute to point the new method we created.

(Our new method is saving the selected value of our new property and then calls the base class’s onclick method in order to save the original items of the form)

In order to set the complete form to use our new class the following changes are rquired in the .aspx page header:

<%@ Assembly Name="Netwise.MOSS.CustomPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=ef72d5cefb9f703d"%>
<%@ Page Language="C#" Inherits="Netwise.MOSS.CustomPages.ProjectSettingsPage" MasterPageFile="~/_layouts/application.master"      %>

So now our form is using our new class from out new assembly.

Just copy the new .aspx file to the layouts folder and place the assembly in the GAC. (make sure you use the right publickeytoken, version and culture when you alter the page header)

Our new settings form now looks like this:

image

In the next post I will demonstrate how to create a new settings form so you won’t have to worry about changing the original forms and also have the ability to create custom settings sections for you MOSS application.

Posted by dorong | 4 comment(s)
תגים:, , ,

This post is about enabling web slices on Microsoft Office SharePoint Server 2007.

The following example will demonstrate how to easily implement a simple control adapter which will render the required html code for turning any web part on your server into a live web slice.

First thing I did was creating a class called WebSliceAdapter which inherits from ControlAdpater.

   1: public class WebSliceAdapter : ControlAdapter

ControlAdapter has a single method needs to be overridden:

   1: protected override void Render(HtmlTextWriter writer)
   2: {
   3:     var webpart = Control as WebPart;
   4:     if (webpart != null)
   5:     {
   6:         if (Page.Request.QueryString["sliceitem"] != null && Page.Request.QueryString["sliceitem"].Equals(webpart.ID))
   7:         {
   8:             Page.Response.Clear();
   9:             renderWebSlice(webpart, writer);
  10:             Page.Response.End();
  11:         }
  12:         else
  13:         {
  14:             renderWebSlice(webpart, writer);
  15:         }
  16:     }
  17:     else
  18:     {
  19:         base.Render(writer);
  20:     }
  21: }

(I’m clearing the response and ending it because the webslice is not functioning well for some reason when the html it too large or because of the default  js & css of MOSS. I’m still trying to figure out why and how to overcome this)

The following methods are rendering the required HTML code for the webslice surrounding the web part itself:

   1: private void renderWebSlice(WebPart webpart, HtmlTextWriter writer)
   2: {
   3:     writer.Write(getWebSliceHeader(webpart.ID, webpart.Title, 15));
   4:     base.Render(writer);
   5:     writer.Write("</div></div>");
   6: }
   1: private string getWebSliceHeader(string id,string title,int refreshRate)
   2: {
   3:                  return String.Format(
   4:                     @"<div class='hslice' id='hsliceitem{0}'>
   5:                         <a rel='feedurl' href='?sliceitem={0}'></a>
   6:                         <span style='visibility:hidden;display:none;' class='ttl'>{2}</span>
   7:                         <span class='entry-title' style='visibility:hidden;display:none;'>{1}</span>
   8:                         <div class='entry-content'> ",
   9:                     id, title, refreshRate.ToString());
  10: }

 

 

 

 

getWebSliceHeader gets 3 parameters: id of the webslice, the title to display in the favorites bar and the refreshRate which configures the time in minutes for the webslice to be updated. Every time the content of the webslice will be changed the webslice will appear as bold in the favorites bar indicating the user an update is available.

after signing the assembly and placing it into the GAC all is needed in order to make it work is a small change in the compat.browser file located under App_Browsers folder within the web application:

   1: <browser refID="Default">  
   2:   <controlAdapters>
   3:    <adapter controlType="Microsoft.SharePoint.WebPartPages.ContentEditorWebPart" 
   4:             adapterType="Netwise.MOSS.Extensions.ControlAdapters.WebSliceAdapter, Netwise.MOSS.Extensions.ControlAdapters, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4d08bf77f3d60265" />
   5:    <adapter controlType="Microsoft.SharePoint.Publishing.WebControls.SummaryLinkWebPart,Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" 
   6:             adapterType="Netwise.MOSS.Extensions.ControlAdapters.WebSliceAdapter, Netwise.MOSS.Extensions.ControlAdapters, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4d08bf77f3d60265" />
   7:    <adapter controlType="Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart,Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" 
   8:             adapterType="Netwise.MOSS.Extensions.ControlAdapters.WebSliceAdapter, Netwise.MOSS.Extensions.ControlAdapters, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4d08bf77f3d60265" />
   9:   </controlAdapters>
  10: </browser>

Make sure to use full assembly names for every web part you wish to enable the adapter for. In this example I’m using the ContetEditorWebPart, SummaryLinkWebPart and the ContentByQueryWebPart.

* In this example I’m enabling the adapter for all the browsers but you can / should use IE8 definition only.

** Webslices use a different http header when they update so you might need to add a separate definition as well.

This screen shot is from my MOSS homepage after enabling the adapter. This page contains 1 content query web part for showing me my tasks, a news list and a content editor web part. As you can see I now have 3 webslice feeds which I can see from the top bar and on the page itself:

clip_image002

clip_image002[4]

Adding the content editor web part:

clip_image002[6]

The webslices on the favorites bar:

clip_image002[8]

Viewing the content of the webslice:

clip_image002[10]

More Posts Next page »