Canalblog
Editer l'article Suivre ce blog Administration + Créer mon blog
Publicité
asp.net
Publicité
Archives
19 février 2010

Percussion rhythmyx CMS Web Service and c#

I've been asked by a client to implement a module that request Percussion Rhythmyx CMS Web Service to search for some documents and get their published url.

I had a big issue because the existing Rhythmyx Web Service does not provide the document's published url. They just provide the Assembly url. I had to implement a small algorithm to get the assembly url and build the published url:

1. I call the FindItem service to get services details
2. I call the GetAssemblyUrls service to get the assembly url of the found items
3. For each Item, I call via HTTP the AssemblyUrl with a specific snipet
4. I parse the returned snipet html content to pick the Published uri
5. I concatenete the resulted uri with the server base url to obtain the full publish http url.

Here is the source code:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.Services.Protocols;
using PROJ.Common.WSRhythmyx;

namespace PROJ.Common.Rhythmyx
{   
    /// <summary>
    /// Wrapper class to access CMS-Rhythmyx web services
    /// </summary>
    public class RhythmyxManager : IDisposable
    {
        #region Properties
       
        /// <summary>
        /// Security service to login and logout
        /// </summary>
        private securitySOAP _svcSecurity;

        /// <summary>
        /// Content service to get content data (search/get items)
        /// </summary>
        private contentSOAP _svcContent;

        /// <summary>
        /// Contains all RhythmyxManager configuration data
        /// </summary>
        private RhythmyxConfig _config;
        #endregion Properties

        #region Constructor
        /// <summary>
        /// Initialize config data
        /// </summary>
        public RhythmyxManager()
        {
            _config = new RhythmyxConfig();
            _svcSecurity = new securitySOAP();
            _svcSecurity.Url = _buildAddress(_svcSecurity.Url);

        }
        #endregion Constructor

        #region Public Methods
        /// <summary>
        /// Calls the login service to get the session ID
        /// and build the proxy header to transmit the sessionID
        /// </summary>
        public void Login()
        {          
            LoginResponse loginResponse = null;
            try
            {
                //Call Service
                loginResponse = _svcSecurity.Login(_getLoginRequest());

            }
            catch (SoapException ex)
            {
                _logException(ex, "login");
                throw new CMSServiceCallException(ex);
            }
            catch (System.Net.WebException ex)
            {
                _logException(ex, "login");
                throw new CMSNotAccessibleException(ex);
            }
            catch (Exception ex)
            {
                _logException(ex, "login");
                throw new UnSpecifiedCMSException(ex);
            }

            //Create ContentService
            _svcContent = _createContentService(loginResponse.PSLogin.sessionId);

        }

        /// <summary>
        /// Logout the Rhythmyx session
        /// </summary>
        public void Logout()
        {
            if (_svcContent != null && _svcSecurity != null)
            {
                LogoutRequest logoutReq = new LogoutRequest();
                logoutReq.SessionId = _svcContent.PSAuthenticationHeaderValue.Session;

                //Call Service
                try
                {
                    _svcSecurity.Logout(logoutReq);
                }
                catch (SoapException ex)
                {
                    _logException(ex, "logout");
                    throw new CMSServiceCallException(ex);
                }
                catch (System.Net.WebException ex)
                {
                    _logException(ex, "logout");
                    throw new CMSNotAccessibleException(ex);
                }
                catch (Exception ex)
                {
                    _logException(ex, "logout");
                    throw new UnSpecifiedCMSException(ex);
                }
            }
        }

      

