--HG--
branch : dev
This commit is contained in:
Renaud Paquay
2010-12-14 23:02:40 -08:00
21 changed files with 345 additions and 106 deletions

View File

@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="AzureSDK" value="C:\Program Files\Windows Azure SDK\v1.2\"/>
<add key="AzureSDK" value="C:\Program Files\Windows Azure SDK\v1.3\"/>
</appSettings>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>

View File

@@ -55,7 +55,7 @@
<ItemGroup>
<Reference Include="Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Moq, Version=4.0.812.4, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\moq\Moq.dll</HintPath>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<ServiceConfiguration serviceName="OrchardCloudService" osVersion="*" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
<ServiceConfiguration serviceName="OrchardCloudService" osFamily="1" osVersion="*" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration">
<Role name="Orchard.Azure.Web">
<Instances count="1" />
<ConfigurationSettings>

View File

@@ -1,11 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="OrchardCloudService" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WebRole name="Orchard.Azure.Web">
<Sites>
<Site name="Web">
<Bindings>
<Binding name="HttpIn" endpointName="HttpIn" />
</Bindings>
</Site>
</Sites>
<ConfigurationSettings>
<Setting name="DataConnectionString" />
</ConfigurationSettings>
<InputEndpoints>
<Endpoints>
<InputEndpoint name="HttpIn" protocol="http" port="80" />
</InputEndpoints>
</Endpoints>
</WebRole>
</ServiceDefinition>

View File

@@ -1,7 +1,10 @@
using System.Web;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Autofac;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Orchard.Environment;
namespace Orchard.Azure.Web {
@@ -16,6 +19,21 @@ namespace Orchard.Azure.Web {
}
protected void Application_Start() {
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSetter) =>
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))
);
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += (sender, e) => {
// If a configuration setting is changing
if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)) {
// Set e.Cancel to true to restart this role instance
e.Cancel = true;
}
};
RegisterRoutes(RouteTable.Routes);
_host = OrchardStarter.CreateHost(MvcSingletons);

View File

@@ -58,7 +58,7 @@
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="NHibernate.ByteCode.Castle">
<HintPath>..\..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll</HintPath>
</Reference>
@@ -144,7 +144,6 @@
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WebRole.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />

View File

@@ -1,28 +0,0 @@
using System.Linq;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace Orchard.Azure.Web {
public class WebRole : RoleEntryPoint {
public override bool OnStart() {
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSetter) =>
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))
);
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
return base.OnStart();
}
private static void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e) {
// If a configuration setting is changing
if ( e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange) ) {
// Set e.Cancel to true to restart this role instance
e.Cancel = true;
}
}
}
}

View File

@@ -54,7 +54,7 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.WindowsAzure.ServiceRuntime, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="Microsoft.WindowsAzure.StorageClient, Version=1.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>

View File

