在用 Deep Zoom Composer 生成内容后,传到 SharePoint Server 2007 中后,发现不能浏览,仔细检查发现 Deep Zoom Composer 生成的很多以 _files 的目录,传到 SharePoint Server 2007 中后,目录被改了名字,自动在最后加了下划线 “_”,导致不能正常访问。
此处详细介绍了此问题。
Enjoy coding | Enjoy life
在用 Deep Zoom Composer 生成内容后,传到 SharePoint Server 2007 中后,发现不能浏览,仔细检查发现 Deep Zoom Composer 生成的很多以 _files 的目录,传到 SharePoint Server 2007 中后,目录被改了名字,自动在最后加了下划线 “_”,导致不能正常访问。
此处详细介绍了此问题。
在 ASP.NET 2.0 中,GridView 支持修改/删除记录,但却不支持新增记录的功能(个人感觉是 GridVew 的一大缺憾,估计在下一版本中会加入此功能),大多数人建议用 FormView 来完成增加记录的功能,但是 FormView 和 GridView 不是同一个表格,所以无法在同一个页面的同一个表格中显示。如果故意将 FormView 或自己的一堆于用新增功能的控件使用普通的表格组装起来,那么会碰到一个很麻烦的问题,即两个表格的列宽如何协调一致,大多数情况下,大家在做表格的时候,表格中各列的宽度都是自动调整的,所以强行指定宽度在很多情况下并不适用。
通过实践,想出了一种办法,主要步骤如下所示:
1) 在 GridView 的 EmptyDataView 中,放置一个普通的Html Table,以便在GridView绑定的数据源中无数据时依旧显示表头(如果数据源为返回的数据行数为0,GridView默认是不显示表头的),假设 ID 为 tbHeader,它的作用是下面用于新增功能的 tbForm 的各列控件提供说明(充当表头);
2)在 GridView 下面,放置一个普通的 HTML 表格,其列数和 GridView 中定义的列数保持一致,但行数只有一行,然后在此表格的各列中放入用于新增功能的各个控件(如 TextBox等),假设此表格的 ID 为 tbForm
3)在页面中加入一段客户端脚本,以便使页面展示到客户端时,利用 Javascript 将两个表格强行合并到一起,这样就可以将只有一行的 tbForm 合并到 GridView中,因此 GridView 的最下面多出一行,其中有 tbForm 表格中定义的输入控件和“添加”链接(按钮),主要代码(JavaScript)如下:
function MergeTable(source,dest)
{
var row;
var cell;
var sourceTb = document.all(source);
var destTb = document.all(dest);
for (var i=0; i<sourceTb.rows.length; i++)
{
row = document.createElement(“TR”);
for (var j=0; j<sourcetb.ROWS(I).CELLS.LENGTH; j++)
<sourceTb.rows(i).cells.length; j++)
{
cell = document.createElement(“TD”);
row.appendChild(cell);
//复制对象
for(k=0;k<sourceTb.rows(i).cells(j).all.length;k++)
cell.appendChild(sourceTb.rows(i).cells(j).all.item(k));
}
destTb.tBodies(0).appendChild(row);
}
for (var i=sourceTb.rows.length-1; i>=0; i–)
{
sourceTb.deleteRow(i)
}
}
function ChangeTableLayout()
{
if(document.all(‘tbHeader’) == null)
MergeTable(‘tbForm’,”);
else
MergeTable(‘tbForm’,’tbHeader’);
}ChangeTableLayout();
如果 GridView 绑定时没有数据,将不显示其中定义的各列,而只显示 EmptyDataView 中的 tbHeader,这时要合并 tbHeader 和 tbForm。如果 GridView 绑定时包含数据,则不会显示 EmptyDataView(当然也不会显示其中的 tbHeader),但这时会显示 GridView 中定义的各个列,因此只需将 GridView 本身和 tbForm 合并即可。 GridView 的客户端ID可以用GridView.ClientID来获取。
在服务器端很容易知道 GridView 绑定后是否包含数据,但对于客户端来说,不容易检查,一个简单的作法就是检查页面中有没有 tbHeader 对象(如果有,则说明表格没有数据,如果无此对象,表示 GridView 中包含数据… 好啰嗦)
4)如果是 AJAX 环境,上述脚本有可能不被执行,可以调用 Sys.Application.load.add ( JavaScriptFunction) 来强制执行脚本,来合并表格,主要代码如下(C#):
ScriptManager myScriptManager = ScriptManager.GetCurrent(Page);
if (myScriptManager.IsInPartialRenderingMode)
{
Page.ClientScript.RegisterStartupScript(this.GetType(), “ShowFullTable1”, “ChangeTableLayout();\n”, true);
}
else
{
Page.ClientScript.RegisterStartupScript(this.GetType(), “ShowFullTable2”, “Sys.Application.load.add (ChangeTableLayout);\n”, true);
}
注:上述代码中的 ChangeTableLayout 为客户端脚本函数的名称,其中调用第3步骤中的代码,上述代码在 Atlas 中通过,在 ASP.NET AJAX Beta 上尚末测试。
另:为了更能说明上文代码的效果,我抓了一个截图,图中包括表头在内的前三行就是 GridView,最下面一行其实来自于另一个表格,在客户端强制合并后,显示效果就是这样,看起来象是一个表格。
感谢大家的意见,我对原文中的表述不明确的地方进行了更改。
我们在编写基于 ASP.NET 的应用程序时,如果代码执行出错或检测到异常,一般会提示用户“返回”或“回退”,或者在多步操作、列表/详细的查看界面中,也会给用户提供回退到上一页面的链接,对于这种情况,大家很快就会想到的简单做法就是利用 Javascript 来实现,即 history.go(-1) ,但是由于 ASP.NET 页面的 PostBack 机制,所以 history.go(-1) 可能还是当前页面,而不能真正回退到上一页面。
在 Classifieds Site Starter Kit 中,学习到一种不错的关于回退的处理方法,可以分别在客户端和服务器控件中实现页面的回退,代码如下:
1)首先在页面中增加两个属性
//记录上一个页面的信息
private string UrlReferrer
...{
get
...{
return ViewState["UrlReferrer"] as string;
}
set
...{
ViewState["UrlReferrer"] = value;
}
![]()
}
![]()
//记录 PostBack 的次数
public int NumPostBacks
...{
get
...{
if (ViewState["NumPostBacks"] != null)
return (int)ViewState["NumPostBacks"];
else
...{
ViewState["NumPostBacks"] = 0;
return 0;
}
}
set
...{
ViewState["NumPostBacks"] = value;
}
}
2)在 Page_Load 事件记录上一页面地址、更新 Postback 次数、设置回退链接的地址
// 记录上一页面的信息或更新 PostBack 的次数
protected void Page_Load(object sender, EventArgs e)
...{
![]()
if (!Page.IsPostBack)
...{
if (Request.UrlReferrer != null)
this.UrlReferrer = Request.UrlReferrer.ToString();
}
else
NumPostBacks++;
![]()
int goBackSteps = NumPostBacks + 1;
BackLink.NavigateUrl = String.Format("javascript:history.go(-{0});", goBackSteps);
}
3)直接在代码中处理回退操作(如 Back_Click),可以直接调用如下方法
//在代码中回退
protected void ReturnToPreviousPage()
...{
string referrer = UrlReferrer;
if (referrer != null)
Response.Redirect(referrer);
else
Response.Redirect("~/default.aspx", true);
}
近期帮别人在 ASP.NET 中实现简捷的安全/权限控制,于是进行了相关研究与内容搜集,并整理成代码,已在实际的项目中运用,效果突出,比 .NET 中的代码访问安全性(CAS)和 ASP.NET 中的相关安全控制机制更为灵活。
于是把其中实现的内容和主要代码共享出来,但内容较多,放在在随笔里会干扰大家查看首页的视线,所以归到文章里,感兴趣者可以在这里(http://blog.joycode.com/moslem/articles/85194.aspx)查看。
本文及其中的代码主要介绍了以下内容:
在 ASP.NET 2.0 中,大多数控件都可以直接“自动套用格式”,以便设置一些内置的配色方案,能够方便地设计多姿多彩、色彩丰富的页面,除些之外,ASP.NET 2.0 还提供了网站和页面的主题(Theme)和Skin的功能,可以在网页或网站层次设定各种控件的显示风格,以便统一站点的外观。
在实际开发中,实际上大家可能不会过多地采用内置的“自动套用格式”的功能,而是自己来设定控件的色彩、字体等,当然也有可能是通过 Theme/Skin 来进行的,但是,以我的实践来看,实现机制是很好,但结果往往另人不满意,主要原因是缺乏色彩方面的感觉,往往会设计出来色彩冲突、不协调,甚至在很多情况下相当不好看的界面 …
即然有这么好的实现机制,肯定会有人实现/共享很好的 Theme/Skin ,Google 了一下,找到一组 Theme,看起来还是相当不错的,可以直接应用的项目开发中,当然了,这组 Theme 也有一些小瑕疵,即某些对比色的过于接近而比较模糊。
Theme预览:http://www.dotnettreats.com/SampleThemes/Default.aspx
Theme下载:http://www.dotnettreats.com/tools/Default.aspx
首先看看 CodeProject 上的两个东西
1、The Freeze Pane DataGrid (http://www.codeproject.com/aspnet/FreezePaneDatagrid.asp)
利用文章中提到做法及代码,可以实现在 ASP.NET 1.1 上的、支持横向滚动与纵向滚动的表格,基本上是使用 CSS 实现的,比较简单。
在 ASP.NET 2.0 上,由于文档 HTML DOCKTYPE 发生了变化(HTML->XHTML),所以在使用原文中的横向滚动条会出现问题,但是使用纵向滚动条和锁定表头没有问题。
这种做法没有考虑页面 PostBack 时记录表格的滚动位置,使得用户不得不重新去寻找刚才选中/编辑的那条记录,这比较的不人性化。
2、ScrollingGrid: A cross-browser freeze-header two-way scrolling DataGrid(http://www.codeproject.com/aspnet/ScrollingGrid.asp)
此文章利用 Panel 控件和 DataGrid 控件实现了 ASP.NET 1.1 下的完整的、可实现双向滚动、表头锁定的表格,而且它实现了可以记录表格的滚动位置,页面 PostBack 后,表格仍能自动滚动到原有位置。这个控件的一个最大优点是能够适应多种浏览器,如 Internet Explorer 、FireFox 等。
在 ASP.NET 平台上,由于 DataGrid 控件已经升级为 GridView ,所以此控件已不能使用,按照文章下面的讨论,作者声称会尽快升级控件,但似乎在实现时碰到一些麻烦(如何确实表头各列的宽度),目前还没有结果。
目前我的做法:
示例代码:
<h1>滚动条表格演示h1>
<style type="text/css">......
th {...}{...}{...}{
border-right: 1px solid silver;
position:relative;
top: expression(this.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.scrollTop-2); /**//**//**//*IE5+ only*/
}
style>
![]()
<atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="True">
atlas:ScriptManager>
<br />
<asp:Panel ID="GridPanel" runat="server" Height="250px" ScrollBars="Auto" Width="562px">
<atlas:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="ObjectDataSource1" SkinID="GridView" Width="434px">
</asp:GridView>
</ContentTemplate>
</atlas:UpdatePanel>
</asp:Panel>
这样能基本上实现一个能够锁定表头、竖向滚动、能够在页面PostBack时保持滚动位置的表格,能够满足大部分应用需要。
一、授权管理概述
一般在应用程序中,比较理想、完整的授权模式就是基于角色的授权(Role Based Access Control-RBAC),它的主要目标是实现了用户与安全操作的分离,而在中间加入了角色的隔离层,从而实现了灵活性和可扩展性,具体来说,主要是这种方式:
这样通过“三个实体,两个对应”,就能找到用户和功能的关系(即用户能否执行此功能)。
二、ASP.NET 2.0 中的授权管理功能
ASP.NET 2.0中,提供了三种授权管理的提供者,分别为:AspNetSqlRoleProvider、AuthorizationStoreRoleProvider、AspNetWindowsTokenRoleProvider。
默认 AspNetSqlRoleProvider 只能提供角色与用户的实体及其对应关系,缺乏功能层次的定义和关系,所以仅能适用于有限场合,如基于 URL 的授权,或者在程序中手动使用 Roles API(Roles.IsUserInRole)来判断当前用户是否具有授权,但是这种方式的前提是角色是 Hard Code(硬编码的),以后如果角色所允许的功能发生了变化,只能通过修改程序来实现。
再来看看 Windows 2003 中附带的授权管理器(Authorization Manager,以下简称 AzMan),不可否认,它的设计比较完整地体现了 RBAC的思想,即首先定义操作,然后把操作归类形成任务(大任务也可以包括子任务),可以定义角色,然后在角色中定义其可以执行的任务或操作(大角色也可以包含小角色),最后可以应用程序中分配用户、建立角色与用户的对应关系,同时,AzMan 还提供业务规则检查的功能,即可以在任务级别定义脚本,对是否授权进行进一步的细粒度的检查,表面上看,还是比较圆满的。
但是 AzMan 的一个重大限制就是只支持 Windows 用户,它要求用户的标识为 SID(如S-1-5-21-XXXX-XXX)的格式。虽然我们可以使用 AzMan 的 API ,如 InitializeClientContextFromStringSid 来构造一个虚拟的 SID (例如,在我们的应用程序中,我们用户的 ID 为一个自增量的整数 Y,那就可以虚拟成 S-1-5-21-Y的方式)这样我们就可以利用 AzMan API 来绕开只允许Windows 用户的限制,但这些工作全部要手工完成,纯体力活,得不偿失。
本来以为 ASP.NET 2.0 的AuthorizationStoreRoleProvider 做为默认的三种 RoleProvider 之一,能为我们提供这些方面的便利功能,但在
使用之后才发现,限制依然存在,实用性仍然不够:
如果能够把 AspNetSqlMembershipProvider 中用户的 MemberId (GUID类型,格式为“XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”)转换成 SID (S-R-I-S-S)还有可能通过 AzMan API 来利用AzMan ,但这不是通过 AuthorizationStoreRoleProvider 来进行的,而且目前我也没有找到好的、能够双向转换的方法。
三、一种实用的授权管理机制设想
目前临时想到一种即能完整实现 RBAC,又能利用 RoleProvider 的解决办法是:
非常喜欢Visaul Studio 2005 中的代码段,大大节省了一些无聊的、没有技术含量的代码编写工作,如实体类的属性设置等。
Visual Studio 2005 中的 VB.NET 的代码段比 Visual C# 的多出不少,是不是吸引开发人员?
我们可以修改系统中现有的 snippet 文件(C:\Program Files\Microsoft Visual Studio 8\VC#\Snippets\2052\Visual C#目录),来增加自己常用的 snippet。
网上(http://www.gotcodesnippets.net/)也有不少别人编写的 snippet ,可以按需取用。
要想共享或发布 snippet,最好做成Visual Studio 支持的格式 vsi ,其实是就是一个 更改了后缀名的 zip 文件,里面包含一个 XML 描述文件、一个或多个 snippet 文件(可以从上面网站中获得一个 vsi 文件来参考如何制作过程)。
在 Visual Studio 2005 中,插入代码段的快捷键是 Ctrl K + Ctrl X ,代码段管理器的快捷窗口是 Ctrl K + Ctrl B。
这里提供一个比较好用的 Public Property 的 Snippet ,是对 http://www.gotcodesnippets.net 上拿到 Public Property 稍加修改之后形成的东西,这个我最常用 🙂 。
<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Public Property</Title>
<Shortcut>pp</Shortcut>
<Description>公共属性</Description>
<Author>LiYanZhi ([email protected]_</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>accessor</ID>
<ToolTip>The access modifier</ToolTip>
<Default>public</Default>
</Literal>
<Literal>
<ID>type</ID>
<ToolTip>属性类型</ToolTip>
<Default>string</Default>
</Literal>
<Literal>
<ID>name</ID>
<ToolTip>属性名称</ToolTip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ _$name$;
![]()
$accessor$ $type$ $name$
{
get { return _$name$; }
set { _$name$ = value; }
}$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
在修改 Snippet 时,也发现其中的一些不足,比如表达式中函数支持太少,例如上例中,一般用 _name 表示内部字段,用 Name 表示属性,但缺乏第一个字母大小写转换的函数。
function act1(no)
{
if(document.all.item(“y”+no).style.display==””) document.all.item(“y”+no).style.display=”none”;
else document.all.item(“y”+no).style.display=””;
}
1、在 Windows 或 AD 中批量增加用户
使用方法,新建一 users.csv,字段格式必须按如下顺序,且不能为空: 登录名, 姓名, 密码, 描述;然后将脚本与 CSV 文件存储至同一目录,以管理员/域管理员运行脚本即可。
代码>> (单击显示/隐藏)
'// Name: BulkAddUser
'// Purpose: 从 CSV 文件中批量生成 Active Directory 用户
![]()
'// 注意:
'// 1. 源文件必须使用逗号分割
'// 2. 字段必须按如下顺序,且不能为空: 登录名, 姓名, 密码, 描述
'// 3. 只能由域管理员才能运行此程序
![]()
Option Explicit
![]()
Dim oFSO, oTF, objRootDSE, objContainer, i
Dim aLine, sLine, sLogon, sPass
Dim sLoginName, sDisplayName, sDescription
![]()
![]()
'// 用户批量文件
Const InpFile = "users.csv"
Const ForReading = 1
![]()
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oTF = oFSO.OpenTextFile(InpFile, ForReading, True)
![]()
Set objRootDSE = GetObject("LDAP://rootDSE")
Set objContainer = GetObject("LDAP://cn=Users," & objRootDSE.Get("defaultNamingContext"))
![]()
i = 1
Do While oTF.AtEndOfStream <> True
sLine = oTF.ReadLine
aLine = Split(sLine, ",", -1, 1)
sLoginName = aLine(0)
sDisplayName = aLine(1)
sPass = aLine(2)
sDescription = aLine(3)
![]()
If IsEmpty(sLoginName) Or IsEmpty(sDisplayName) Then
MsgBox "第 " & i & " 行数据有误:登录名和姓名不能为空", vbExclamation, "Add Bulk Users"
End If
![]()
Set objLeaf = objContainer.Create("User", "cn=" & sLoginName)
objLeaf.Put "sAMAccountName", LCase(sLoginName)
objLeaf.Put "givenName", Left(sDisplayName, 1)
objLeaf.Put "sn", Mid(sDisplayName, 2)
objLeaf.Put "UserPrincipalName", LCase(sLogon)
objLeaf.Put "DisplayName", sDisplayName
objLeaf.Put "description", sDescription
![]()
objLeaf.SetInfo
![]()
'// 是否重复
If Err.Number = -2147019886 Then
MsgBox "User logon for " & sLogon & " already exists.", vbExclamation, "Bulk Add Users"
Else
'// Set initial password
objLeaf.setpassword sPass
objLeaf.AccountDisabled = False
objLeaf.SetInfo
End If
i = i + 1
Loop
![]()
MsgBox "用户创建完毕.", vbInformation, "Bulk Add Users"
Set oTF = Nothing
Set oFSO = Nothing
2、清理 IIS Log 中的非重要信息(如 XML、JS、JPG、GIF等资源的访问日志),只留下 asp 和 aspx 的访问日志。经我在一台服务器上测试,日志文件由 6G 变为 900M ,大大节省了空间。
使用方法:保存代码至 .vbs 文件,确认其中关于 IIS 日志路径是否正确,然后运行即可。
代码>> (单击显示/隐藏)
<span id=y2 style="DISPLAY: none" color="#0000ff" face="Courier New"
Const ForReading = 1
Const ForWriting = 2
![]()
Dim tempLine,curDir,objFolder,objFSO,objFiles,objFile,f
![]()
curDir = "C:\Windows\System32\LogFiles\"
![]()
![]()
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder(curDir)
Set objFiles = objFolder.Files
![]()
For Each f in objFiles
If Instr(f.Name,"ex0") > 0 Then
Set objFile = objFSO.OpenTextFile(curDir + f.Name, ForReading)
Set objFile1 = objFSO.OpenTextFile(curDir + Replace(f.Name,"ex0","bk0"), ForWriting, True)
![]()
![]()
Do Until objFile.AtEndOfStream
tempLine = objFile.ReadLine
If Instr(tempLine,".asp - 80") > 0 OR Instr(tempLine,".aspx - 80") > 0 Then
objFile1.WriteLine tempLine
End If
Loop
![]()
objFile.Close
objFile1.Close
End If
Next
![]()
Msgbox "日志文件清理完毕"
经常借用别人公司的一台托管的服务器上传下载些资料,顺便也帮人盯着服务器的配置与安全,前两天发现,系统盘 10G 硬盘空间没剩多少了,进去一看,原来 IIS 的日志文件(%windir%\system32\logfiles)下有5-6个G的日志文件,每天的日志约有 30-40M,于是把这些日志文件备份出来,压缩存储了一下,立刻节省了大量磁盘空间出来。
这个网站的访问里并不大,但日志文件为什么这么大呢,打开一看,IIS 的日志记录是很详细完整,但是仔细一看,好多行信息都是 jpg 和 gif ,这可能由于这台服务器是公司/产品宣传的缘故,所以图像文件较多,因此当访问一个页面时,会记录 10-20 条日志信息,从日志的真正用处来看,这些多余的图像文件的访问日志并不需要,而且,这么多的日志信息显然消耗了服务器的计算、IO、存储资源,影响高并发时的性能。
于是想在 IIS 的日志里对记录的 URI 资源类型做一下限制,很遗憾,没有发现这样的选项,在 Google 上似乎也没有找到替代的做法,这样看来,这好象是 IIS 日志记录功能的一个不足之处。
更新:感谢思归提供的解决办法,我把 Images 目录的日志记录取消了之后,日志文件在大小见下图:
另外再次极力推荐一下 IIS 的 gzip 功能,最近一直在研究其对性能和带宽的影响,应该来说,对于绝大多数互联网上的服务器来说,CPU 的资源比带宽资源的利用率更低,因此应该利用这些闲置的 CPU 资源来压缩页面,降低带宽点用,提高响应速度,而且,对于静态内容来说,这种压缩可以生成临时文件,避免每次访问都对资源进行压缩,因此对 CPU 资源的影响也是非常小的。对于大型企业来说,要在集中位置部署一个 B/S 的系统,那么利用 gzip 能大大节省租用运营商通讯线路带来的成本(利用 Internet Security and Accelerator 提供的 HTTP 压缩和缓存功能,也可以达到同样的目的)
在 port80software 这个网站上,提供了一在线测试工具,可能对你的网站进行测试,以确定其是否提供 gzip 压缩以及压缩比重、传输速度的提升等。