        /// <summary>
        /// Call FindItems webs service, and get details for each found items
        /// Search is executed on title field OR on sys_contentid field (Item ID)
        /// Search extract only pdf files
        /// </summary>
        /// <param name="searchParams">Search criterias</param>
        /// <param name="getUrls">
        ///     If false, does not extract published url -> to optimize response time
        /// Url can be extracted later with the GetItemUrl method
        /// </param>
        /// <returns>Items found</returns>
        public RhythmyxItems.RhythmyxDataTable FindItems(string title, string id, string fileName, bool getUrls)
        {
            if (string.IsNullOrEmpty(title) && string.IsNullOrEmpty(id) && string.IsNullOrEmpty(fileName)) throw new CMSEmptySearchCriteriaException();

            RhythmyxItems.RhythmyxDataTable res = new RhythmyxItems.RhythmyxDataTable();         

            #region Set search params
            PSSearchField[] searchParams = new PSSearchField[2];
            //Set service method params
            FindItemsRequest req = new FindItemsRequest();
            req.loadOperations = true;
            req.PSSearch = new PSSearch();
            req.PSSearch.PSSearchParams = new PSSearchParams();
            req.PSSearch.useDbCaseSensitivity = false;
            req.PSSearch.PSSearchParams.Parameter = searchParams;

            ///PDF only search
            searchParams[0] = _createParam("sys_suffix", operatorTypes.equal, ".pdf", connectorTypes.and);

            ///Title search criteria
            if (!string.IsNullOrEmpty(title))
            {
                req.PSSearch.PSSearchParams.Title = new PSSearchParamsTitle();
                req.PSSearch.PSSearchParams.Title.Value = "%" + title + "%";
                req.PSSearch.PSSearchParams.Title.Operator = operatorTypes.like;
                req.PSSearch.PSSearchParams.Title.Connector = connectorTypes.and;
            }
            else if (!string.IsNullOrEmpty(id))
            {
                searchParams[1] = _createParam("sys_contentid", operatorTypes.equal, id, connectorTypes.and);
            }
            else
            {
                searchParams[1] = _createParam("filename", operatorTypes.like, "%" + fileName + "%", connectorTypes.and);
            }
            #endregion Set search params

            #region Create returned structure
            req.PSSearch.PSSearchParams.SearchResults = new PSSearchParamsSearchResults();
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField = new PSSearchResultField[10];
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[0] = _setSearchParamField("author");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[1] = _setSearchParamField("displaytitle");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[2] = _setSearchParamField("sys_title");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[3] = _setSearchParamField("filename");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[4] = _setSearchParamField("sys_lang");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[5] = _setSearchParamField("description");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[6] = _setSearchParamField("item_file_attachment_size");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[7] = _setSearchParamField("sys_suffix");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[8] = _setSearchParamField("reference");
            req.PSSearch.PSSearchParams.SearchResults.PSSearchResultField[9] = _setSearchParamField("sys_currentview");
            #endregion

            #region Call Service
            PSSearchResults[] list = null;
            try
            {
                list = _svcContent.FindItems(req);
            }
            catch (SoapException ex)
            {
                _logException(ex, "FindItems");
                throw new CMSServiceCallException(ex);
            }
            catch (System.Net.WebException ex)
            {
                _logException(ex, "FindItems");
                throw new CMSNotAccessibleException(ex);
            }
            catch (Exception ex)
            {
                _logException(ex, "FindItems");
                throw new UnSpecifiedCMSException(ex);
            }
            #endregion Call Service

            #region Extract data from result structure
            foreach (PSSearchResults val in list)
            {
                RhythmyxItems.RhythmyxRow row = res.NewRhythmyxRow();               
                row.Name = val.name;
                row.ContentType = val.ContentType.name;

                              
                foreach (PSSearchResultsFields field in val.Fields)
                {
                    if (!string.IsNullOrEmpty(field.Value))
                    {
                        switch (field.name)
                        {
                            case "sys_contentid": { row.Id = Convert.ToInt64(field.Value); break; }
                            case "author": { row.Author = field.Value; break; }
                            case "displaytitle": { row.DisplayTitle = field.Value; break; }
                            case "sys_title": { row.SystemTitle = field.Value; break; }
                            case "filename": { row.FileName = field.Value; break; }
                            case "sys_lang": { row.Language = field.Value; break; }
                            case "description": { row.Description = field.Value; break; }
                            case "item_file_attachment_size": { row.Size = field.Value; break; }
                            case "sys_suffix": { row.Type = field.Value; break; }
                            case "reference": { row.Reference = field.Value; break; }
                            case "sys_currentview": { row.CurrentView = field.Value; break; }
                        }
                    }
                }
                res.AddRhythmyxRow(row);
            }
            int count = 0;
            long[] ids = new long[res.Count];
            foreach (RhythmyxItems.RhythmyxRow item in res)
            {
                ids[count] = item.Id;
                count++;
            }

            if (getUrls)
            {
                string[] intranetUrls = _getUrls(ids, EnvConf.EEnvironment.INTRANET);
                string[] internetUrls = _getUrls(ids, EnvConf.EEnvironment.INTERNET);

                count = 0;
                foreach (RhythmyxItems.RhythmyxRow item in res)
                {
                    item.IntranetUrl = intranetUrls[count];
                    item.InternetUrl = internetUrls[count];
                    count++;
                }
            }
            #endregion Extract data from result structure

            return res;            
        }

