MetaWeblog.cs :  » Bloggers » SubText » Subtext » Framework » XmlRpc » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Bloggers » SubText 
SubText » Subtext » Framework » XmlRpc » MetaWeblog.cs
#region Disclaimer/Info

///////////////////////////////////////////////////////////////////////////////////////////////////
// Subtext WebLog
// 
// Subtext is an open source weblog system that is a fork of the .TEXT
// weblog system.
//
// For updated news and information please visit http://subtextproject.com/
// Subtext is hosted at Google Code at http://code.google.com/p/subtext/
// The development mailing list is at subtext@googlegroups.com 
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////

#endregion

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using CookComputing.XmlRpc;
using Subtext.Extensibility;
using Subtext.Framework.Components;
using Subtext.Framework.Configuration;
using Subtext.Framework.Logging;
using Subtext.Framework.Properties;
using Subtext.Framework.Routing;
using Subtext.Framework.Security;
using Subtext.Framework.Services;
using Subtext.Framework.Tracking;
using Subtext.Framework.Util;

//Need to find a method that has access to context, so we can terminate the request if AllowServiceAccess == false.
//Users will be able to access the metablogapi page, but will not be able to make a request, but the page should not be visible

namespace Subtext.Framework.XmlRpc{
    /// <summary>
    /// Implements the MetaBlog API.
    /// </summary>
    public class MetaWeblog : SubtextXmlRpcService, IMetaWeblog, IWordPressApi
    {
        static readonly Log Log = new Log();

        public MetaWeblog(ISubtextContext context) : this(context, context.ServiceLocator.GetService<IEntryPublisher>())
        {
        }

        public MetaWeblog(ISubtextContext context, IEntryPublisher entryPublisher)
            : base(context)
        {
            EntryPublisher = entryPublisher;
        }

        protected IEntryPublisher EntryPublisher
        {
            get; 
            private set;
        }

        private Entry GetBlogPost(string pageId)
        {
            Entry entry = Repository.GetEntry(Int32.Parse(pageId, CultureInfo.InvariantCulture), false /*activeOnly*/, true /*includeCategories*/);
            if(entry == null)
            {
                return null;
            }
            entry.Blog = Blog;
            return entry;
        }

        public BlogInfo[] getUsersBlogs(string appKey, string username, string password)
        {
            Blog info = Blog;
            ValidateUser(username, password, info.AllowServiceAccess);

            var bi = new[] {
                new BlogInfo
                {
                    blogid = info.Id.ToString(CultureInfo.InvariantCulture),
                    blogName = info.Title,
                    url = Url.BlogUrl().ToFullyQualifiedUrl(info).ToString()
                }
            };
            return bi;
        }

        public bool deletePost(string appKey, string postid, string username, string password,
                               [XmlRpcParameter(
                                   Description =
                                   "Where applicable, this specifies whether the blog should be republished after the post has been deleted."
                                   )] bool publish)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            try
            {
                Repository.DeleteEntry(Int32.Parse(postid, CultureInfo.InvariantCulture));
                return true;
            }
            catch
            {
                throw new XmlRpcFaultException(1,
                                               String.Format(CultureInfo.InvariantCulture,
                                                             Resources.XmlRpcFault_CannotDeletePost, postid));
            }
        }

