QuickLinkManager 在 MySite 中算是像書籤一樣的功能,我們可以利用它來實作一個像是 My Clip Board 的內嵌功能放在所有的 SPS Site 中,然後可以將自己喜歡的文件加入書籤。只不過 SPS2003 的管制很嚴謹 (算是件好事),在 Portal Level 跟 Web Level 又有著分別,所以要實作這個功能的時候,還是得用些小技巧,以免又落入虛擬的邏輯惡夢中。

[Name Space usage]

using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Portal.Security;
using System.Security.Principal;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Portal;
using Microsoft.SharePoint.Portal.Topology;
using Microsoft.SharePoint.Portal.UserProfiles ;

要列出目前在 Quick Link Manager 中所有的連結,這部分沒有甚麼技巧,參考一下 SDK 中的範例,就可以輕鬆的寫出來了。這邊我們是先得到目前的 Server Name,然後再用 UserProfile 的方式去取回該 User 的 QLM。

[List Links]

string sServerURL = “”;
if(this.Page.Request.ServerVariables[“HTTPS”].ToString()==”off”)
sServerURL += “http://”;
else
sServerURL += “https://”;
sServerURL = sServerURL + this.Page.Request.ServerVariables[“SERVER_NAME”].ToString();

TopologyManager tm = new TopologyManager();
PortalSite ps = tm.PortalSites[new Uri(sServerURL)];
PortalContext pc = PortalApplication.GetContext(ps);

string _sUserID = Context.User.Identity.Name;
UserProfileManager upm = new UserProfileManager(pc);
UserProfile u = upm.GetUserProfile(_sUserID);
QuickLinkManager qlm = u.QuickLinks;

foreach(QuickLink myQL in qlm)
{
Console.WriteLine(myQL.LinkID.ToString() + “:” + myQL.Title + ” (” + myQL.URL + “)”);
}

至於新增及刪除 QuickLinkManager 中的資料,先是寫出了 Console Application 作為測試,可以正常的使用,但是當實作為 WebPart 的時候,發生無法使用的情況,本來以為又是權限的問題,搞了很久還是解不出原因,Call Microsoft Support,給回來的 WebPart Sample Code 在加入我自己的 Lab Site 的時候還是沒有辦法使用。

原來~~~~ 微軟的支援人員使用 Portal (SPSite) 來作為 WebPart 的測試環境,而我的應用環境是在 Web Site 端的,所以我的 Lab Site 是建成一個 SPWeb。QuickLinkManager 的新增與刪除功能,經過測試是沒有辦法在 Web 層級的 WebPart 使用 (List 是可以的),難怪微軟那邊測試沒問題,拿到我手上就怎麼也跑不起來了。

PS. 此問題來來回回解了兩天,因此,在你看過這篇文章後,你的 IT 生命力又延長了兩天。

[利用重導技巧]

等等,這裡還產生了一個疑問,因為在 SPS2003 上未客製化過的 Web Site,微軟有使用到 QuickLinkManager 的功能,那麼,它到底是怎麼運作的呢? 仔細研究了一番,發現也許是使用重導的方式來達成的。剛剛我們說到,可以在 Portal 使用這個功能,那麼我們就讓這個功能的 WebPart 是在 Portal 端就行了。以下是邏輯劇本:

[1] 使用者在列表(Web Site) 上點選新增/刪除
[2] 新增/刪除 的連結實際指到 Portal 的 aspx 程式,裡頭並包含了 QuickLinkManager 具增刪的 WebPart,使用 URI 進行資料傳遞
[3] URI 中包含返回之連結 (Web Site),進行增刪動作後重導回 返回頁面

Note: Portal 的 aspx 部分不應該包含 Web UI (未實作,原因如下說明)

如此一來,不但可以進行增刪的動作,使用者也不會有明顯的感覺畫面跳了兩頁。

我們使用 FrontPage 2003 來連到 Portal,在 Portal Level 的目錄中新增一個空白的 aspx 頁面,加入 Web Zone (用於放置 WebPart),在這個 aspx 中加入一行:

<span class=’ms-SPLink’><WebPartPages:SettingsLink runat=”server”/></span>

這一行可以讓你使用 IE 瀏覽此頁時出現管理介面的 UI,用此管理介面 UI 加入寫好的 WebPart。

[LinkMgr WebPart Source Code]

為了要可以測試,所以這個 WebPart 實際上是有 UI 的,否則很造成一個問題,那就是加入了這個 WebPart 到頁面上之���,直接重導掉了,連維護都不行 (到此連結會直接重導,而跑到別的頁面)。

Button btGetLink = new Button();
Button btAddLink = new Button();
Button btDelTopOne = new Button();
HtmlInputText txTitle = new HtmlInputText();
HtmlInputText txID = new HtmlInputText();
HtmlInputText txURL = new HtmlInputText();
HtmlInputText txSource = new HtmlInputText();
Label txResult = new Label();
HtmlInputText txStatus = new HtmlInputText();