        /// <summary>
        /// Gets the Item's published URL
        /// </summary>
        /// <param name="id"></param>
        /// <param name="siteName"></param>
        /// <param name="environmentBaseUrl"></param>
        /// <returns></returns>
        public string GetItemUrl(long itemID, EnvConf.EEnvironment environment)
        {
            string res = "";

            #region Set services parameters
            GetAssemblyUrlsRequest req = new GetAssemblyUrlsRequest();
            req.Id = new long[] { itemID };
            req.Template = _config.TemplateName;
            req.Context = _config.ContextID;
            req.ItemFilter = _config.ItemFilter;
            req.Site = _config.GetEnvironment(environment).SiteName;
            #endregion Set services parameters

            #region Call Service
            GetAssemblyUrlsResponse resul = null;
            try
            {
                resul = _svcContent.GetAssemblyUrls(req);
            }
            catch (SoapException ex)
            {
                _logException(new CMSServiceCallException(ex), "GetItemUrl");
            }
            catch (System.Net.WebException ex)
            {
                _logException(ex, "GetItemUrl");
                throw new CMSNotAccessibleException(ex);
            }
            catch (Exception ex)
            {
                _logException(ex, "GetItemUrl");
                throw new UnSpecifiedCMSException(ex);
            }
            #endregion Call Service

            #region Get Url
            if (resul != null && resul.Urls != null && resul.Urls.Length > 0)
            {
                string snipetUrl = resul.Urls[0];
                string itemPublishedUri = _extractUrlFromHtml(snipetUrl);
                if (!string.IsNullOrEmpty(itemPublishedUri))
                    res = _config.GetEnvironment(environment).BaseUrl + itemPublishedUri;
            }
            #endregion

            return res;
        }
       
        #endregion Public Methods

        #region Private methods
        /// <summary>
        /// Create the login request iwth config data
        /// </summary>
        /// <returns>Login request containing authentication credentials</returns>
        private LoginRequest _getLoginRequest()
        {
            LoginRequest loginReq = new LoginRequest();
            loginReq.Username = ConfigHelper.RhythmyxUserName;
            loginReq.Password = ConfigHelper.RhythmyxPassword;
            loginReq.Community = ConfigHelper.RhythmyxCommunity;
            return loginReq;
        }

        /// <summary>
        /// Create the ContentService and sets its Authentication header with the session ID
        /// </summary>
        /// <param name="sessionID">Session ID to keep session open</param>
        /// <returns>Content Service</returns>
        private contentSOAP _createContentService(string sessionID)
        {
            contentSOAP svcContent = new contentSOAP();
            svcContent.Url = _buildAddress(svcContent.Url);
            svcContent.PSAuthenticationHeaderValue = new PSAuthenticationHeader();
            svcContent.PSAuthenticationHeaderValue.Session = sessionID;
            return svcContent;
        }

        /// <summary>
        /// Build the WebServiceAdress with the configuration keys
        /// </summary>
        /// <param name="srcAddress">default Service adress to be modified to get the configured address</param>
        /// <returns>Target service address</returns>
        private string _buildAddress(String srcAddress)
        {
            int pathStart = srcAddress.IndexOf("/Rhythmyx/");
            return ConfigHelper.RhythmyxWSUrl.Substring(0, pathStart) + srcAddress.Substring(pathStart);
        }
       
        /// <summary>
        /// Create a search param (search criteria and its value searched for) to provide to the findItem service
        /// </summary>
        /// <param name="name">Field name criteria</param>
        /// <param name="op">Operator</param>
        /// <param name="value">value searched for the specified field</param>
        /// <param name="connector">Connector as "or", "and"</param>
        /// <returns>Param object</returns>
        private PSSearchField _createParam(string name, operatorTypes op, string value, connectorTypes connector)
        {
            PSSearchField param = new PSSearchField();
            param.name = name;
            param.Operator = op;
            param.Value = value;
            param.Connector = connector;
            return param;
        }

