RSS 2.0 Feed
CnForums
CnForums相关
摘要:基于CommunityServer本地化开发,除论坛外,还有blog和相册系统,更有丰富的插件功能。 下载: http://www.CommunityServer.cn/builds...[阅读全文]

posted @ | Feedback (20) | Filed Under [ Community Server CnForums ]

摘要:可能我们已经有了一个完整的系统,已经有了一套用户系统,而需要加上论坛,也就是和CnForums整合,希望能共享验证,共享用户资料。可能有很多种方案,以我以前写过的一套解决方案为例,说明一下思路和具体实现过程,抛砖引玉:先说说用户共享,最理想的做法是共享一个用户库,而一般都不太方便,所以最简单有效的方法就是保证两个用户库的同步,也就是以前系统的用户表数据在CnForums的表数据有一份拷贝,所有系统的注册/修改资料/登录在同一个入口,修改资料则同步修改,注册用户则同步插入。要注意CnForums中数据表有2个:forums_Users、forums_UserProfile ,以UserID关联,在同步用户数据的时候,最好直接使用CnForums现有的注册/修改资料的存储过程forums_User_CreateUpdateDelete,或者参考其实现。(特别注意一下,如果你想密码也同步,就要注意@PasswordFormat参数,如果以前系统的密码没有加密,那么该参数的值就设置为0,否则就设置一个10以后的数字)具体实现就是将CnForums的所有表和存储过程安装在原来的数据库上,因为CnForums的所有存储过程和表都是forums_开头,所以很容易区分。用SQL调用CnForums的forums_User_CreateUpdateDelete存储过程或参考其实现,写个SQL或者导入程序把以前系统的用户数据导入到CnForums的用户表中,修改 修改资料和注册的程序,保证两个地方用户数据的同步。用户同步后,就是共享登录验证,共享登录验证有两种方案,一种就是保证密码同步、另一种就是只采用一个系统的登录验证。先说说第一种,CnForums可以支持多种密码验证方案,如果你以前密码没有加密,那么什么都不用改,只要注意在同步用户的时候用户资料的PasswordFormat字段为0。如果是自己的加密方式,那么就要修改一下CnForums的相关源码,在AspNetForums.Enumerations.UserPasswordFormat中增加枚举,枚举的数值和同步用户数据时@PasswordFormat参数参数的值一致,这就是为什么让你设置一个10以上的数字了。再在AspNetForums.Users.public static string Encrypt(UserPasswordFormat format, string cleanString, string salt)方法中增加您原来系统的加密算法。如果您嫌第一种复杂,那么就可以采用第二种,共用一个登录验证,CnForums使用的是asp.net的Forms验证,在登录成功授权的时候,记录登录的用户名(username)(祥见AspNetForums.Controls中的Login.cs:FormsAuthentication.SetAuthCookie(userToLogin.Username, autoLogin.Checked),以后每次获取用户资料都是根据这个登录用户名来取用户资料(具体参考AspNetForums.Users.GetUser()、AspNetForums.Users.GetLoggedOnUsername()和AspNetForums.ForumsHttpModule.Application_AuthenticateRequest(Object source, EventArgs e)方法,ForumContext.Current.UserName = context.User.Identity.Name;),所以我们如果要让CnForums共享主系统的登录验证,可以在主系统登录成功后,也记录当前登录的Username,也就是Login.cs中的FormsAuthentication.SetAuthCookie(userToLogin.Username, autoLogin.Checked),在CnForums中,只要表中有当前登录的这个用户,就会认为当前这个用户是登录过的(在CnForums的HttpModule里面,如果当前登录的Username在表中没记录,则会自动注销,所以一定要保持用户表的同步)。还有要注意就是Forms验证不能跨域,所以就要让CnForums免虚拟目录安装,具体方法可搜索以前的帖子。第二种方案的具体实现:免虚拟目录安装好CnForums;在主系统的登录程序中,密码校验成功后,使用FormsAuthentication.SetAuthCookie(username, autoLogin)方法设置登录成功,这样,当主系统登录后,CnForums就也是成功登录的。...[阅读全文]

posted @ | Feedback (17) | Filed Under [ CnForums ]

摘要:AspNetForums中对于用户权限,是类似于Windows系统采用基于角色(Role)的权限控制,这样,我们就可以根据需要,设定不同的角色,设置各个角色的权限,并将各个用户属于不同的角色。根据用户所属的角色,来判断用户可以有哪些权限,进行哪些操作。 先看看数据库设计:forums_Users表,用户表,UserID为主键,唯一确定一个用户forums_Roles表,角色表,RoleID为主键,唯一确定一个角色forums_UsersInRoles表,用户和角色对应关系表,存储用户所在的角色forums_ForumPermissions表,角色在某版块的权限,记录了在每个版块中各个角色可以有权限进行哪些操作 再看看是如何来判断用户操作权限的:当用户登录后,要获取论坛所有版块的信息并缓存,在获取版块信息的同时,要获取当前用户在每个版块操作(如发表新帖、投票、附件等)的权限。操作权限有三种:"有权限"、"无权限"和"未设置",如果是未设置,那么就看这个操作的默认权限是什么。一个用户可以属于多个角色,每个角色对应的操作权限不一样,要判断这个用户有没有当前操作的权限,则遍历这个用户所属的所有角色,如果任一角色有权限,那么该用户就有该操作的权限。具体实现请参考源码:Components\Forums.cs中的GetForums(ForumContext forumContext, int userID, bool ignorePermissions, bool cacheable)方法和Components\Provider\ForumsDataProvider.cs中PopulateForumPermissionRightFromIDataReader方法。 角色和web.config的结合:Asp.Net有一个功能就是可以用于控制对 URL 资源的客户端访问(可参考QuickStart上的说明)。它对于用于生成请求的 HTTP 方法(GET 或 POST)是可配置的,并且可被配置为允许或拒绝访问用户组或角色组。这个方法也被应用到AspNetForums中,例如在admin目录(管理员操作相关页面)和Moderate目录(版主操作相关页面)下都有一个web.config目录,通过web.config,可以设定其所在目录的URL客户端访问权限。 以Admin目录下的web.config中的配置为例: <authorization>  <allow roles="Global Administrators" />  <allow roles="Site Administrators" />  <deny users="*" /> </authorization>这个表示对于属于Global Administrators角色和Site Administrators角色授权,其他任何用户都禁止访问该目录下的页面。因为对于部分页面,希望版主也可以进行访问,那么可以增加如下配置:    <location path="ForumAdmin.aspx">      <system.web>        <authorization>          <allow roles="Global Administrators" />          <allow roles="Site Administrators" />          <allow roles="Global Moderators" />          <allow roles="Site Moderators" />          <deny users="?" />        </authorization>      </system.web>    </location>    这样,对于/Admin/ForumAdmin.aspx这个URL,属于Global Moderators或Site Moderators角色的用户也可以有权访问。 怎么设置登录用户的角色?具体实现请参考源码:Components\Roles.cs 和文章《如何使用 Forms 身份验证创建 GenericPrincipal 对象》 附:CnForums源码下载...[阅读全文]

posted @ | Feedback (31) | Filed Under [ CnForums ]

摘要:Forums中有一个自定义的异常类ForumException,继承自ApplicationException,与之对应的有一个枚举ForumExceptionType,用来记录异常的类型。Forums的自定义异常类主要是对可能出现的各种异常分别进行处理——记录错误日志、消息提示。 当asp.net中抛出异常后,会触发HttpApplication的Error事件,可以在HttpModule中捕获到,如Forums就是在其HttpModule中处理Forums的异常:application.Error += new EventHandler(this.Application_OnError);。在捕获到自定义异常后,根据异常的类别(ForumExceptionType枚举)对一些重要的错误信息记录日志(保存到数据库的forums_Exceptions表),以供管理员参考。而对于异常,为了给用户友好的提示,会将根据异常类显示出比较友好的错误信息。 Forums的自定义异常不仅仅作用在于对错误异常的处理,而且还有一个重要作用就是用来做信息提示用,例如提示用户注册成功,那么抛出一个异常:throw new ForumException(ForumExceptionType.UserAccountCreatedAuto); 然后就会自动跳转到大家所看到的提示用户已经成功注册的信息。 Forums是怎么样根据异常跳转到对应的信息呢?前面说了Forums对于每个自定异常都对应有一个错误类型枚举ForumExceptionType,Forums针对每一种语言都有一个Messages.xml,xml的规则如下:<root> <message id="1"> <title>无权限登录</title> <body>您不是超级管理员.</body> </message> <message id="2"> <title>无权限修改</title> <body>此版块不存在,或您无权限修改.</body> </message> </root> 其中的id和ForumExceptionType的int型一一对应public enum ForumExceptionType ...{ AdministrationAccessDenied = 1, PostEditAccessDenied = 2, } 那么在HttpModule中处理异常时,直接跳转到消息提示页Response.Redirect(Globals.GetSiteUrls().Message( exception.ExceptionType ), true);,如http://localhost/CnForums/Msgs/default.aspx?MessageID=1 在msg的显示页,会根据MessageID,从xml中找到对应的提示信息,显示在页面上。...[阅读全文]

posted @ | Feedback (24) | Filed Under [ CnForums ]

摘要:Forums中很多地方要用到邮件发送,如:邮件注册、找回密码、邮件订阅等。 添加新贴的代码流程非常慢。每次添加帖子,应用程序首先要确保没有重复贴,然后格式化帖子内容和表情图像,记号并索引,如果必要还要将帖子添加到相应的队列中,对附件进行有效性检查,最终完成发贴后,给预订者发出 e-mail 通知。显然,这里做的工作太多。索引帖子是一个很耗时的操作,此外,内建的 System.Web.Mail 功能要与 SMTP 服务器连接并顺序发送邮件。当特定帖子或主题预定者数量增加时,发送帖子的执行时间会越来越长。   并不是每个请求都需要索引邮件,所以Forums采用的是批量集中处理,并且一次只索引25个帖子或每隔五分钟发送一次邮件。定时器这部分在我的上一个blog有说明《asp.net forums中定时器的应用》 在Asp.Net中邮件发送非常容易,一般都需要Smtp服务器,但是一些免费信箱的Smtp服务器(如smtp.163.com)都需要账号密码验证。在MailMessage对象的Filds集合中加入以下内容即可: Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1" ); //basic authenticationMessage.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", txtUserName.Text ); // 设置smtp服务器登录账号(如您163.com的信箱账号)Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", txtPassword.Text ); // 设置smtp服务器登录密码 (如您的163.com的信箱账号密码)但是使用免费的Smtp服务器一定要注意一个问题,就是发送邮件的账号(即Message.From)必须和上面的Smtp服务器登录账号一致,否则将会导致找不到cdo对象的异常。 示例代码:邮件发送示例代码...[阅读全文]

