Anyone who has tried to brand their SharePoint sites has probably encountered the 5 stages of grief when learning about the core.css:

  1. Disbelief - “They couldn’t have possibly designed it this way; I must be missing something!”
  2. Anger – “This is ridiculous! What a waste of time and money!”
  3. Bargaining – “If I can get this to work the way I want, I promise I will love SharePoint forever.”
  4. Depression – “Maybe I’m not cut out for this SharePoint stuff. I should do something else. I wonder if there are any openings at Best Buy.”
  5. Acceptance – “Okay, I just won’t brand my site the way I wanted to do it.”

Essentially, Microsoft (in it’s infinite wisdom) has most of the styles, for any particular template, stored in the core.css.

There has been plenty of discussion on how one might modify those styles. Heather Solomon is certainly the resident expert on it. When diving in you might start to read about things like “ghosted” and “unghosted” or “customized” and “uncustomized”.

The reality is that you may only want to make one minor change to the core.css and not have the whole thing be sent into the file system in a “customized” state. Or maybe you’re a purist (read: anal-retentive) and you just don’t like the idea of your core.css being stored on the file system.


Understanding that a value in a stylesheet takes precedence in the order it was applied (hence the name “cascading”) you might start to think:

“Gee, if I create a new style in a new stylesheet with the same name of the one stored in the core.css, it should overwrite that style and all will be wonderful again!”

A-hem…not exactly.

By it’s very nature, core.css is designed to load after any other specified stylesheet. Therefore, no style can be overwritten because core.css is ALWAYS applied last.

CURSE YOU MICROSOFT!

So to change this behavior, we must hijack the rendering of the CSSLink server control in SharePoint.

CSSLink is used in order to load a specified stylesheet (using the DefaultUrl property). This will return all of the system stylesheets along with the path of the stylesheet you specified. All of this will be in order (yep, you guessed it) with the core.css sheet specified last.

The class that I built is rooted in the work of Michael Hofer and CleverWorkarounds.

This class creates a server control that is to be used in replace of CSSLink and is called EnhancedCssLink. I would read Michael Hofer’s blog on how you might add this class to your SharePoint site (build a .dll, mark it as safe in your web.config, deploy it to your SharePoint GAC or bin folder, etc.) If you can’t find out how to do it there, Google it man!

Michael Hofer’s class puts the core.css last in the order, which can be problematic. CleverWorkarounds improved on this idea by switching the core.css with a specified .css sheet. Ultimately, giving the specified stylesheet the highest priority. Cool!

I expanded on these two ideas by allowing the user to specify multiple stylesheets (separated by a comma). The DefaultCss property is now named “CSSPath”. Here’s an example on how to use it:

<PublishingEnhancements:EnhancedCssLink runat=”server” CSSPath=“../../_styles/master.css, ../../_styles/webparts.css”/>

This will now return the system stylesheets in the proper order with the specified stylesheets loaded last.

Finally, here is my code for the EnhancedCssLink class:

 

//Publishing Enhancements

namespace PublishingEnhancements

{

public class EnhancedCssLink : CssLink

{

public EnhancedCssLink() : base() { }

private string CSS;

public virtual string CSSPath

{

get

{

return CSS;

}

set

{

CSS = value;

}

}

protected override void Render(System.Web.UI.HtmlTextWriter output)

{

// Let base render the stylesheets

StringWriter sw = new StringWriter();

base.Render(new HtmlTextWriter(sw));

string renderedOutput = sw.ToString();

if (this.CSS == null) {

output.Write(renderedOutput);

}

else

{

// Split the styleSheets into an array

string[] styleSheets = renderedOutput.Split(new char[] { ‘\n’ }, StringSplitOptions.RemoveEmptyEntries);

if (styleSheets.Length == 0)

{

output.Write(renderedOutput);

}

else

// Render the system stylesheets, then render any specified stylesheets after that

{

string[] customStyleSheets = this.CSS.Split(new char[] { ‘,’ }, StringSplitOptions.RemoveEmptyEntries);

output.Write(string.Concat(styleSheets));

for (int i = 0; i < customStyleSheets.Length; i++)

{

output.Write(“<link rel=’stylesheet’ type=’text/css’ href=’” + customStyleSheets[i].ToString() + “‘ />”);

}

}

}

}

}

}

 

December 18th, 2007Citrix Web Interface Web Part

Found an awesome array of features provided by Citrix that enables users to access their Citrix distributed applications right from SharePoint.

This will prove very useful for our Physician’s Portal. Now our physician’s can access hospital applications from their office or home without leaving the SharePoint portal!

Citrix has some great documentation on how to install/configure this Web Part. Here is probably a good place to start. You will have to get the WISP (Web Interface for SharePoint) .wsp files through your MyCitrix account. Be sure to grab the administrator’s guide too.

The requirements are:

Citrix Presentation Server (of course!)

WSS 3.0 and/or MOSS 2007 (duh!)

Visual J# 2.0 Second Edition (installed on all your WFE)

For the most part installing and deploying the solutions was a breeze. I used page 17 from the guide. Followed the steps VERY carefully.

NOTE: The CitrixWssCore.wsp has to be installed/deployed before anything else!

Setting up Single Sign-On turned out to be a bit of a challenge. Here are some steps that might prove handy:

1. Go to the server that is running your Central Admin. Under Administrative Tools > Services ensure that the “Microsoft Single Sign-on Service” is set to start automatically. I would also go into properties of this service and specify an administrator account under the “Log On” account. I just chose our SharePoint farm administrator account. If you change the account make sure to restart the service.