@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Core.XmlRpc;
using Orchard.Core.XmlRpc.Models;
namespace Orchard.Core.Common.Services {
public class XmlRpcHandler : IXmlRpcHandler {
private readonly IContentManager _contentManager;
public XmlRpcHandler(IContentManager contentManager) {
_contentManager = contentManager;
}
public void SetCapabilities(XElement options) {
const string manifestUri = "http://schemas.microsoft.com/wlw/manifest/weblog";
options.SetElementValue(XName.Get("supportsCustomDate", manifestUri), "Yes");
}
public void Process(XmlRpcContext context) {
switch (context.Request.MethodName) {
case "metaWeblog.newPost":
MetaWeblogSetCustomPublishedDate(
GetId(context.Response),
Convert.ToString(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
(XRpcStruct) context.Request.Params[3].Value,
Convert.ToBoolean(context.Request.Params[4].Value),
context._drivers);
break;
case "metaWeblog.editPost":
MetaWeblogSetCustomPublishedDate(
GetId(context.Response),
Convert.ToString(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
(XRpcStruct) context.Request.Params[3].Value,
Convert.ToBoolean(context.Request.Params[4].Value),
context._drivers);
break;
}
}
private void MetaWeblogSetCustomPublishedDate(int contentItemId, string appKey, string userName, string password, XRpcStruct content, bool publish, ICollection<IXmlRpcDriver> drivers) {
if (!publish)
return;
var publishedUtc = content.Optional<DateTime?>("dateCreated");
if (publishedUtc == null || publishedUtc > DateTime.UtcNow) // only pre-dating of content with the CommonPart
return;
var driver = new XmlRpcDriver(item => {
if (!(item is int))
return;
var id = (int)item;
var contentItem = _contentManager.Get(id, VersionOptions.Latest);
if (contentItem == null || !contentItem.Is<CommonPart>())
return;
contentItem.As<CommonPart>().PublishedUtc = publishedUtc;
});
if (contentItemId > 0)
driver.Process(contentItemId);
else
drivers.Add(driver);
}
private static int GetId(XRpcMethodResponse response) {
return response != null && response.Params.Count == 1 && response.Params[0].Value is int
? Convert.ToInt32(response.Params[0].Value)
: 0;
}
public class XmlRpcDriver : IXmlRpcDriver {
private readonly Action<object> _process;
public XmlRpcDriver(Action<object> process) {
_process = process;
}
public void Process(object item) {
_process(item);
}
}
}
}

View File

@@ -62,6 +62,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Common\Models\CommonPartVersionRecord.cs" />
<Compile Include="Common\Services\XmlRpcHandler.cs" />
<Compile Include="Containers\Controllers\ItemController.cs" />
<Compile Include="Containers\Drivers\ContainablePartDriver.cs" />
<Compile Include="Containers\Drivers\ContainerPartDriver.cs" />

View File

@@ -27,7 +27,8 @@ namespace Orchard.Core.XmlRpc.Controllers {
new XElement(XName.Get("clientType", ManifestUri), "Metaweblog"),
new XElement(XName.Get("supportsKeywords", ManifestUri), "No"),
new XElement(XName.Get("supportsCategories", ManifestUri), "No"),
new XElement(XName.Get("supportsFileUpload", ManifestUri), "No"));
new XElement(XName.Get("supportsFileUpload", ManifestUri), "No"),
new XElement(XName.Get("supportsCustomDate", ManifestUri), "No"));
foreach (var handler in _xmlRpcHandlers)
handler.SetCapabilities(options);

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using Orchard.Core.XmlRpc.Models;
@@ -23,7 +24,9 @@ namespace Orchard.Core.XmlRpc.Services {
{"double", x=>new XRpcData<double> { Value = (double)x }},
{"dateTime.iso8601", x=> {
DateTime parsedDateTime;
if(!DateTime.TryParse(x.Value, out parsedDateTime)) {
// try parsing a "normal" datetime string then try what live writer gives us
if(!DateTime.TryParse(x.Value, out parsedDateTime)
&& !DateTime.TryParseExact(x.Value, "yyyyMMddTHH:mm:ss", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out parsedDateTime)) {
parsedDateTime = DateTime.Now;
}
return new XRpcData<DateTime> {Value = parsedDateTime};

View File

@@ -18,8 +18,8 @@ namespace Orchard.Core.XmlRpc.Services {
{typeof(bool), p=>new XElement("boolean", (bool)p.Value?"1":"0")},
{typeof(string), p=>new XElement("string", p.Value)},
{typeof(double), p=>new XElement("double", (double)p.Value)},
{typeof(DateTime), p=>new XElement("dateTime.iso8601", ((DateTime)p.Value).ToString("o"))},
{typeof(DateTime?), p=>new XElement("dateTime.iso8601", ((DateTime?)p.Value).Value.ToString("o"))},
{typeof(DateTime), p=>new XElement("dateTime.iso8601", ((DateTime)p.Value).ToString("yyyyMMddTHH:mm:ssZ"))},
{typeof(DateTime?), p=>new XElement("dateTime.iso8601", ((DateTime?)p.Value).Value.ToString("yyyyMMddTHH:mm:ssZ"))},
{typeof(byte[]), p=>new XElement("base64", Convert.ToBase64String((byte[])p.Value))},
{typeof(XRpcStruct), p=>Map((XRpcStruct)p.Value)},
{typeof(XRpcArray), p=>Map((XRpcArray)p.Value)},

View File

@@ -51,7 +51,7 @@ namespace Orchard.Blogs.Handlers {
OnCreated<BlogPostPart>((context, bp) => updateBlogPostCount(bp.BlogPart));
OnPublished<BlogPostPart>((context, bp) => updateBlogPostCount(bp.BlogPart));
OnUnpublished<BlogPostPart>((context, bp) => updateBlogPostCount(bp.BlogPart));
OnVersioned<BlogPostPart>((context, bp1, bp2) => updateBlogPostCount(bp2.BlogPart));
OnVersioned<BlogPostPart>((context, bp1, bp2) => updateBlogPostCount(bp1.BlogPart));
OnRemoved<BlogPostPart>((context, bp) => updateBlogPostCount(bp.BlogPart));
OnRemoved<BlogPart>(

View File

@@ -51,33 +51,8 @@ namespace Orchard.Blogs.Models {
}
}
public DateTime? CreatedUtc {
get { return this.As<ICommonPart>().CreatedUtc; }
}
public DateTime? ScheduledPublishUtc { get; set; }
private string _scheduledPublishUtcDate;
public string ScheduledPublishUtcDate
{
get {
return !HasPublished && !string.IsNullOrEmpty(_scheduledPublishUtcDate) || !ScheduledPublishUtc.HasValue
? _scheduledPublishUtcDate
: ScheduledPublishUtc.Value.ToShortDateString();
}
set { _scheduledPublishUtcDate = value; }
}
private string _scheduledPublishUtcTime;
public string ScheduledPublishUtcTime {
get {
return !HasPublished && !string.IsNullOrEmpty(_scheduledPublishUtcTime) || !ScheduledPublishUtc.HasValue
? _scheduledPublishUtcTime
: ScheduledPublishUtc.Value.ToShortTimeString();
}
set { _scheduledPublishUtcTime = value; }
public DateTime? PublishedUtc {
get { return this.As<ICommonPart>().PublishedUtc; }
}
}
}

View File

@@ -70,7 +70,8 @@ namespace Orchard.Blogs.Services {
Convert.ToString(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
Convert.ToInt32(context.Request.Params[3].Value));
Convert.ToInt32(context.Request.Params[3].Value),
context._drivers);
context.Response = new XRpcMethodResponse().Add(result);
}
@@ -147,7 +148,8 @@ namespace Orchard.Blogs.Services {
string blogId,
string userName,
string password,
int numberOfPosts) {
int numberOfPosts,
IEnumerable<IXmlRpcDriver> drivers) {
IUser user = ValidateUser(userName, password);
@@ -161,7 +163,12 @@ namespace Orchard.Blogs.Services {
var array = new XRpcArray();
foreach (var blogPost in _blogPostService.Get(blog, 0, numberOfPosts, VersionOptions.Latest)) {
array.Add(CreateBlogStruct(blogPost, urlHelper));
var postStruct = CreateBlogStruct(blogPost, urlHelper);
foreach (var driver in drivers)
driver.Process(postStruct);
array.Add(postStruct);
}
return array;
}
@@ -210,7 +217,8 @@ namespace Orchard.Blogs.Services {
_contentManager.Create(blogPost.ContentItem, VersionOptions.Draft);
if (publish)
var publishedUtc = content.Optional<DateTime?>("dateCreated");
if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow))
_blogPostService.Publish(blogPost);
foreach (var driver in drivers)
@@ -253,11 +261,21 @@ namespace Orchard.Blogs.Services {
var description = content.Optional<string>("description");
var slug = content.Optional<string>("wp_slug");
blogPost.Title = title;
blogPost.Slug = slug;
blogPost.Text = description;
// BodyPart
if (blogPost.Is<BodyPart>()) {
blogPost.As<BodyPart>().Text = description;
}
if (publish)
//RoutePart
if (blogPost.Is<RoutePart>()) {
blogPost.As<RoutePart>().Title = title;
blogPost.As<RoutePart>().Slug = slug;
_routableService.FillSlugFromTitle(blogPost.As<RoutePart>());
blogPost.As<RoutePart>().Path = blogPost.As<RoutePart>().GetPathWithSlug(blogPost.As<RoutePart>().Slug);
}
var publishedUtc = content.Optional<DateTime?>("dateCreated");
if (publish && (publishedUtc == null || publishedUtc <= DateTime.UtcNow))
_blogPostService.Publish(blogPost);
foreach (var driver in drivers)
@@ -292,14 +310,20 @@ namespace Orchard.Blogs.Services {
private static XRpcStruct CreateBlogStruct(BlogPostPart blogPostPart, UrlHelper urlHelper) {
var url = urlHelper.AbsoluteAction(() => urlHelper.BlogPost(blogPostPart));
return new XRpcStruct()
var blogStruct = new XRpcStruct()
.Set("postid", blogPostPart.Id)
.Set("dateCreated", blogPostPart.CreatedUtc)
.Set("title", blogPostPart.Title)
.Set("wp_slug", blogPostPart.Slug)
.Set("description", blogPostPart.Text)
.Set("link", url)
.Set("permaLink", url);
if (blogPostPart.PublishedUtc != null) {
blogStruct.Set("dateCreated", blogPostPart.PublishedUtc);
blogStruct.Set("date_created_gmt", blogPostPart.PublishedUtc);
}
return blogStruct;
}
}
}

View File

@@ -44,6 +44,8 @@
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Drivers\PublishLaterPartDriver.cs" />
@@ -59,6 +61,7 @@
<Compile Include="Services\IPublishLaterService.cs" />
<Compile Include="Services\PublishingTaskManager.cs" />
<Compile Include="Services\PublishLaterService.cs" />
<Compile Include="Services\XmlRpcHandler.cs" />
<Compile Include="ViewModels\PublishLaterViewModel.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Orchard.ContentManagement;
using Orchard.Core.Contents;
using Orchard.Core.XmlRpc;
using Orchard.Core.XmlRpc.Models;
using Orchard.Localization;
using Orchard.PublishLater.Models;
using Orchard.Security;
using Orchard.Tasks.Scheduling;
namespace Orchard.PublishLater.Services {
public class XmlRpcHandler : IXmlRpcHandler {
private readonly IContentManager _contentManager;
private readonly IPublishingTaskManager _publishingTaskManager;
private readonly IMembershipService _membershipService;
private readonly IAuthorizationService _authorizationService;
public XmlRpcHandler(IContentManager contentManager,
IPublishingTaskManager publishingTaskManager,
IMembershipService membershipService,
IAuthorizationService authorizationService) {
_contentManager = contentManager;
_publishingTaskManager = publishingTaskManager;
_membershipService = membershipService;
_authorizationService = authorizationService;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void SetCapabilities(XElement options) {
const string manifestUri = "http://schemas.microsoft.com/wlw/manifest/weblog";
options.SetElementValue(XName.Get("supportsCustomDate", manifestUri), "Yes");
}
public void Process(XmlRpcContext context) {
switch (context.Request.MethodName) {
case "metaWeblog.newPost":
MetaWeblogSetCustomPublishedDate(
GetId(context.Response),
Convert.ToString(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
(XRpcStruct)context.Request.Params[3].Value,
Convert.ToBoolean(context.Request.Params[4].Value),
context._drivers);
break;
case "metaWeblog.editPost":
MetaWeblogSetCustomPublishedDate(
GetId(context.Response),
Convert.ToString(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
(XRpcStruct)context.Request.Params[3].Value,
Convert.ToBoolean(context.Request.Params[4].Value),
context._drivers);
break;
case "metaWeblog.getPost":
MetaWeblogGetCustomPublishedDate(
GetPost(context.Response),
Convert.ToInt32(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
context._drivers);
break;
case "metaWeblog.getRecentPosts":
MetaWeblogGetCustomPublishedDate(
GetPost(context.Response),
Convert.ToInt32(context.Request.Params[0].Value),
Convert.ToString(context.Request.Params[1].Value),
Convert.ToString(context.Request.Params[2].Value),
context._drivers);
break;
}
}
private void MetaWeblogGetCustomPublishedDate(XRpcStruct postStruct, int itemId, string userName, string password, ICollection<IXmlRpcDriver> drivers) {
if (itemId < 1)
return;
var driver = new XmlRpcDriver(item => {
var post = item as XRpcStruct;
if (post == null)
return;
var postId = post.Optional<int>("postid");
var contentItem = _contentManager.Get(postId, VersionOptions.Latest);
if (contentItem == null)
return;
var publishedUtc = contentItem.As<PublishLaterPart>().ScheduledPublishUtc.Value;
if (publishedUtc == null || publishedUtc <= DateTime.UtcNow)
return;
post.Set("dateCreated", publishedUtc);
post.Set("date_created_gmt", publishedUtc);
});
if (postStruct != null)
driver.Process(postStruct);
else
drivers.Add(driver);
}
private void MetaWeblogSetCustomPublishedDate(int contentItemId, string appKey, string userName, string password, XRpcStruct content, bool publish, ICollection<IXmlRpcDriver> drivers) {
var user = ValidateUser(userName, password);
if (user == null)
return;
var publishedUtc = content.Optional<DateTime?>("dateCreated");
if (publishedUtc == null || publishedUtc <= DateTime.UtcNow) // only post-dating/scheduling of content with the PublishLaterPart
return;
var driver = new XmlRpcDriver(item => {
if (!(item is int))
return;
var id = (int)item;
var contentItem = _contentManager.Get(id, VersionOptions.Latest);
if (contentItem == null || !contentItem.Is<PublishLaterPart>())
return;
_authorizationService.CheckAccess(Permissions.PublishOthersContent, user, null);
contentItem.As<PublishLaterPart>().ScheduledPublishUtc.Value = publishedUtc;
_publishingTaskManager.Publish(contentItem, (DateTime)publishedUtc);
});
if (contentItemId > 0)
driver.Process(contentItemId);
else
drivers.Add(driver);
}
private static XRpcStruct GetPost(XRpcMethodResponse response) {
return response != null && response.Params.Count == 1 && response.Params[0].Value is XRpcStruct
? response.Params[0].Value as XRpcStruct
: null;
}
private static int GetId(XRpcMethodResponse response) {
return response != null && response.Params.Count == 1 && response.Params[0].Value is int
? Convert.ToInt32(response.Params[0].Value)
: 0;
}
private IUser ValidateUser(string userName, string password) {
IUser user = _membershipService.ValidateUser(userName, password);
if (user == null) {
throw new OrchardCoreException(T("The username or e-mail or password provided is incorrect."));
}
return user;
}
public class XmlRpcDriver : IXmlRpcDriver {
private readonly Action<object> _process;
public XmlRpcDriver(Action<object> process) {
_process = process;
}
public void Process(object item) {
_process(item);
}
}
}
}

View File

@@ -41,7 +41,7 @@ namespace Orchard.Tags.Services {
switch (context.Request.MethodName) {
case "metaWeblog.getCategories": // hack... because live writer still asks for it...
if (context.Response == null)
context.Response = new XRpcMethodResponse();
context.Response = new XRpcMethodResponse().Add(new XRpcArray());
break;
case "wp.getTags":
var tags = MetaWeblogGetTags(