        public bool editPost(string postid, string username, string password, Post post, bool publish)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            Entry entry = GetBlogPost(postid);
            if(entry != null)
            {
                entry.Author = Blog.Author;
                entry.Email = Blog.Email;
                entry.Categories.Clear();
                post.CopyValuesTo(entry);
                entry.IncludeInMainSyndication = true;
                entry.PostType = PostType.BlogPost;

                //User trying to change future dating.
                if(publish && post.dateCreated != null &&
                   Blog.TimeZone.IsInFuture(post.dateCreated.Value, TimeZoneInfo.Utc))
                {
                    entry.DateSyndicated = post.dateCreated.Value;
                }
                entry.IsActive = publish;

                entry.DateModified = Blog.TimeZone.Now;

                EntryPublisher.Publish(entry);
                
                if(entry.Enclosure == null)
                {
                    if(post.enclosure != null)
                    {
                        Components.Enclosure enclosure = post.enclosure.Value.CopyValuesToEnclosure();
                        enclosure.EntryId = entry.Id;
                        Repository.Create(enclosure);
                    }
                }
                else // if(entry.Enclosure != null)
                {
                    if(post.enclosure != null)
                    {
                        Components.Enclosure enclosure = entry.Enclosure;
                        post.enclosure.Value.CopyValuesTo(enclosure);
                        Repository.Update(enclosure);
                    }
                    else
                    {
                        Repository.DeleteEnclosure(entry.Enclosure.Id);
                    }
                }
            }
            return true;
        }

        public Post getPost(string postid, string username, string password)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            Entry entry = GetBlogPost(postid);
            if(entry == null)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_CouldNotFindEntry);
            }
            var post = new Post
            {
                link = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                description = entry.Body,
                excerpt = entry.Description ?? string.Empty,
                dateCreated = entry.DateCreated,
                postid = entry.Id,
                title = entry.Title,
                permalink = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                categories = new string[entry.Categories.Count]
            };

            if(entry.Enclosure != null)
            {
                post.enclosure = new Enclosure
                {
                    length = (int)entry.Enclosure.Size,
                    type = entry.Enclosure.MimeType,
                    url = entry.Enclosure.Url
                };
            }

            if(entry.HasEntryName)
            {
                post.wp_slug = entry.EntryName;
            }

            entry.Categories.CopyTo(post.categories, 0);

            return post;
        }

        public Post[] getRecentPosts(string blogid, string username, string password, int numberOfPosts)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            ICollection<Entry> entries = Repository.GetEntries(numberOfPosts, PostType.BlogPost, PostConfig.IsActive, true);

            IEnumerable<Post> posts = from entry in entries
                                      select new Post
                                      {
                                          dateCreated = entry.DateCreated,
                                          description = entry.Body,
                                          excerpt = entry.Description,
                                          link = Url.EntryUrl(entry),
                                          permalink = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                                          title = entry.Title,
                                          postid = entry.Id.ToString(CultureInfo.InvariantCulture),
                                          userid = entry.Body.GetHashCode().ToString(CultureInfo.InvariantCulture),
                                          wp_slug = (entry.HasEntryName ? entry.EntryName : null),
                                          categories = (entry.Categories ?? new string[0]).ToArray(),
                                          enclosure = (entry.Enclosure == null
                                                           ? new Enclosure()
                                                           : new Enclosure
                                                           {
                                                               length = (int)entry.Enclosure.Size,
                                                               url = entry.Enclosure.Url,
                                                               type = entry.Enclosure.MimeType
                                                           })
                                      };

            return posts.ToArray();
        }

        public CategoryInfo[] getCategories(string blogid, string username, string password)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            ICollection<LinkCategory> categories = Repository.GetCategories(CategoryType.PostCollection, false);
            if(categories == null)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_NoCategories);
            }

            IEnumerable<CategoryInfo> categoryInfos = from category in categories
                                                      select new CategoryInfo
                                                      {
                                                          categoryid =
                                                              category.Id.ToString(CultureInfo.InvariantCulture),
                                                          title = category.Title,
                                                          htmlUrl =
                                                              Url.CategoryUrl(category).ToFullyQualifiedUrl(Blog).
                                                              ToString(),
                                                          rssUrl =
                                                              Url.CategoryRssUrl(category).ToFullyQualifiedUrl(Blog).
                                                              ToString(),
                                                          description = category.Title
                                                      };

            return categoryInfos.ToArray();
        }

        /// <summary>
        /// Creates a new post.  The publish boolean is used to determine whether the item 
        /// should be published or not.
        /// </summary>
        /// <param name="blogid">The blogid.</param>
        /// <param name="username">The username.</param>
        /// <param name="password">The password.</param>
        /// <param name="post">The post.</param>
        /// <param name="publish">if set to <c>true</c> [publish].</param>
        /// <returns></returns>
        public string newPost(string blogid, string username, string password, Post post, bool publish)
        {
            return PostContent(username, password, ref post, publish, PostType.BlogPost);
        }

        public mediaObjectInfo newMediaObject(object blogid, string username, string password, mediaObject mediaobject)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);
            string imageDirectory = Url.ImageDirectoryPath(Blog);
            try
            {
                // newMediaObject allows files to be overwritten
                // The media object's name can have extra folders appended so we check for this here too.
                FileHelper.EnsureDirectory(Path.Combine(imageDirectory,
                                                        mediaobject.name.Substring(0,
                                                                                   mediaobject.name.LastIndexOf("/",
                                                                                                                StringComparison
                                                                                                                    .
                                                                                                                    Ordinal) +
                                                                                   1).Replace("/", "\\")));
                string imageFilePhysicalPath = Path.Combine(imageDirectory, mediaobject.name);
                FileHelper.WriteBytesToFile(imageFilePhysicalPath, mediaobject.bits);
            }
            catch(IOException)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_ErrorSavingFile);
            }

            mediaObjectInfo media;
            media.url = Url.ImageUrl(Blog, mediaobject.name);
            return media;
        }

        // w.bloggar workarounds/nominal MT support - HACKS

        // w.bloggar is not correctly implementing metaWeblogAPI on its getRecentPost call, it wants 
        // an instance of blogger.getRecentPosts at various time. 
        // 
        // What works better with w.bloggar is to tell it to use MT settings. For w.bloggar users 
        // with metaWeblog configured, we'll throw a more explanatory exception than method not found.

        // Wordpress API

        #region IWordPressApi Members

        public int newCategory(string blogid, string username, string password, WordpressCategory category)
        {
            var newCategory = new LinkCategory
            {
                CategoryType = CategoryType.PostCollection,
                Title = category.name,
                IsActive = true,
                Description = category.name
            };

            newCategory.Id = Links.CreateLinkCategory(newCategory);

            return newCategory.Id;
        }

        public int newPage(string blog_id, string username, string password, Post content, bool publish)
        {
            return Convert.ToInt32(PostContent(username, password, ref content, publish, PostType.Story),
                                   CultureInfo.InvariantCulture);
        }

        public int editPage(string blog_id, string page_id, string username, string password, Post content, bool publish)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            Entry entry = GetBlogPost(page_id);
            if(entry != null)
            {
                entry.Author = Blog.Author;
                entry.Email = Blog.Email;
                entry.Body = content.description;
                entry.Title = content.title;
                entry.Description = content.excerpt ?? string.Empty;
                entry.IncludeInMainSyndication = true;

                if(content.categories != null)
                {
                    entry.Categories.AddRange(content.categories);
                }

                entry.PostType = PostType.Story;
                entry.IsActive = publish;

                if(!string.IsNullOrEmpty(content.wp_slug))
                {
                    entry.EntryName = content.wp_slug;
                }

                entry.DateModified = Blog.TimeZone.Now;
                EntryPublisher.Publish(entry);
            }
            return Convert.ToInt32(page_id, CultureInfo.InvariantCulture);
        }

        public Post[] getPages(string blog_id, string username, string password, int numberOfPosts)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            ICollection<Entry> entries = Repository.GetEntries(numberOfPosts, PostType.Story, PostConfig.IsActive, true);
            IEnumerable<Post> posts = from entry in entries
                                      select new Post
                                      {
                                          dateCreated = entry.DateCreated,
                                          description = entry.Body,
                                          excerpt = entry.Description ?? string.Empty,
                                          link = Url.EntryUrl(entry),
                                          permalink = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                                          title = entry.Title,
                                          postid = entry.Id.ToString(CultureInfo.InvariantCulture),
                                          userid = entry.Body.GetHashCode().ToString(CultureInfo.InvariantCulture),
                                          wp_slug = (entry.HasEntryName ? entry.EntryName : null),
                                          categories = (entry.Categories ?? new string[0]).ToArray(),
                                          enclosure = (entry.Enclosure == null
                                                           ? new Enclosure()
                                                           : new Enclosure
                                                           {
                                                               length = (int)entry.Enclosure.Size,
                                                               url = entry.Enclosure.Url,
                                                               type = entry.Enclosure.MimeType
                                                           })
                                      };

            return posts.ToArray();
        }

        public Post getPage(string blog_id, string page_id, string username, string password)
        {
            Blog info = Blog;
            ValidateUser(username, password, info.AllowServiceAccess);

            Entry entry = GetBlogPost(page_id);
            var post = new Post
            {
                link = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                description = entry.Body,
                excerpt = entry.Description ?? string.Empty,
                dateCreated = entry.DateCreated,
                postid = entry.Id,
                title = entry.Title,
                permalink = Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(),
                categories = new string[entry.Categories.Count]
            };
            entry.Categories.CopyTo(post.categories, 0);
            if(entry.HasEntryName)
            {
                post.wp_slug = entry.EntryName;
            }
            if(entry.Enclosure != null)
            {
                post.enclosure = new Enclosure
                {
                    length = (int)entry.Enclosure.Size,
                    type = entry.Enclosure.MimeType,
                    url = entry.Enclosure.Url
                };
            }

            return post;
        }

        public bool deletePage(string blog_id, string username, string password, string page_id)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            try
            {
                Repository.DeleteEntry(Int32.Parse(page_id, CultureInfo.InvariantCulture));
                return true;
            }
            catch
            {
                throw new XmlRpcFaultException(1,
                                               String.Format(CultureInfo.InvariantCulture,
                                                             Resources.XmlRpcFault_CannotDeletePage, page_id));
            }
        }

        #endregion

        private void ValidateUser(string username, string password, bool allowServiceAccess)
        {
            if(!Config.Settings.AllowServiceAccess || !allowServiceAccess)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_WebServiceNotEnabled);
            }

            bool isValid = SecurityHelper.IsValidUser(Blog, username, password);
            if(!isValid)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_UsernameAndPasswordInvalid);
            }
        }

        private string PostContent(string username, string password, ref Post post, bool publish, PostType postType)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            var entry = new Entry(postType) {PostType = postType, IsActive = publish, Author = Blog.Author, Email = Blog.Email};
            post.CopyValuesTo(entry);
            entry.AllowComments = true;
            entry.DisplayOnHomePage = true;

            DateTime dateTimeInPost = post.dateCreated != null ? post.dateCreated.Value : DateTime.UtcNow;
            // Store in the blog's timezone
            dateTimeInPost = Blog.TimeZone.FromUtc(dateTimeInPost);

            entry.DateCreated = entry.DateModified = Blog.TimeZone.Now;
            if(publish)
            {
                entry.DateSyndicated = dateTimeInPost;
            }

            entry.IncludeInMainSyndication = true;
            entry.IsAggregated = true;
            entry.SyndicateDescriptionOnly = false;

            int postId;
            try
            {
                //TODO: Review whether keywords should be true.
                postId = EntryPublisher.Publish(entry);
                if(Blog.TrackbacksEnabled)
                {
                    NotificationServices.Run(entry, Blog, Url);
                }

                if(post.enclosure != null)
                {
                    Components.Enclosure enclosure = post.enclosure.Value.CopyValuesToEnclosure();
                    enclosure.EntryId = postId;
                    Repository.Create(enclosure);
                }

                AddCommunityCredits(entry);
            }
            catch(Exception e)
            {
                throw new XmlRpcFaultException(0, e.Message + " " + e.StackTrace);
            }
            if(postId < 0)
            {
                throw new XmlRpcFaultException(0, Resources.XmlRpcFault_AddPostFailed);
            }
            return postId.ToString(CultureInfo.InvariantCulture);
        }

        private void AddCommunityCredits(Entry entry)
        {
            try
            {
                CommunityCreditNotification.AddCommunityCredits(entry, Url, Blog);
            }
            catch(CommunityCreditNotificationException ex)
            {
                Log.WarnFormat(Resources.XmlRpcWarn_CommunityCredits,
                               Url.EntryUrl(entry).ToFullyQualifiedUrl(Blog).ToString(), ex.Message);
            }
            catch(Exception ex)
            {
                Log.Error(Resources.XmlRpcError_CommunityCredits, ex);
            }
        }

        [XmlRpcMethod("mt.getCategoryList",
            Description = "Gets a list of active categories for a given blog as an array of MT category struct.")]
        public MtCategory[] GetCategoryList(string blogid, string username, string password)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            ICollection<LinkCategory> lcc = Repository.GetCategories(CategoryType.PostCollection, false);
            if(lcc == null)
            {
                throw new XmlRpcFaultException(0, "No categories exist");
            }

            var categories = new MtCategory[lcc.Count];
            int i = 0;
            foreach(LinkCategory linkCategory in lcc)
            {
                var category = new MtCategory(linkCategory.Id.ToString(CultureInfo.InvariantCulture),
                                               linkCategory.Title);
                categories[i] = category;
                i++;
            }
            return categories;
        }

        [XmlRpcMethod("mt.setPostCategories",
            Description = "Sets the categories for a given post.")]
        public bool SetPostCategories(string postid, string username, string password,
                                      MtCategory[] categories)
        {
            ValidateUser(username, password, Blog.AllowServiceAccess);

            if(categories != null && categories.Length > 0)
            {
                int postId = Int32.Parse(postid, CultureInfo.InvariantCulture);

                IEnumerable<int> categoryIds = from category in categories
                                               select int.Parse(category.categoryId, CultureInfo.InvariantCulture);

                if(categoryIds.Any())
                {
                    Repository.SetEntryCategoryList(postId, categoryIds);
                }
            }

            return true;
        }

        [XmlRpcMethod("mt.getPostCategories",
            Description = "Sets the categories for a given post.")]
        public MtCategory[] GetPostCategories(string postid, string userName, string password)
        {
            ValidateUser(userName, password, Blog.AllowServiceAccess);

            int postId = Int32.Parse(postid, CultureInfo.InvariantCulture);
            ICollection<Link> postCategories = Repository.GetLinkCollectionByPostId(postId);
            var categories = new MtCategory[postCategories.Count];
            if(postCategories.Count > 0)
            {
                // REFACTOR: Might prefer seeing a dictionary come back straight from the provider.
                // for now we'll build our own catid->catTitle lookup--we need it below bc collection
                // from post is going to be null for title.
                ICollection<LinkCategory> cats = Repository.GetCategories(CategoryType.PostCollection, false);
                var catLookup = new Hashtable(cats.Count);
                foreach(LinkCategory currentCat in cats)
                {
                    catLookup.Add(currentCat.Id, currentCat.Title);
                }

                int i = 0;
                foreach(Link link in postCategories)
                {
                    var category = new MtCategory(link.CategoryId.ToString(CultureInfo.InvariantCulture),
                                                   (string)catLookup[link.CategoryId]);

                    categories[i] = category;
                    i++;
                }
            }

            return categories;
        }

        /// <summary>
        /// Retrieve information about the text formatting plugins supported by the server.
        /// </summary>
        /// <returns>
        /// an array of structs containing String key and String label. 
        /// key is the unique string identifying a text formatting plugin, 
        /// and label is the readable description to be displayed to a user. 
        /// key is the value that should be passed in the mt_convert_breaks 
        /// parameter to newPost and editPost.
        /// </returns>
        [XmlRpcMethod("mt.supportedTextFilters",
            Description = "Retrieve information about the text formatting plugins supported by the server.")]
        public MtTextFilter[] GetSupportedTextFilters()
        {
            return new[] {new MtTextFilter("test", "test"),};
        }

        #region Nested type: BloggerPost

        public struct BloggerPost
        {
            public string content;
            public DateTime dateCreated;
            public string postid;
            public string userid;
        }

        #endregion

        // we'll also add a couple structs and methods to give us nominal MT API-level support.
        // by doing this we'll allow w.bloggar to run against .Text using w.b's MT configuration.

        #region Nested type: MtCategory

        public struct MtCategory
        {
            public string categoryId;
            [XmlRpcMissingMapping(MappingAction.Ignore)] public string categoryName;
            [XmlRpcMissingMapping(MappingAction.Ignore)] public bool isPrimary;

            /// <summary>
            /// Initializes a new instance of the <see cref="MtCategory"/> class.
            /// </summary>
            /// <param name="category">The category.</param>
            public MtCategory(string category)
            {
                categoryId = category;
                categoryName = category;
                isPrimary = false;
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="MtCategory"/> class.
            /// </summary>
            /// <param name="id">The id.</param>
            /// <param name="category">The category.</param>
            public MtCategory(string id, string category)
            {
                categoryId = id;
                categoryName = category;
                isPrimary = false;
            }
        }

        #endregion

        #region Nested type: MtTextFilter

        /// <summary>
        /// Represents a text filter returned by mt.supportedTextFilters.
        /// </summary>
        [XmlRpcMissingMapping(MappingAction.Ignore)]
        public struct MtTextFilter
        {
            public string key;
            public string label;

            /// <summary>
            /// Initializes a new instance of the <see cref="MtTextFilter"/> class.
            /// </summary>
            /// <param name="key">The key.</param>
            /// <param name="label">The label.</param>
            public MtTextFilter(string key, string label)
            {
                this.key = key;
                this.label = label;
            }
        }

        #endregion
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.