UserProfileManager upm = new UserProfileManager();
DataGrid dgList = new DataGrid();

protected override void RenderWebPart(HtmlTextWriter output)
{
this.EnsureChildControls();
RenderChildren(output);
}

protected override void CreateChildControls()
{
btGetLink.Text = “Get My Links”;
btGetLink.Attributes.Add (“ID”, “btnLst”);
btGetLink.Click += new System.EventHandler(this.btGetList_OnClick);
btGetLink.Visible = true;

dgList.Visible = true;

btAddLink.Text = “Add Link”;
btAddLink.Attributes.Add (“ID”, “btnAdd”);
btAddLink.Click += new System.EventHandler(this.btAddLink_OnClick);
btAddLink.Visible = true;

btDelTopOne.Text = “Delete Top One”;
btDelTopOne.Attributes.Add (“ID”, “btnDel”);
btDelTopOne.Click += new System.EventHandler(this.btDelTopOne_OnClick);
btDelTopOne.Visible = true;

Controls.Add(btGetLink);
Controls.Add(btAddLink);
Controls.Add(btDelTopOne);
Controls.Add(dgList);

string myAction = “”;
string myID = “”;
string myTitle = “”;
string myURL = “”;
string mySource = “”;

NameValueCollection coll = this.Page.Request.QueryString;

string[] sID = coll.GetValues(“ID”);
string[] sACT = coll.GetValues(“ACT”);
string[] sTitle = coll.GetValues(“TITLE”);
string[] sUrl = coll.GetValues(“URL”);
string[] sSource = coll.GetValues(“SOURCE”);

myAction = sACT == null ? “” : sACT[0];
myID = sID == null ? “” : sID[0];
myTitle = sTitle == null ? “” : sTitle[0];
myURL = sUrl == null ? “” : sUrl[0];
mySource = sSource == null ? “” : sSource[0];

txTitle.Value = myTitle;
txURL.Value = myURL;
txSource.Value = mySource;
txID.Value = myID;

txStatus.Attributes.Add (“ID”, “txStatus”);
if (this.Page.IsPostBack)
{
txStatus.Value = “Done”;
}
else
{
txStatus.Value = “”;
}

switch (myAction)
{
case “ADD”:
txResult.Text = “\n <script language=javascript> if(document.all.item(‘txStatus’).value==””) { document.all.item(‘btnAdd’).click(); } </script> \n”;
break;
case “DEL”:
txResult.Text = “\n <script language=javascript> if(document.all.item(‘txStatus’).value==””) { document.all.item(‘btnDel’).click(); } </script> \n”;
break;
default:
txResult.Text = “\n <script language=javascript> if(document.all.item(‘txStatus’).value==””) { document.all.item(‘btnLst’).click(); } </script> \n”;
break;
}

if ( txStatus.Value == “Done” )
{
txResult.Text = “\n <script language=javascript> window.location = “” + mySource + “”; </script> \n”;
}

Controls.Add (btGetLink);
Controls.Add (btAddLink);
Controls.Add (btDelTopOne);
Controls.Add (dgList);
Controls.Add (txTitle);
Controls.Add (txID);
Controls.Add (txURL);
Controls.Add (txSource);
Controls.Add (txStatus);
Controls.Add (txResult);
base.CreateChildControls();
}

protected override void OnPreRender(EventArgs e)
{
base.OnPreRender (e);
}

private void btAddLink_OnClick(object sender, System.EventArgs e)
{
txStatus.Value = “Done”;
try
{
UserProfile u = upm.GetUserProfile(true);
QuickLinkManager qlm = u.QuickLinks;
string myTitle = txTitle.Value;
string myURL = txURL.Value;
string myDate = System.DateTime.Today.ToShortDateString();
qlm.Add(myTitle, myURL, myDate, true);
}
catch(Exception ex)
{
Context.Response.Write(ex.Message);
}
}

private void btGetList_OnClick(object sender, System.EventArgs e)
{
txStatus.Value = “Done”;
try
{
UserProfile u = upm.GetUserProfile(true);
QuickLinkManager qlm = u.QuickLinks;
dgList.DataSource = qlm.GetDataSet(false);
dgList.DataBind();
}
catch(Exception ex)
{
Context.Response.Write(ex.Message);
}
}

private void btDelTopOne_OnClick(object sender, System.EventArgs e)
{
txStatus.Value = “Done”;
try
{
UserProfile u = upm.GetUserProfile(true);
QuickLinkManager qlm = u.QuickLinks;
DataSet ds = qlm.GetDataSet(false);
qlm.Delete(Convert.ToInt32(txID.Value));
}
catch(Exception ex)
{
Context.Response.Write(ex.Message);
}
}

[End]

最後修改日期: 2004-08-27