摘要
本系列文章将一步步教你如何在GitHub上创建自己的博客或主页,事实上相关的文章网上有很多,这里只是把自己的经验分享给新手,方便他们逐步开始GitHub之旅。本篇将介绍如何使GitHub博客如何兼容Windows Writer。
本文首发于我的github主页:一步步在GitHub上创建博客主页(7)--兼容Windows Writer的服务提供器,欢迎交流
缘起
什么?GitHub主页能支持Windows Writer?开玩笑吧!你一定会这么问我。好吧,我的确是标题党了,但是本篇要介绍的不是让GitHub主页兼容我们的Windows Writer,而是自己实现一个的兼容Windows Writer的服务提供器。你可以通过编程让这个提供器支持你想要的功能,以方便我们在本机构建jekyll模板和博客结构。
我喜欢使用Windows Writer写文章,主要是因为它能够兼容很多博客系统,我可以将截图复制在Writer里面,还可以使用代码插件,当我发布博客的时候这一切都工作单非常好,图片会上传到我的博客托管网站上,并自动建立应有的图片链接。这使我节省了很多时间。然而,最近开始在Github上开自己的博客。并使用git管理静态的文件。我在本系列前面的文章中对此有很详细的描述。但是,由于Github实际上仅仅支持的是静态html,我必须在本地用windows writer完成我的文章编辑,然后将writer生成的html源代码贴到我预先准备好的_posts路径下的文件中。如果文章没有图片那还比较简单,但是如果有图片就非常麻烦了,每张图片都必须想办法从writer中复制并手动在img文件夹中创建好,这还不是最糟糕的,我还必须将source中的img标签的src手动一个个改成应该的链接。重复上面的步骤让我十分不爽。之前想过是不是通过做一个writer的插件来让这个过程更方便些,但是无果。今天偶然想到,能不能像博客园那样自己在本地实现一个writer的provider,这样writer一定会将图片和文章“上传”到我的本地web应用程序,我只要在web应用程序中把上面这些繁琐的工作自动实现就行了。
XML-RPC和IMetaWeblog
在网上搜索到相关的实现方法:给自己的Blog程序添加对Windows Live Writer的支持
writer与支持writer的提供程序之间有很多接口方式,其中一种简单的实现方式就是XML-RPC和MetaWeblog。
什么是XML-RPC:顾名思义吧,就是基于XML的远程调用,类似SOAP。XML-RPC的唯一优势就是”as simple as possible”。
什么是MetaWeblog:是一种博客系统的接口标准,容易实现。
XML-RPC.NET
这是一个实现了XML-RPC的一个类库,并在源码中结合实现,附带了IMetaWeblog等接口的定义。这里下载。
开始
交代完基本概念后,我们开始动手做起来。首先按照上面的链接下载XML-RPC.NET。我喜欢使用源代码构建应用程序,所以解压后找到源代码中的src目录,其中是一个.net 2.0的项目xmlrpc,包含了完整的源代码。构建一个solution,并添加这个项目,以及新建一个ASP.NET web应用程序,并引用项目中的xmlrpc
不出意外,现在可以直接编译通过。找到解压包中的interfaces文件夹中的MetaWeblogAPI.cs,该文件中有IMetaWeblog的所有接口定义,将它添加到web应用程序中。为了能够与writer兼容,需要添加一个接口和一个结构:
{
public string url;
public string blogid;
public string blogName;
}
UserBlog[] getUsersBlogs(string appKey, string username, string password);
XML-RPC.NET的XmlRpcService类实现了IHttpHandler,并提供所有XML-RPC的所有底层细节的实现。所以接下来只要用一个ashx实现IMetaWeblog就可以了:
Rpc.ashx
public class Rpc : XmlRpcService, IMetaWeblog
{
}
然后debug起来之后,能够看到一个方法说明列表。
再来看看接口中各个方法的定义:
- getUsersBlogs:在writer中设置博客的时候会被调用,必须实现,随便返回个就行。
- editPost:用于writer编辑已经在服务器端的文章,可选实现
- getCategories:获取所有的分类,在writer“设置类别”功能刷新的时候会被调用;初始化一个blog配置的时候也可能被调用,可选实现
- getPost:writer试图同步远程文章,以实现同步,可选实现
- getRecentPosts:获取最近文章,可选实现
- newPost:新文章创建,必须实现
- newMediaObject:图片通过此方法上传,最好实现,不然就没有做的意义了
接下来,我说说我的需求吧。我需要在writer中点击“发布”时,图片能够自动存到我指定的路径,文章也能自动创建在我指定的路径,文章中的图片链接能够正确的指向。
配置writer
选择其他服务
网址输入我们的ashx地址。用户名和密码随便啦,因为我们自己实现的,不care。先不要点记住密码
writer不能识别API类型,手动选择一下,并且再次输入地址。
这样就配置好了一个新的blog提供器,你可以自己定义一个名字。重启writer后就能在列表中看到这个提供器了。
接下来所有的细节就是实现这些接口,不难。这里我不再详细讲述。不过需要注意一个图片的问题:
writer处理图片都时候,默认会产生一个a和一个img,在上传至服务端的时候会同时上传两张图片一张用于a的链接,一张用于img,两张图片有大小的区别。如果都上传无疑是空间的浪费。所以在编辑文档的时候要注意去掉图片的默认链接,这样上传至服务端的图片就会只有一张:
选中图片,设置为”无连接“
如果有人需
某一个时候,CheckBoxList的选择太多,用户需要一个全选或全取消的功能。下面Insus.NET使用Javascript来实现它。
准备好一个对象:
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for MusicType
/// </summary>
namespace Insus.NET
{
public class MusicType
{
private int _ID;
private string _TypeName;
public int ID
{
get { return _ID; }
set { _ID = value; }
}
public string TypeName
{
get { return _TypeName; }
set { _TypeName = value; }
}
public MusicType()
{
//
// TODO: Add constructor logic here
//
}
public MusicType(int id, string typeName)
{
this._ID = id;
this._TypeName = typeName;
}
}
}
填充对象:
{
List<MusicType> mt = new List<MusicType>();
mt.Add(new MusicType(1, "甜密情歌"));
mt.Add(new MusicType(2, "网络红歌"));
mt.Add(new MusicType(3, "儿童歌曲"));
mt.Add(new MusicType(4, "民族精选"));
mt.Add(new MusicType(5, "校园歌曲"));
mt.Add(new MusicType(6, "摇滚音乐"));
mt.Add(new MusicType(7, "胎教音乐"));
mt.Add(new MusicType(8, "红色名曲"));
mt.Add(new MusicType(9, "串烧金曲"));
mt.Add(new MusicType(10, "动慢歌曲"));
return mt;
}
在站点建一个aspx网页,并拉两个控件,一个是CheckBox和CheckBoxList:
<asp:CheckBoxList ID="CheckBoxListMusicType" runat="server" RepeatColumns="3" RepeatDirection="Horizontal" Width="300"></asp:CheckBoxList>
接下来,我们为CheckBoxList绑定数据:
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Insus.NET;
public partial class Default2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
If you are developing a project related to AD(Active Directory), you probably need to search a particular user by its user name, surely you can create a method to achieve you own algorithm, but if the system provide a build-in one, I suggest you can just take the advantage the build-in method, which is quite optimized. In this post, I would like to share with you how to find a specified user using two corresponding methods.
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.Collections;
using System.DirectoryServices.ActiveDirectory;
namespace ConsoleApplicationTest
{
class Program
{
static void Main(string[] args)
{
}
/// <summary>
/// Search the user by using filter (ok )
/// </summary>
/// <param name="de"></param>
/// <param name="filter"></param>
/// <returns></returns>
public string SearchUser(DirectoryEntry de, string filter)
{
string userPath = string.Empty;
if (de == null)
{
Domain domain = Domain.GetCurrentDomain();
de = domain.GetDirectoryEntry();
}
using (DirectorySearcher searcher = new DirectorySearcher(de))
{
searcher.SizeLimit = System.Int32.MaxValue;
searcher.PageSize = System.Int32.MaxValue;
if (string.IsNullOrEmpty(filter))
{
searcher.Filter = "(objectCategory=user)";
}
else
{
searcher.Filter = "(&(objectCategory=user)(sAMAccountName=" + filter + "))";
}
try
{
// search all the items.
using (SearchResultCollection result = searcher.FindAll())
{
foreach (SearchResult _ent in result)
{
DirectoryEntry ent = _ent.GetDirectoryEntry();
userPath = ent.Path;
}
}
}
catch (Exception er)
{
//log.Error(er.ToString());
}
}
return userPath;
}
/// <summary>
/// By members
/// </summary>
/// <param name="dirEntry"></param>
/// <param name="filter"></param>
public void SearchUser2(DirectoryEntry dirEntry, string filter)
{
object members = dirEntry.Invoke("Members", null); //传入dev.spdb.com
foreach (object member in (IEnumerable)members)
{
System.DirectoryServices.DirectoryEntry de = new
System.DirectoryServices.DirectoryEntry(member);
String distName = GetStringProperty(de.Properties, "distinguishedName");
String prefix = (string)de.Properties["objectCategory"][0];
int startPos = 0;
int endPos = 0;
startPos = prefix.IndexOf("=") + 1;
endPos = prefix.IndexOf(",", startPos);
prefix = prefix.Substring(startPos, endPos - startPos);
}
}
private string GetStringProperty(PropertyCollection properties,string propertyName)
{
string value=string.Empty;
foreach (string name in properties.PropertyNames)
{
value=properties[propertyName][0].ToString();
}
return value ;
}
}
public class