posts - 8, comments - 71, trackbacks - 44, articles - 0

2008年4月14日

在SharePoint网站中我们有系统自带的HTTP404页面,也就是说,如果你请求的页面不存在,则该HTTP404页面就被调用了。
现在假如我们要设定自己的页面那怎么做?像这样一个简单的操作在sharepoint里也并不简单。如果在SharePoint Central Administration中可以对此进行设定是最自然不过的事情,可惜的是当前版本的SharePoint没有这个功能。我们需要告诉SharePoint使用自己的页面,现在看来有两种方法。

第一,写一个console application,在服务器上运行这个程序来告诉SharePoint用自己的页面,代码类似这样:
SPWeb site = this.Web;
System.Uri webApplicationUri = new Uri(site.Url);
SPWebApplication webApplication = SPWebApplication.Lookup(webApplicationUri);
webApplication.FileNotFoundPage = MyPageURL_404;
webApplication.Update();
这个方法因为需要在服务器上运行一段额外的代码,假如你不是服务器管理员的话比较麻烦。

第二,可以把上面的功能做成一个Feature,该Feature可以在Site Action里加一个命令来设置,这样就不需要服务器管理员的介入,只要是网站集管理员就可以了。这种做法的目的是当我们以管理员帐号登录的时候,在site collection administration里出现一个可以让我们设置HTTP404页面的地方。
感谢Tim Dobrinski的文章:http://www.thesug.org/blogs/lsuslinky/Lists/Posts/Post.aspx?List=ee6ea231%2D5770%2D4c2d%2Da99c%2Dc7c6e5fec1a7&ID=5

Feature.xml
<?xml version="1.0" encoding="utf-8" ?>
<Feature
Id="{GUID}"
Title="File Not Found Page Settings"
Description=""
Scope="Site"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
 <ElementManifest Location="elements.xml" />
</ElementManifests>
</Feature>
Elements.xml
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="FileNotFoundPage"
GroupId="SiteCollectionAdmin"
Location="Microsoft.SharePoint.SiteSettings"
Sequence="2000"
Title="File Not Found Page"
RequireSiteAdministrator="True"
Description="" >
<UrlAction Url="~site/_layouts/fp/Set404Page.aspx"/>
</CustomAction>
</Elements>
我们还需要一个aspx文件当作这个命令的载体,用来设定自定义404页面。
<%@ 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" %>
<%@ Import Namespace="Microsoft.SharePoint.Administration" %>
<script runat="server">
protected override void OnLoad(EventArgs e) {
if (Page.IsPostBack)
{
SPWeb site = this.Web;
System.Uri webApplicationUri = new Uri(site.Url);
SPWebApplication webApplication = SPWebApplication.Lookup(webApplicationUri);
webApplication.FileNotFoundPage = PageURL_404.Text;
webApplication.Update();
}
}
</script>
。。。。。。(此处略掉页面html代码)
一个要注意的问题是:我们的自定义404页面必须要放在 /12/TEMPLATE/LAYOUTS/1033目录里面,(1033是英语,如果是别的语言比如说法语则换成1036,etc.)

 

posted @ 2008-04-14 21:55 夏天的感觉 阅读(389) | 评论 (0)编辑

2008年3月7日

需要写一个自定义的sharepoint timer job, 目的是要定时到Site Collection Images这个List里检查图片的过期日期,如果即将过期的话,需要发送email到相关的人员。

 

GOOGLE了一下,发现资料很少。不过幸运的是我发现了 Andrew Connell的博客,上面有一个很详细的示例代码。http://www.andrewconnell.com/blog/articles/CreatingCustomSharePointTimerJobs.aspx

 

代码的基本思路是:我们的自定义job类需要从SPJobDefinition继承过来,并且要改写几个构造体和一个Execute方法。其中不带参数的默认构造体是必须的。 

那么在Execute方法中怎么得到这个SPWebApplication对象呢?AC用了如下的代码:

SPWebApplication webApplication = this.Parent as SPWebApplication;

Parent属性并且CASTWebApplication对象, 这段我比较困扰,因为根据SDK里的说明,本来就有一个WebApplication属性可以用,为什么还要这么麻烦呢。我试了如下代码

SPWebApplication webApplication = this.WebApplication;

测试结果发现完全等效。

 

 

好,这个类写完以后,我们还要写另外一个SPFeatureReceiver类。因为我们需要一个Feature来把我们的Timer部署到服务器上去,通过Feature Activated/deactivated来触发/关闭这个TimerJob

 