posted @ | Feedback (29) | Filed Under [ CnForums ]

摘要:在Asp.Net中使用定时器,破宝之前已有Blog写过《在 ASP.NET 中使用计时器(Timer)》,这里主要针对Asp.Net Forums来说一下其具体实现。 在Asp.Net Forums中,对定时器有如下应用:1. 更新论坛统计信息2. 定时索引指定条数的帖子3. 定时群发队列中的邮件 Forums中对定时器的调用是放在自定义HttpModule的Init方法中(如果您没有使用HttpModule,也可以在Globals.aspx中的Application_OnStart 中调用定时器)。         // 定时器         static Timer statsTimer;         static Timer emailTimer;         // 定时间隔         private long EmailInterval = ForumConfiguration.GetConfig().ThreadIntervalEmail * 60000;         private long StatsInterval = ForumConfiguration.GetConfig().ThreadIntervalStats * 60000;         public String ModuleName {              get { return "ForumsHttpModule"; }          }             // *********************************************************************         //  ForumsHttpModule         //         /**//// <summary>         /// Initializes the HttpModule and performs the wireup of all application         /// events.         /// </summary>         /// <param name="application">Application the module is being run for</param>         public void Init(HttpApplication application) {              // Wire-up application events             //             // 略去其他代码                          ForumConfiguration forumConfig = ForumConfiguration.GetConfig();             // 如果使用定时器并且定时器还没初始化             if( forumConfig != null             &&  forumConfig.IsBackgroundThreadingDisabled == false ) {                 if (emailTimer == null)                     // 新建定时器                     // 新建一个TimerCallback委托,具体要执行的方法在ScheduledWorkCallbackEmailInterval中                     emailTimer = new Timer(new TimerCallback(ScheduledWorkCallbackEmailInterval), application.Context, EmailInterval, EmailInterval);                 if( forumConfig.IsIndexingDisabled == false                  &&    statsTimer == null ) {                     statsTimer = new Timer(new TimerCallback(ScheduledWorkCallbackStatsInterval), application.Context, StatsInterval, StatsInterval);             }         }         }         /**//// <summary>         /// 释放定时器         /// </summary>         public void Dispose() {             statsTimer = null;             emailTimer = null;         }         Timer Callbacks#region Timer Callbacks         /**//// <summary>         /// 定时发送队列中待发送的邮件         /// </summary>         private void ScheduledWorkCallbackEmailInterval (object sender) {             try {                 // 当处理邮件时暂停定时器                 emailTimer.Change( System.Threading.Timeout.Infinite, EmailInterval );                 // 发送队列中的邮件                 //                 Emails.SendQueuedEmails( (HttpContext) sender);                 // 更新匿名用户                 //                 Users.UpdateAnonymousUsers( (HttpContext) sender);             }             catch( Exception e ) {                 ForumException fe = new ForumException( ForumExceptionType.EmailUnableToSend, "Scheduled Worker Thread failed.", e );                 fe.Log();             }             finally {                 // 重新启动定时器                 emailTimer.Change( EmailInterval, EmailInterval );             }         }         /**//// <summary>         /// 定时索引帖子和定时更新论坛统计信息         /// </summary>         private void ScheduledWorkCallbackStatsInterval(object sender) {             try {                 // 休眠定时器                 statsTimer.Change( System.Threading.Timeout.Infinite, StatsInterval );                 // 每次索引100篇帖子                 //                 Search.IndexPosts( (HttpContext) sender, 100); ......[阅读全文]

