何小碩's profileGet More... ExperiencePhotosBlogListsMore Tools Help

Blog


    January 31

    Colour (color) Calendar(2)

    The in-built calendar view is SharePoint 2007 is a heck of a lot better than the one in 2003 but it still lacks a feature requested by many customers - the ability to colour (or color for our US friends) code the entries like you can in Outlook.
    I had previously created a coloured calendar in 2003 and started to investigate how this might be achieved in 2007. Whilst there are commercial alternatives avilable, the licencing models can be cumbersome - tied to a physical machine etc which make development and deployment a pain.
    (Update: if you want to know about the 2003 version, click here)
    In SharePoint 2003 the calendar was rendered entirely using Javascript so it was a question of modifying the schema.xml file for the list to output the relevant parameters and then overiding an ows.js function to ensure the colours got rendered. However in 2007 the calendar view is generated from code, specifically a bunch if classes like SPListView, SPCalendarView etc. Lots of this class heirarchy is marked as sealed or internal so it is not easy to try to inherit and change the behaviour. Plus lots of the code is obfuscated too.
    As an alternative approach I began to look at perhaps manipulating the calendar in Javascript once it had been delivered to the browser (thanks to Todd Bleeker for inspiration here)
    First of all I created a standard SharePoint Calendar list. Then I added a Choice column called Category and put the following values in the choice field.
    Appointment
    Birthday
    Business
    Important
    Vacation

    Secondly I created a calculated column called CatTitle set to the following formula as seen in the image,
    =Category & "|||" & Title
    You could make these site columns if you wish to re-use elsewhere.

    Finally change the calendar view on the list so that the display field for each of day/week/month view is the CatTitle field.

    Now we need to insert some javascript to edit the titles so that they category and pipes are removed and the entries are coloured in. Edit the calendar page (Site Actions -> Edit Page) and insert a content editor web part UNDER the calendar list view. Paste the following javasript into the source view. Make the web part hidden.

    <script>
    var SEPARATOR = "|||";

    var nodes, category;

    nodes = document.getElementsByTagName("a");

    for(var i = 0; i < nodes.length; i++)
    {


    if(nodes[i].innerText.indexOf(SEPARATOR) != -1)
    {

    UpdateCalendarEntryText(nodes[i]);


    var foundNode = nodes[i];
    var trap = 0;

    while(foundNode.nodeName.toLowerCase() != "td")
    {

    foundNode = foundNode.parentNode;

    trap++;
    if(trap > 10)
    {

    break; // don't want to end up in a loop

    }
    }


    var colour = GetCalendarColour(category);
    if(colour != "")

    foundNode.style.background = colour;
    }
    }


    function UpdateCalendarEntryText(anchorNode)
    {


    var children = anchorNode.childNodes;
    for(var i = 0; i < children.length; i++)
    {


    if(children[i].nodeType == 3 && children[i].nodeValue.indexOf(SEPARATOR) != -1)
    {

    var parts = children[i].nodeValue.split(SEPARATOR);

    category = parts[0];
    children[i].nodeValue = parts[1];
    }


    else
    UpdateCalendarEntryText(children[i]);
    }
    }


    function GetCalendarColour(desc)
    {


    var colour;
    switch(desc.toLowerCase())
    {

    case "appointment":

    colour = "#ffd266";
    break;
    case "birthday":

    colour = "#ae99dc";
    break;
    case "business":

    colour = "#8aabe0";
    break;
    case "important":

    colour = "#e77379";
    break;
    case "vacation":

    colour = "#fffa91";
    break;
    default:
    colour = "";
    }


    return colour;
    }

    </
    script>

    You should now have a coloured calendar! Month, week and day views should appear coloured. Remember that if you place a calendar web part on a site that you will need a hidden content editor web part containing the javascript as well.

    If you want to change the categories and colours, then simply add or change entries in the choice column and change the function in the Javascript accordingly.
    In an ideal world this would be some sort of deployable feature, perhaps custom list definition. I will have a look at doing this at some point if I get time....

    comes from:http://planetwilson.blogspot.com/2007/09/sharepoint-2007-colour-color-calendar.html

    Colour (color) Calendar(1)

    I have finished working on what I hope is the final version of the colour calendar.

    The previous version was well received but was lacking in a couple of areas. Firstly it required the creation of custom calendar lists. The custom list was based on the in-built calendar list but was missing some of the functionality like Connect to Outlook. Secondly you had to manually add the colour coder web part to calendar view screens.

    The new version is built right into the existing SharePoint calendar list. Once you have deployed the solution you will find a new option under list settings for calendar lists to enable and disable the colour coding functionality.

    Enabling the functionality creates the needed columns and adds the web part to any calendar views present on the list. Simply by clicking the enable button you will have a working colour calendar.

    Disabling the functionality simply switches the display titles back to the default, it does not remove the colour coding web part or remove the columns so that you do not lose data. You can simply switch it back on again to see the colouring.



    The colour coder web part works as before defaulting to a central colour mapping list in the root web but you can override to a local one if you wish.

    If you add additional calendar views to your list, simply disable and then re-enable the functionality to have it applied to the new views.



    It has been built so as to be upgradeable from the previous version. If you run the upgrade script as opposed to the deploy script then you will find your old colour calendar lists still work but the option to create a "Colour Calendar List" will be removed. You can obviously just use the new method for any new colour calendars you wish to create.

    Thanks for all the feedback so far.

    As usual the latest release can be found here

    Regards,

    Mark Wilson

    ref:http://planetwilson.blogspot.com/2008/02/version-2-of-colour-calendar-released.html

    January 29

    在 MOSS 中新增一變更密碼頁面來供使用者進行密碼變更

    Goal: Add a Change Password capability to an extranet MOSS site so that users can change their password.

    Assumptions: Environment uses Windows authentication not Forms Based Authentication. Active Directory is the Membership Provider.

    Since this is an extranet, users will never have access to a workstation that is a member of the domain. Last time I had to do this, I was working with SharePoint Portal Server 2003 and had to write this as a custom web part that talked to Active Directory using the Directory Services API's in .NET 1.1. Bringing that old web part into MOSS is not the best way to do this. It's better to use the ChangePasword login control from the .NET 2.0 (and later) framework, and create a very simple _Layouts page to do the job. Then make that page available by putting a link to it on the Welcome menu.

    But wait! Is creating a change password page necessary? Well, I think so: although there is a password.aspx page in _layouts, when I try to use it, it produces the following error: "Operation is not valid due to the current state of the object." I suspect it would take more effort to try to figure out what it's complaining about than to make our own change password page.

    There are some differences between the most basic MOSS change password page and a generic ASP.NET change password page. They are:

    MOSS Page

    Pure ASP.NET Page

    <%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

    <%@ Page Language="C#" MasterPageFile="~/_layouts/application.master"

    Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>

    <%@ Import Namespace="Microsoft.SharePoint" %>

    <asp:Content ID="MainContent" ContentPlaceHolderID="PlaceHolderMain" Runat="Server">

    <asp:changepassword id="ChangePassword1" runat="server"

    PasswordHintText =

    "Please enter a password at least 7 characters long,

    containing a number and one special character."

    NewPasswordRegularExpression =

    '@\"(?=.{7,})(?=(.*\d){1,})(?=(.*\W){1,})'

    NewPasswordRegularExpressionErrorMessage =

    "Error: Your password must be at least 7 characters long,

    and contain at least one number and one special character." >

    </asp:changepassword>

    </asp:Content>

    <%@ page language="C#"%> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html>

    <head>

    <title>Change Password with Validation</title>

    </head>

    <body>

    <form id="form1" runat="server">

    <div>

    <asp:changepassword id="ChangePassword1" runat="server" PasswordHintText = "Please enter a password at least 7 characters long, containing a number and one special character." NewPasswordRegularExpression = '@\"(?=.{7,})(?=(.*\d){1,})(?=(.*\W){1,})' NewPasswordRegularExpressionErrorMessage = "Error: Your password must be at least 7 characters long, and contain at least one number and one special character." >

    </asp:changepassword>

    </div>

    </form>

    </body>

    </html>

    In other words, all the tags other than the <asp:changepassword> tags provide the chrome, and the <asp:changepassword> tag provides the functionality we care about. Note that neither page needs a code-behind at this point. Of course, if you need to do custom logic, you would want a code behind.

    So now that the code is developed, how do we deploy it? Well, we want to put the file in the _Layouts directory, and have a link to it show up on the Welcome (Personal Actions) menu. So we need a SharePoint Feature that specifies a custom action. Wrapping that in a SharePoint Solution Package wouldn't hurt either.

    References:

    Useful examples: http://glorix.blogspot.com/2007/08/custom-action-locations-and-groupid.html and http://office12.blogspot.com/2007/07/changepassword-page-for-fbaform-based.html

    MSDN ChangePassword Control reference: http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.changepassword.aspx

    Except that we are forgetting one very important thing: testing! So before I actually deployed this via a feature, I dropped the Change Password ASPX page into the Layouts directory and tried it out. I ran into two issues:

    1. The regular expression documented in the MSDN page listed above does not work as advertised. I think there is an unmatched doublequote.
    2. When I click the button to change the password, I get back an "Unknown Error" message.

    So now I am confused – isn't the concept of a membership provider supposed to abstract away the question of which authentication method is being used? But it seems like it isn't. That is, I have to conclude that I can't use the Change Password login control with Windows Authentication, only with Forms Based Authentication.

    Michael

    ref:http://blumenthalit.net/blog/Lists/Posts/Post.aspx?ID=11

    January 12

    Convert Sub-Site To Site Collection

     

    I finally figured it out! This was supposed to be one of those very simple tasks that I should have been able to do without any custom code. Turning a sub-site (or web) into a site collection (or top level site) turned out to be the most difficult task I've yet to face with SharePoint 2007. In theory you should be able to do this using the following commands which could be put into a batch file:

    REM Create a test web for exporting
    stsadm -o createweb -url "http://intranet/testweb" -sitetemplate "SPSTOPIC#0"
    
    REM Export the test web to the filesystem
    stsadm -o export -url "http://intranet/testweb" -filename "c:\testweb" -includeusersecurity -versions 4 -nofilecompression -quiet
    
    REM Create a managed path for the new top level site
    stsadm -o addpath -url "http://intranet/testsite" -type explicitinclusion
    
    REM Create an empty site with a default site template (note that if you don't specify a template you have to manually activate the required features)
    stsadm -o createsite -url "http://intranet/testsite" -owneremail "someone@example.com" -ownerlogin "domain\username" -sitetemplate "SPSTOPIC#0"
    
    REM Import the site
    stsadm -o import -url "http://intranet/testsite" -filename "c:\testweb" -includeusersecurity -nofilecompression -quiet

    Unfortunately what you get is only a partially functional site (and in some case not functional at all). There are several errors that you are likely to encounter after running the above using the created testweb or your own existing web. The first and most obvious error is that when you load the default.aspx page of the new site you may get a File Not Found error (note that running the above as is will not give you this error). This is the result of the publishing pages PageLayout URL getting messed up (effectively still pointing to an old value). I addressed this specific issue with a separate command (http://stsadm.blogspot.com/2007/08/fix-publishing-pages-page-layout-url.html) and the I've encapsulated that functionality into the new commands I've created which are detailed below.

    The next error you're likely to see is on the Area Template Settings page (Site Settings -> Page layouts and site templates). The specific error is "Data at the root level is invalid. Line 1, position 1".

    Anyone who's done a lot of XML work should recognize this error as an XML parsing error. This error occurs if the web you imported from was set to inherit it's page layouts from it's parent. When a web is setup this way there's a property called "__PageLayouts" which gets set to "__inherit".

    For a top level site collection this value should always be either an empty string (all page layouts are available) or XML describing which layouts are available. The import operation does not consider this and leaves the value as is thus resulting in the XML error when attempting to parse "__inherit" as XML. The fix for this is simple enough - change the value to an empty string. Unfortunately that's not all we have to do. Fixing the above error results in the page loading without errors, however, the page layouts section does not load. There's still several issues that need to be resolved. If you now go and view the master gallery (Site Settings -> Master pages and page layouts) you should see all the default page layouts. If you have any custom page layouts those won't exist and will cause problems.

    Also, if you attempt to edit a file you'll notice that even though we used a publishing template it doesn't prompt you to check the file out. What's more is that once you view the form for a layout you should see that only the core fields are present (no Content Type, Associated Content Type, Variations, etc.).

    There are several things that need to be fixed here - first we need to activate all the features that would otherwise be activated on a new site collection (need this so that we can get the publishing workflow options enabled). Second we need to reset all the properties for the gallery to match that of our source gallery (namely we need to allow management of content types).

    Third we have to change the ContentType field from being a Text field to being a Choice field (more about this in a minute). Fourth we need to re-associate each file as a Page Layout file by setting all the necessary properties (Content Type, Associated Content Type, etc.). In regards to changing the ContentType field this is the one that caused me the most headache to figure out. For some reason during the import of the site this field gets a bit messed up (note that I'm not referring to the ContentType field that is linked in from the Page content type which is associated with the library but rather another field that is part of the gallery definition itself). The field should be a Choice field with options such as "Page Layout", "Publishing Mater Page", "Master Page", and "Folder". However, during the import the field is converted to a Text field - don't ask me why.

    The result is that when you query for the list of available page layouts the unmanaged code that Microsoft uses to do the actual query chokes because it can't find a matching value in the list so it produces an invalid query which will always return no results back. Unfortunately there's no way to programmatically change this back to a Choice field using the provided API as going from a Text field to a Choice field is unsupported (I don't really understand why - seems it should be supported so long as the Choice field allows fill-ins). To fix this I had to hack the database - there's a table called AllLists which stores the definition of the gallery in a field called tp_fields - the definition is an XML file so I simply had to figure out what the XML should be and set it accordingly.

    UPDATE 9/4/2007: I just discovered that another issue is related to the global navigation. If you view the navigation via the browser it will look as though everything is just peachy but if you attempt to manipulate the navigation programmatically you'll find that the PublishingWeb's GlobalNavigationNodes property is empty (no items are present). This is because the global navigation is stored at the site collection level so when you import the web it does not take the global navigation with it, just the current. The fix is simple enough - just loop through the current navigation collection and copy it to the global navigation collection. This will help to allow other programmatic manipulations of the global navigation to succeed. So, to summarize the things that need to be repaired after importing into your empty site collection:

    1. Activate any features that are needed by the site
    2. Set the master page gallery settings
      1. Enable content type management
      2. Set your publishing options
      3. Fix the Content Type field so that it's a Choice field type
    3. Fix the Page Layouts and Site Templates
      1. Set the "__PageLayouts" property to an empty string (can then be set to something else using SetAvailablePageLayouts() but first needs to be set to an empty string as SetAvailablePageLayouts() will not work until it's fixed)
      2. Copy any missing page layouts from the source
      3. Set all appropriate properties on each page layout
    4. Fix all the publishing pages PageLayout property to have the correct URL
    5. UPDATE 9/4/2007: Update the global navigation

    Some of the above can be done via the browser but most of it requires programmatic changes. In order to solve all these problems I've created two custom stsadm commands - the first will take a site make all the repairs identified above (so it assumes you've already imported the site).

    The second basically just abstracts the whole process of exporting a web, creating a site, importing into the site, and then repairing the site (this way the entire process can be done with just one command). The commands I created are detailed below (forgive the verbosity of the names - I had trouble coming up with something shorter).

    1. gl-repairsitecollectionimportedfromsubsite

    The code is fairly well documented so rather than discuss it all (there's a lot of it) I've linked it to this post here. The syntax of the command can be seen below:

    C:\>stsadm -help gl-repairsitecollectionimportedfromsubsite
    
    stsadm -o gl-repairsitecollectionimportedfromsubsite
    
    Repairs a site collection that has been imported from an exported sub-site.  Note that the sourceurl can be the actual source site or any site collection that can be used as a model for the target.
    
    Parameters:
            -sourceurl <source location of the existing sub-site or model site collection>
            -targeturl <target location for the new site collection>

    Here’s an example of how to repair the site created using the batch file above:

    stsadm –o gl-repairsitecollectionimportedfromsubsite –sourceurl "http://intranet/testweb/" -targeturl "http://intranet/testsite/"

    2. gl-convertsubsitetositecollection

    As stated above, this command is just an abstraction of other commands - it simply calls out to stsadm to do export the site (note that you can provide a previously exported site file/folder), create the managed path, create the empty site, import the site, and finally repair the imported site. As there's nothing spectacular going on here I didn't bother culling the code out in this post (download the project if you're interested in the details). The syntax of the command can be seen below:

    C:\>stsadm -help gl-convertsubsitetositecollection
    
    stsadm -o gl-convertsubsitetositecollection
    
    Converts a sub-site to a top level site collection via a managed path.
    
    Parameters:
            -sourceurl <source location of the existing sub-site or model site collection>
            -targeturl <target location for the new site collection>
            [-createmanagedpath]
            [-exportedfile <filename of exported site if previously exported>]
            [-nofilecompression]
            [-ownerlogin <DOMAIN\Name>]
            [-ownername <display name>]
            [-secondaryemail <someone@example.com>]
            [-secondarylogin <DOMAIN\Name>]
            [-secondaryname <display name>]
            [-lcid <language>]
            [-sitetemplate <site template if exportedfile is specified - must match the template of the exported site>]
            [-nositetemplate][-title <site title>]
            [-donotcreatesite (specify if the site to import to already exists)]
            [-description <site description>]
            [-hostheaderwebapplicationurl <web application url>]
            [-quota <quota template>]
            [-deletesource]

    Here's an example of how to do all that the batch file above is doing (minus the creation of the testweb) as well as the repair operation all with one command:

    stsadm –o gl-convertsubsitetositecollection –sourceurl "http://intranet/testweb/" -targeturl "http://intranet/testsite/" -createmanagedpath -nofilecompression -owneremail "someone@example.com" -ownerlogin "domain\user" -deletesource

    One area of improvement may be to pull the owner and secondary owner information from the source site collection so that this information does not have to be provided - maybe I'll do that if I feel I have the time or if people express enough interest. Note that you can specify a title and description but they'll be overwritten during the import - I only included them so that if the import fails and you're left with an incomplete site you'll at least have a name for it if you should forget to delete it and stumble upon it a year later.

    Figuring out how to solve all the issues surrounding converting a web to a site collection was a real pain the a$$ so any feedback that people have on this would be greatly appreciated - hopefully if there are others out there that have stumbled on this then they'll benefit from it as well. Keep in mind also that though I think I've solved all the errors related to the conversion it's possible that different implementations may have additional errors that I have not seen - if that's the case please let me know (especially if you've solved the problems) so that I can share with others. Please note that because these commands update the database directly using them could, according to Microsoft, put your environment into an un-supported state. Please make sure you understand what the commands are doing and what your support options with Microsoft are.

    Update 9/21/2007: I've fixed a couple minor bugs that pop up when converting a non-publishing site. I've also enhanced the command to take advantage of another new command I created: gl-updatev2tov3upgradeareaurlmappings (updates the url mapping of V2 bucket webs to V3 webs thereby reflecting the change of url as a result of the move so if a user tries to hit the V2 url it will redirect to the new and updated V3 url).

    Update 10/2/2007: I've enhanced the command to take advantage of another new command I created: gl-retargetcontentquerywebpart (fixes Grouped Listings web parts that remained pointed at the old list rather than the newly imported list).

    Update 10/12/2007: I've removed the retainobjectidentity parameter. If you attempt to use this parameter you will receive a syntax error. Turns out that retaining the object identity when going from a sub-site to a site collection just created a nightmare. However, because I still had to handle these web parts that were broken I decided to enhance the repair routines to manually retarget the DataFormWebPart and ContentByQueryWebPart web parts. So if a matching list can be found on the source then any of these web parts on your pages should be fixed so that you don't have to manually fix them (the gl-repairsitecollectionimportedfromsubsite command will do the same).

    Posted by Gary Lapointe at 9:11 AM

    子網站的 Quota 怎麼設... 這樣設...

    SharePoint Site Quota Web Part

    In SharePoint all user content i.e. documents, list items etc is stored in the Content Database associated with a Site Collection. One question that administrators always ask is if they can restrict the database\disk usage for a given Site Collection. This is really usefull for My Sites and the good news is that this functionality is provided OOTB using Quotas.

    The concept is that you create a Quota template by using UI elements in Central Administration, the template collection is available as a drop down list when ever you are provisioning a new Site Collection on the New Site Collection page. Adding and removing quotas can also be done any time after the site is created.

    So far so good, today my colleague Joshua (Joshua Tan) relayed the requirements of a current project where the customer wanted to keep the users informed of thier quota use and I put on my code churn cap. To restate, the problem is how to communicate quota usage to users so they know if they have space remaining on thier MySites or any other Site for that matter.

    Showing the user the amount of quota left/used should not be too hard? I asked my self and it turned out to be easier then I thought! In less then 30 minutes I had a basic working prototype. Given below are glory shots of the Site Quota web part in action. You can always add additional details like the amount of disk space available and maybe other improvements which I have listed at the end.

    sitequotawebpartsitescreenshot.png
    Site Quota web part on the site’s Home page with 250 MB Maximum Disk Space

    sitequotawebpart.png
    Site Quota web part by itself - gradient starts from green and grows to a fire engine red

    Here is the code I wrote with the usefull parts highlighed.  It is provided as-is, use at your own risk: 

    using System;
    using System.Runtime.InteropServices;
    using System.Web.UI;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Serialization;

    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using Microsoft.SharePoint.WebPartPages;

    namespace SiteQuotaWebPart
    {
        [Guid("eed45f6b-40ab-45d7-b148-41697a307a79")]
        public class SiteQuotaWebPart : System.Web.UI.WebControls.WebParts.WebPart
        {
            private int quotaUse = 0;
            private void CalculateQuotaUse()
            {
      SPSite siteCollection = SPContext.Current.Site;
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite elevatedSiteCollection = new SPSite(siteCollection.ID))
                    {
                        this.quotaUse = Convert.ToInt32(elevatedSiteCollection.Usage.Storage * 100 / elevatedSiteCollection.Quota.StorageMaximumLevel);
                    }
                });

            }
            protected override void Render(HtmlTextWriter writer)
            {
                this.CalculateQuotaUse();
                writer.Write(”<style type=’text/css’>.QG {background-image: url(’http://moss/QuotaGradient.png’);}</style>”);
                writer.Write(”<p><br><center><table style=’width:” + this.quotaUse * 2 + “px’ class=’QG’ cellspacing=’0′ cellpadding=’0′><tr><td> </td></tr></table></p>”);
                writer.Write(”<p><span class=’ms-pageinformationheader’>Quota Used: ” + this.quotaUse + “%</span></p></center>”);
            }
        }
    }

    A number of improvements come to mind and will be added in the final production release:

    • Show disk space used in megabytes
    • Show total disk space in megabytes
    • Allow web part configuration to change the formatting of HTML
    • Allow arbitury values for Sites or Site Collection where Quotas can’t be enforced or don’t exist
    • Email notification to the owner of a site (not a site collection) when site uses more then alloted quota (the email should be generated only once regardless of how many instances of the Quota Web Part Exist)
    • Package the gradient image resource in the Web Part Assembly for ease of deployment
    • The webpart can be packed as a feature.
    • Error handling…

    comes from 這個人 http://raiumair.wordpress.com/2007/07/12/site-quota-web-part/