        /// <summary>
        /// Logs an exception via LogHelper
        /// </summary>
        /// <param name="ex">Exception to log</param>
        /// <param name="operation">Operation that has aborted</param>
        private void _logException(Exception ex, string operation)
        {
            LogHelper.Log(this, "CMS " + operation + " failed", ex, LogHelper.ELogSeverity.Error);
        }

        /// <summary>
        /// Create search param field
        /// </summary>
        /// <param name="criteriaName"></param>
        private PSSearchResultField _setSearchParamField(string criteriaName)
        {
            PSSearchResultField res = new PSSearchResultField();
            res.name = criteriaName;
            return res;
        }

        /// <summary>
        /// Calls the Assembly URL Snipet and extract the Published URI from it
        /// </summary>
        /// <param name="snipetUrl">Url of the page containing the published URI</param>
        /// <returns>Published URI</returns>
        private string _extractUrlFromHtml(string snipetUrl)
        {
            string snipetContent = "";

            try
            {
                snipetContent = HttpHelper.GetHttpRequest(snipetUrl, ConfigHelper.RhythmyxUserName, ConfigHelper.RhythmyxPassword);
            }
            catch (System.Net.WebException ex)
            {
                _logException(ex, "GetSnipetContent");
                throw new CMSNotAccessibleException(ex);
            }
            catch (Exception ex)
            {
                _logException(ex, "GetSnipetContent");
                throw new UnSpecifiedCMSException(ex);
            }
            if (!string.IsNullOrEmpty(snipetContent))
            {
                string startString = "href=\"";
                snipetContent = snipetContent.Substring(snipetContent.IndexOf(startString) + startString.Length);
                return snipetContent.Substring(0, snipetContent.IndexOf("\""));
            }
            return "";
        }

        /// <summary>
        /// Calls the GetAssemblyUrls service to get the Item's assembly urls and construct the published urls
        /// </summary>
        /// <param name="ids"></param>
        /// <param name="templateName"></param>
        /// <param name="contextID"></param>
        /// <param name="itemfilter"></param>
        /// <param name="siteName"></param>
        /// <param name="environmentBaseUrl"></param>
        /// <returns></returns>
        private string[] _getUrls(long[] ids, EnvConf.EEnvironment environment)
        {
            string[] res = new string[ids.Length];

            #region Set services parameters
            GetAssemblyUrlsRequest req = new GetAssemblyUrlsRequest();
            req.Id = ids;
            req.Template = _config.TemplateName;
            req.Context = _config.ContextID;
            req.ItemFilter = _config.ItemFilter;
            req.Site = _config.GetEnvironment(environment).SiteName;
            //req.FolderPath = "//Sites/intranet/Assets/Docs";//"1394";
            #endregion Set services parameters

            #region Call Service
            GetAssemblyUrlsResponse resul = null;
            try
            {
                resul = _svcContent.GetAssemblyUrls(req);
            }
            catch (SoapException ex)
            {
                _logException(new CMSServiceCallException(ex), "GetItemUrl");
            }
            catch (System.Net.WebException ex)
            {
                _logException(ex, "GetItemUrl");
                throw new CMSNotAccessibleException(ex);
            }
            catch (Exception ex)
            {
                _logException(ex, "GetItemUrl");
                throw new UnSpecifiedCMSException(ex);
            }
            #endregion Call Service

            #region Get Urls
            if (resul != null && resul.Urls != null && resul.Urls.Length > 0)
            {
                for (int i = 0; i < ids.Length; i++)
                {
                    string snipetUrl = resul.Urls[i];
                    string itemPublishedUri = _extractUrlFromHtml(snipetUrl);
                    if(!string.IsNullOrEmpty(itemPublishedUri))
                        res[i] = _config.GetEnvironment(environment).BaseUrl + itemPublishedUri;
                }
            }
            #endregion

            return res;
        }

        #endregion Private methods

        #region IDisposable Members
        /// <summary>
        /// Dispose all web services proxy and session data
        /// </summary>
        public void Dispose()
        {
            //Dispose resources
            if (_svcSecurity != null)
            {
                _svcSecurity.Dispose();
                _svcSecurity = null;
            }
            if (_svcContent != null)
            {
                _svcContent.Dispose();
                _svcContent = null;
            }
        }

        #endregion
    }
}

Publicité
Publicité
Commentaires
Publicité