posted @ | Feedback (15) | Filed Under [ CnForums ]

摘要:在Forums中,有些内容是不固定的,例如用户资料,除了一些基本资料,可能还要有一些其他资料信息,例如MSN、个人主页、签名档等,一般对于这样的都是每一个属性对应于数据库中的一个字段。但是如果以后我们因为需要增加一些属性,例如QQ号、Blog地址等,如果还是用这种增加数据表字段的方法,那么将会频繁的修改数据库表结构、存储过程、数据库访问的程序。 或许您也遇到过类似问题,看Forums中是怎么借用.Net的序列化和反序列化来解决的:例如我需要在用户资料里面增加QQ号这个属性,那么我只需要在User类中增加一个属性public String QQIM {    get { return GetExtendedAttribute("QQIM"); }    set { SetExtendedAttribute("QQIM", value); }}不需要修改数据库表结构,不需要修改存储过程,连数据库访问的程序都不需要动。 其具体实现的主要代码: // 首先新建在User类中新建一个NameValueCollection对象,将这些扩展属性都保存在NameValueCollection对象中 NameValueCollection extendedAttributes = new NameValueCollection(); // 从NameValueCollection集合中取纪录 public string GetExtendedAttribute(string name)     {     string returnValue = extendedAttributes[name];     if (returnValue    == null)     return string.Empty;     else     return returnValue; } // 设置扩展属性的在NameValueCollection中的键值和值 public void SetExtendedAttribute(string    name, string value)     {     extendedAttributes[name] = value; } // 将extendedAttributes对象(前面定义的用来保存所有的用户扩展信息的NameValueCollection对象)序列化为内存流 // 可以用来保存到数据库中 public byte[] SerializeExtendedAttributes()     {     // 序列化对象     BinaryFormatter    binaryFormatter    = new BinaryFormatter();     // 创建一个内存流,序列化后保存在其中     MemoryStream ms    = new MemoryStream();     byte[] b;     // 将extendedAttributes对象(里面保存了所有的用户扩展信息)序列化为内存流     //     binaryFormatter.Serialize(ms, extendedAttributes);     // 设置内存流的起始位置     //     ms.Position    = 0;              // 读入到 byte 数组     //     b =    new    Byte[ms.Length];     ms.Read(b, 0, b.Length);     ms.Close();     return b; } // 反序列化extendedAttributes对象的内容 // 从数据库中读取出来的 public void DeserializeExtendedAttributes(byte[] serializedExtendedAttributes)  {     if (serializedExtendedAttributes.Length    == 0)     return;     try         {     BinaryFormatter    binaryFormatter    = new BinaryFormatter();     MemoryStream ms    = new MemoryStream();     // 将 byte 数组到内存流     //     ms.Write(serializedExtendedAttributes, 0, serializedExtendedAttributes.Length);     // 将内存流的位置到最开始位置     //     ms.Position    = 0;     // 反序列化成NameValueCollection对象,创建出与原对象完全相同的副本     //     extendedAttributes = (NameValueCollection) binaryFormatter.Deserialize(ms);     ms.Close();     }      catch    {}      } 实质上序列化机制是将类的值转化为一个一般的(即连续的)字节流,然后就可以将该流保存到数据库的某个字段中(在数据库中forums_UserProfile表中有一个字段“StringNameValues varbinary(7500)”)。读取的过程对对象进行反序列化时,创建出与原对象完全相同的副本。 注意一般这类属性在数据库中是不能被检索到的,并且要这些属性能被序列化。 更详细内容请查阅MSDN和Asp.Net Forums源码...[阅读全文]

posted @ | Feedback (41) | Filed Under [ CnForums ]