public override void FeatureActivated(SPFeatureReceiverProperties properties)

        {

 

            SPSite site = properties.Feature.Parent as SPSite;

            SPWeb web = site.RootWeb;

           

            // make sure the job isn't already registered

            foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)

            {

                if (job.Name == "ImageValidationCheckerJob")

                {

                    job.Delete();

                }

            }

            // install the job

            ImageValidationCheckerJob imageValidationCheckerJob = new ImageValidationCheckerJob("ImageValidationCheckerJob", site.WebApplication);

            SPDailySchedule schedule = new SPMonthlySchedule();

            schedule.BeginHour = 23;

            schedule.BeginMinute = 40;

            schedule.BeginSecond = 1;           

            schedule.EndHour = 23;

            schedule.EndMinute = 59;

            schedule.EndSecond = 1;

 

            imageValidationCheckerJob.Schedule = schedule;

            imageValidationCheckerJob.Update();

        }

 

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)

        {

            SPSite site = properties.Feature.Parent as SPSite;

           

            // delete the job

            foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)

            {

                if (job.Name == "ImageValidationCheckerJob")

                {

                    job.Delete();

                }

            }

        }

这个SPDailySchedule对象让我有些困扰,我现在理解是你要给定开始时间和结束时间,然后系统会随机算出一个在两者中间的时刻来启动这个工作。比如上面例子,那么每天运行时间应该是介于23401 23591 之间, 不知道是不是这样,不过我猜测应该是如此。

 

代码写完了以后,还要写一个Feature,这个Feature比较简单,就一个XML文件

Feature.xml

<?xml version="1.0" encoding="utf-8" ?>

<Feature xmlns="http://schemas.microsoft.com/sharepoint/"

         Id="1F481C17-4FDA-4919-A64A-EAE5C1301B4B"

         Title="Image Validation Checker"

         Description="If any images in the top level site colleciton images are expiring soon,email relative person."

         Scope="Site"

         Hidden="TRUE"

         Version="1.0.0.0"

         ReceiverAssembly="TimerJobControl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f2aef6a9088f714f"

ReceiverClass="TimerJobControl.ImageValidationCheckerJobInstaller">

</Feature>

 

剩下的就是把feature装好就好了,假如代码没有出错的话。

至于怎么Debug这个程序,走以下步骤:(有些步骤有时候可能不用,但是全用可以保证没有问题,这个是我花了1天的时间得出的血的教训)

(为了调试方面,把Schedule设置为SPMinuteSchedule,并且设置为每2分钟运行一次)

  1. Assembly DLL放到 GAC
  2. 命令行:iisreset
  3. Deactivate feature, 然后activate feature.
  4. 命令行:net stop SPTimerV3
  5. 命令行:net start SPTimerV3
  6. Visual Studio: Attach to process: OWSTIMER.EXE
  7. 完。

 

还有一点要注意的是,如果你想在TimerJob类里面要从web.config文件得到一些值得话,我觉得是不可能的,至少我还没有发现该怎么做,所以,假如代码需要从外部取得一些信息的话,

解决办法是 : c:/program files/common files/microsoft shared/…/12/bin目录里新建一个文件,文件名叫做:OwsTimer.exe.config,

<configuration>

  <appSettings>

   <add key="YourKey" value="YourValue" />

  </appSettings>

</configuration>

然后用ConfigurationManager.AppSettings.Get("YourKey"); 来取得这个值。

记住每次修改代码以后进行调试前,一定要走上面说的步骤,特别是步骤45很容易被忘记,不然可能会遇到很莫名其妙的问题。

最后,happy SharePointING

 

 

posted @ 2008-03-07 04:05 夏天的感觉 阅读(1305) | 评论 (3)编辑

2008年3月1日

以前在编写基于域认证的SharePoint站点时,都没有意识到代码的执行权限问题,因为我基本上都是以管理员身份来登录的。现在把网站认证改为Forms认证以后,一般的用户并不是网站的管理员,导致有些控件会遇到拒绝访问的情况。一个例子是:假如一个普通的Internet用户要往一个Document library来进行写入操作的话就会被拒绝访问。

public void WriteToLib()
{
   
byte[] file = 。。。。。; //get byte array
   SPSite site = new SPSite("url");
   SPWeb web 
= site.OpenWeb("url");
   SPFolder lib 
= web.folders["libName"];
   SPFileCollection files 
= lib.Files;
   files.Add(
"fileName", file); //access denied
}


同样的代码如果用户是网站管理员就没有这个权限问题。
那么解决方案是什么呢?我们需要提升这段代码的权限,而不管当前的用户是不是有足够的权限。从SharePoint SDK中看到可以这样做:

SPSecurity.CodeToRunElevated ElevatedWriteToLibrary = new SPSecurity.CodeToRunElevated(WriteToLib);
SPSecurity.RunWithElevatedPrivileges(ElevatedWriteToLibrary);


这样就把我们的方法的权限提高到了系统帐号的高度,问题就解决了。
如果用到SPSite对象的话,则一定要在这个方法内部来创建,不可以用SPContext.Current.Site,不然没有效果.