2. Load up Central Admin and go to Operations > Manage Single Sign-On. This administers the Single-Sign On service and apparently can only be accessed through the local machine (I’m sure there is a way around this, any ideas?) For the remainder of these steps I would suggest doing them from the server running Central Admin.

3. Click on “Manage server settings” and specify an account name or group that will access to create databases in your SharePoint configuration. You should choose the same account that you specified for the MSSO service in step 1.

4. Now navigate to the site in which you are planning to run the Citrix web part. Under the site settings for that site you should see “Citrix Administration”. (That’s of course assuming that you activated it under “Site collection features” on the same site settings page)

5. Under “Citrix Administration” click “Single Sign-On”

6. The settings we’ve got are “Pass-Through” for Client Single Sign-On and “Microsoft Single Sign-On” for the Server Single Sign-On

7. Above you will see a link to apply your settings and then configure Microsoft Single Sign-On. Well….Do what it says!

8. Fill in the form with your custom information and submit it. That should be it.

This configuration seemed to work for us. If you are still having trouble, or you’re seeing the “Resources not found” message in your Citrix Web Part, try this thread for more information.

Physician's Directory on a Cisco 7960 Series IP Phone

I finally rolled out my solution for a Physician Directory on our Cisco VOIP system. The staff appears to be pleased with this added functionality. This has also opened up more ideas on how we can continue to leverage our knowledge in ASP.NET to provide services on our phone system.

The Cisco phone system operates entirely off of XML. For the most part, it is a glorified XML reader!

Guillaume Gros provided the foundation for a .NET class that I was able to add functionality to.

Now I have my most common functions that I would need (for instance “give me a button”) in managed code. In the code-behind on my aspx page I include the class, call a function, and supply a minimum set of parameters. The class returns the necessary XML to be rendered. Neat!

Let me give you an example (VB):

 

Dim btnSubmit As New CiscoSoftKey(“Search”, “SoftKey:Submit”, 1)

 

This declares a new CiscoSoftKey button specifying that the text of the button will be “Search” and “SoftKey:Submit” is a URL parameter that the Cisco phone interprets as “submit the following parameters”. 1 indicates the position of the button (out of 4 available).

The class accepts the parameters and returns the XML required to be display a Cisco Soft Key on the phone.

Simply Response.Write the XML that is returned and the Cisco phone (for the most part) does all the rest. Below is the code I wrote to build the screen shot you see above (search for physician):

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

Response.ContentType = “text/xml”

‘Create an Array of CiscoInput items
Dim Inputs As New ArrayList
Dim LName As New CiscoInput(“Last Name”, “lname”, “U”, “”)
Dim FName As New CiscoInput(“First Name”, “fname”, “U”, “”)
Inputs.Add(LName)
Inputs.Add(FName)

‘Create and array of CiscoSoftKeyItems
Dim SoftKeys As New ArrayList
Dim btnSubmit As New CiscoSoftKey(“Search”, “SoftKey:Submit”, 1)
Dim btnBack As New CiscoSoftKey(“<<”, “SoftKey:<<”, 2)
Dim btnEROnCall As New CiscoSoftKey(“EROnCall”, “http://<hidden.com>/physdir/eroncall.aspx”, 3)
Dim btnExit As New CiscoSoftKey(“Exit”, “SoftKey:Exit”, 4)
SoftKeys.Add(btnSubmit)
SoftKeys.Add(btnBack)
SoftKeys.Add(btnEROnCall)
SoftKeys.Add(btnExit)

‘Send the array and other information to the .dll to retrieve xml result

Dim CPI As New CiscoIPPhoneInput(“Physician Directory Search”, “Enter search criteria”, “<hidden.com>.aspx”, Inputs, SoftKeys)

‘Write out the XML Code
Response.Write(CPI)

End Sub

And the two methods handled in the class:

public class CiscoInput

{

public string DisplayName;
public string QueryStringParam;
public string InputFlags;
public string DefaultValue;

public CiscoInput(string DisplayName, string QueryStringParam, string InputFlags, string DefaultValue)

{
this.DisplayName = DisplayName;
this.QueryStringParam = QueryStringParam;
this.InputFlags = InputFlags;
this.DefaultValue = DefaultValue;
}
public override string ToString()
{
return “<InputItem>\r\n<DisplayName>” + this.DisplayName + “</DisplayName>\r\n<QueryStringParam>” + this.QueryStringParam + “</QueryStringParam>\r\n<InputFlags>” + this.InputFlags + “</InputFlags>\r\n<DefaultValue>”+this.DefaultValue+“</DefaultValue>\r\n</InputItem>\r\n”;
}
}

public class CiscoSoftKey
{
public string Name;
public string URL;
public string URLDown;
public int position;
public CiscoSoftKey(string Name, string URL, int position)
{
this.Name = Name;
this.URL = URL;
this.position = position;
}

public override string ToString()
{

return “<SoftKeyItem>\r\n<Name>” + this.Name + “</Name>\r\n<URL>” + this.URL + “</URL>\r\n<Position>” + this.position + “</Position>\r\n</SoftKeyItem>\r\n”;
}
}

Attached is the source code for the class. Also, I would recommend the book Developing Cisco IP Phone Services.

 

Here’s the code you mooch!

Cisco IP Phone Services Class

 


© 2007 travislowdermilk.com | iKon Wordpress Theme by TextNData | Powered by Wordpress | rakCha web directory