posted @ 2008-03-01 01:13 夏天的感觉 阅读(385) | 评论 (2)编辑

2008年2月27日

     摘要: 在开发Web Control的时候,经常需要在源代码中嵌入一些HTML代码, 比如这样(代码片段):protectedoverridevoidRenderContents(HtmlTextWriteroutput){SPListmyList=GetListByName(List);if(myList!=null){uintcurrentLanguageCode=getCurrentLanguage... 阅读全文

posted @ 2008-02-27 02:15 夏天的感觉 阅读(2501) | 评论 (15)编辑

2008年2月15日

用VS开发ASP.NET页面的时候,一切都非常的傻瓜式。在工具栏拖几个控件,放一个BUTTON,双击一下BUTTON则VS自动帮我们打开了一个CODE-BEHIND的CLASS,我们似乎什么都可以不要管,写几行简单的代码按一下F5,就好了。被VS养的如果离开了它还真不会写代码了。话说回来Visual Studio真是微软出品的最好的开发产品了。

那么现在这一切搬到SharePoint里去,我们可以不可以使用这个code-behind的技巧呢?答案是肯定的,只不过VS不能像在ASP.NET里那样自动帮我们做所有的事情了,我们必须亲自动手来加一些简单的代码。

大家知道,传统ASP.NET页面是基于文件系统的,而SharePoint页面是基于数据库的(Customized之后),并且任意一个SharePoint aspx页面都是MasterPage和LayoutPage互相结合产生的。一个通常的SharePoint网站可能会有1个MasterPage以及N多的LayoutPage,我们要处理的FORM便几乎一定是存在于某个LayoutPage中的。那么在这种情况下,我们的code behined Class必须要从 Microsoft.SharePoint.Publishing.PublishingLayoutPage继承过来。然后根据FORM中的控件ID来声明相应的变量。比如在FORM HTML中是:

<form id="form1" runat="server">
    <div>
        <asp:Label ID="LabelName" runat="server" Text="Name"></asp:Label>
        <asp:TextBox ID="TextBoxName" runat="server"></asp:TextBox>
        <asp:Button ID="Submit" runat="server" Text="Submit" />
        <asp:Label ID="LabelDisplay" runat="server"></asp:Label>
    </div>
</form>

那么我们的CODE Class就该像这样:(代码片段)

namespace namespace1
{
    public class FormHandler : Microsoft.SharePoint.Publishing.PublishingLayoutPage
    {
        protected TextBox TextBoxName;
        protected Label LabelDisplay;
        protected Button Submit;
        protected override void OnInit(EventArgs e)
        {
            Submit.Click += new EventHandler(Submit_Click);
        }
        protected void Submit_Click(object sender, EventArgs e)
        {
            LabelDisplay.Text = "You entered: " + TextBoxName.Text;
        }
    }
}

注意在声明FORM控件时,不可以省略protected关键字不然程序会出错因为找不到FORM中的相应控件。

将它编译成DLL,并发布到BIN(如果是发布到GAC则需要强命名)。为了让LayoutPage认识这个DLL,我们要在LayoutPage最上面重写 Page Directive:(比如这样)

<%@ Page meta:progid="SharePoint.WebPartPages.Document" Language="C#"  Inherits="namespace1.FormHandler,  FormHandler, Version=1.0.0.0,  Culture=neutral, PublicKeyToken=null" %>

其中Inherits=<NamespaceName.Classname+FullAssemblyName>,需要从该DLL中取得相应的信息,譬如用Reflector来打开该DLL文件就是一个非常好的办法。注意看这里包含5个部分,后4部分可以用Reflector轻松得到,第一部分用这个格式:NamespaceName.ClassName

好,现在当用户单击Submit按钮的时候,我们的代码就会被调用了,至此完成了在SharePoint中Code-Behind的实现。

posted @ 2008-02-15 08:51 夏天的感觉 阅读(2345) | 评论 (12)编辑

2008年2月6日

当开发在MOSS多语言站点中使用的控件时,我们需要为各个不同的语言分别建立resource文件,比如这个站点支持英文跟中文, 那么我们建立2个resource文件:

myproject.en-US.resx (内容: greeting="你好")
myproject.zh-CN.resx (内容: greeting="hello")

命名规则是:<项目名>.<CultureName>.resx

CulturName有两部分组成: <language2>-<Country/region2>,具体可以参考msdn中
CultureInfo Class at:
http://msdn2.microsoft.com/en-us/library/system.globalization.cultureinfo.aspx

这两个resx文件我们要放到MOSS站点的目录里:C:\Inetpub\wwwroot\wss\VirtualDirectories\<YOUR_PORT>\App_GlobalResources

现在,我们可以用代码来访问这两个resource文件了。

ResourceManager rm = new ResourceManager("resources.myproject", Assembly.Load("App_GlobalResources"));
CultureInfo ci = Thread.CurrentThread.CurrentCulture;
string greeting = rm.GetString("greeting", ci);

根据当前的实际culture,来读取相应的值。如果当前culture是中文那么返回“你好”,如果当前culture是英文则返回“hello”。
这种方法的前提是我们的resource文件保存在App_GlobalResources目录中。好处是以后随时可以改resource文件而不需要重新编译代码。

PS:我们也可以把resource文件打包成Satelite Assembly DLL,并以如下方式访问:
ResourceManager rm = new ResourceManager("<DLL_PROJECT_NAMESPACE>.myproject", Assembly.Load("<DLL_ASSEMBLY_NAME>"));
显然这种方式如果改变resource文件,则必须重新编译该DLL。

posted @ 2008-02-06 21:03 夏天的感觉 阅读(325) | 评论 (1)编辑

2008年2月5日

SharePoint Designer, 以前叫做FrontPage的时候,就是一个做网站的客户端工具,用来编辑编辑HTML。现在名字改了,功能也加强了,是SharePoint项目开发中一个有益的补充,但是据我所知,很多专业SharePoint开发人员根本就不用这个工具来做任何事情,为什么? 主要的原因是当你修改某一个页面的时候,SharePoint Designer会自作聪明的帮你加一些料进去而不告诉你,有时候不注意会造成很大的困扰。还有一个原因就是,只要通过SharePoint Designer修改过任何页面,当你按下菜单栏里的保存的时候,他会在数据库里保存一个拷贝,同时脱离跟文件系统中源文件的任何关系。这个过程叫做Customized,也叫Unghosting. 假如你要跨站点共享某些文件,比如master page或者page layout,那么就不能用SharePoint Designer来做这件事情,这个时候,我们一般直接用Visual Studio来编辑这些页面。但是用VS来编辑master page的时候,我们只有代码界面,而用SharePoint Designer,我们有可视化的支持,可以在代码和设计之间随意切换,对初学者比较有用。所以,用不用它,取决于你的实际需求。如果Unghosting是可以接受的,那么它确实是一个不错的选择。msdn上说, SharePoint Designer是当前唯一一个可以用所见即所得的方式来编辑master page的工具。它还有一个用处是可以打开SharePoint站点方便我们浏览里面的内容,我不知道还有什么工具可以做到这样,因为SharePoint几乎所有的东西都存放在 SQLSERVER里面,平常都是看不见摸不着。当然你可以用浏览器直接打开站点,但是他们提供了不一样的视图。SharePoint Designer视图类似于Windows explorer,左边是树形结构,右边显示内容。比如我就用它来上传大量的图片到SharePoint中去,只要像操作普通windows文件夹那样复制粘贴就可以了。

最后说一下customized(unghosting)和uncustomized(ghosting). 下面一个简单对比:

customized uncustomized
保存在数据库 保存在文件系统
读取相对慢 读取相对快
限于一个站点 可以跨站点共享

至于用来开发workflow?我没有用过。毛主席说没有调查没有发言权,所以对于这一点我就没办法说了。

posted @ 2008-02-05 08:45 夏天的感觉 阅读(2276) | 评论 (13)编辑

2008年1月22日

当我们在Visual Studio开发好了定制控件以后,如何在SharePoint Master Paeg里面使用这个控件呢?方法如下:
1. 首先定位到SharePoint网站目录:(如下图)
1.JPG
2. 将我们在Visual Studio里面创建的控件DLL拷贝到上面这个bin目录里面。
3. 打开目录32349目录下的Web.config文件(在我的机器上是这个端口号,请参考自己网站的实际端口), 找到<SafeControls>节点,将自己的DLL信息加进去,这样就把我们的控件设置为被信任的安全控件了。

<SafeControl Assembly="FPControls" Namespace="FPControls" TypeName="*" Safe="True" />


4. 加入如下entry:

<system.web>     

      <controls>

         <add tagPrefix="fp" assembly="FPControls" namespace="FPControls"/>

      </controls>

</system.web>

5. 重启IIS。现在可以在master page里面使用我们的控件了。
打开master page, 将下面红色部分加进去:
..........
<body class="body" onload="javascript:_spBodyOnLoadWrapper();">
 <WebPartPages:SPWebPartManager runat="server"/>
 <form runat="server" onsubmit="return _spFormOnSubmitWrapper();">
 <div>
 <fp:NavControl runat="server" />
 </div>

 <table cellpadding="0" cellspacing="0" class="master">
  <tr>
   <td height="100%" class="shadowLeft">
    <div class="spacer">
    </div>
   </td>
.........
6. 完。

posted @ 2008-01-22 20:53 夏天的感觉 阅读(436) | 评论 (4)编辑