Разработка веб-части автогенерации кода проекта Project Server 2013 (Часть 1)
Статья Андрея Путина (Компания Бастион-Интегратор), первоначально опубликованная здесь.
Или как получить строку подключения к БД Project Server 2013?
В этой статье мы начнем разрабатывать веб-часть автогенерации уникального кода проекта и разберем следующие вопросы:
- Написание простейшей веб-части
- Получение строки подключения к БД Project Server 2013.
При разработке кастомного расширения для Project Server 2013 нередко возникает задача записи или чтения из БД Project Server 2013, при этом возникает вопрос как получить валидную строку подключения к БД, не выносить же ее в настройки или хуже того использовать «хардкод». Итак, для создания нашей веб-части выполним следующие шаги:
1. Создадим новый проект в Visual Studio и дадим обдуманное название, так как наша веб-часть будет генерировать Id проекта, то дадим «обдуманное» название «SampleProjectUniqueIDWebPart».
2. Выберем тип развертывания нашего решения. В данном примере я использую тип развертывания Deploy as a farm solution, так как моя веб-часть будет содержать UserControl.
3. Добавим References (ссылки) на сборки в GAC:
- Microsoft.Office.Project.Server.PWA.dll
- Microsoft.Office.Project.Server.Library.dll
- Microsoft.Office.Project.Schema.dll
для лентяев J
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Office.Project.Schema\v4.0_15.0.0.0__71e9bce111e9429c\Microsoft.Office.Project.Schema.dll
C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Office.Project.Server.Library\v4.0_15.0.0.0__71e9bce111e9429c\Microsoft.Office.Project.Server.Library.dll
C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Microsoft.Office.Project.Server.PWA\v4.0_15.0.0.0__71e9bce111e9429c\Microsoft.Office.Project.Server.PWA.dll
4. После создания нового проекта добавим новый элемент (Add New Item) нашу веб-часть.
5. В разделе Office/SharePoint выберем Web-Part и дадим имя нашей веб-части
6. Перейдем в SolutionExplorer и развернем добавленную веб-часть.
7. Выберем элемент ProjectUniqueIDWebPart.webpart откроем его двойным нажатием мыши и откорректируем свойства веб-части в разделе xml схемы <properties>
<property name="Title" type="string"> Project Unique ID WebPart</property>
<property name="Description" type="string"> Веб-часть автоматической генерации Id проекта</property>
8. Наследуем класс веб-части ProjectUniqueIDWebPart от класса PWAPart
9. Откорректируем раздел Using:
- using System;
- using System.Web.UI;
- using System.Linq;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using Microsoft.SharePoint.WebPartPages;
- using Microsoft.Office.Project.Server.Schema;
- using Microsoft.Office.Project.PWA;
- using System.Web;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Utilities;
- using System.Xml;
- using System.Drawing;
- using System.Collections.Generic;
- using Microsoft.Office.Project.PWA.WebParts;
- using Microsoft.Office.Project.Server.Library;
- using System.ComponentModel;
10. Создадим новый Class для отображения настроек веб-части дадим имя класса ProjectUniqueIDToolPart.cs.
11. Наследуем класс ProjectUniqueIDToolPart.cs от класса ToolPart.
public class ProjectUniqueIDToolPart : ToolPart
{
}
12. Добавим новый элемент проекта (Add New Item) User Control в разделе Office/SharePoint
13. Заготовка под нашу веб-часть готова, теперь перейдем к маленьким заделам на будущее:
Для начала напишем класс ToolPart для сохранения настроек ProjectUniqueIDToolPart.cs.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Microsoft.SharePoint.WebPartPages;
- using System.Web.UI.WebControls;
- namespace SampleProjectUniqueIDWebPart.ProjectUniqueIDWebPart
- {
- public class ProjectUniqueIDToolPart : ToolPart
- {
- #region Members
- Panel toolPartPanel;
- private TextBox _txtCustomFieldUid;
- private ProjectUniqueIDWebPart _webpart;
- #endregion
- #region ToolPart Events
- protected override void CreateChildControls()
- {
- _webpart = ParentToolPane.SelectedWebPart
- as ProjectUniqueIDWebPart;
- toolPartPanel = new Panel();
- toolPartPanel.ID = «toolPartPanel»;
- _txtCustomFieldUid = new TextBox();
- if (_webpart != null)
- {
- _txtCustomFieldUid.Text = _webpart.DefaultCustomFieldUid;
- _txtCustomFieldUid.Width = 200;
- }
- toolPartPanel.Controls.Add(_txtCustomFieldUid);
- Controls.Add(toolPartPanel);
- base.CreateChildControls();
- }
- public override void ApplyChanges()
- {
- base.ApplyChanges();
- _webpart.DefaultCustomFieldUid = _txtCustomFieldUid.Text;
- }
- #endregion
- }
- }
После чего отредактируем класс ProjectUniqueIDWebPart.
- using System;
- using System.Web.UI;
- using System.Linq;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using Microsoft.SharePoint.WebPartPages;
- using Microsoft.Office.Project.Server.Schema;
- using Microsoft.Office.Project.PWA;
- using System.Web;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Utilities;
- using System.Xml;
- using System.Drawing;
- using System.Collections.Generic;
- using Microsoft.Office.Project.PWA.WebParts;
- using Microsoft.Office.Project.Server.Library;
- using System.ComponentModel;
- using SampleProjectUniqueIDWebPart.ControlTemplates.SampleProjectUniqueIDWebPart;
- namespace SampleProjectUniqueIDWebPart.ProjectUniqueIDWebPart
- {
- [ToolboxItemAttribute(false)]
- public class ProjectUniqueIDWebPart : PWAPart
- {
- private const string _ascxPath = @«~/_CONTROLTEMPLATES/15/SampleProjectUniqueIDWebPart/ProjectUniqueIDUserControl.ascx»;
- public string DefaultCustomFieldUid { get; set; }
- protected override void PWA_OnLoad(EventArgs e)
- {
- this.Height = «100%»;
- base.PWA_OnLoad(e);
- }
- protected override void PWA_CreateChildControls()
- {
- ProjectUniqueIDUserControl control = (ProjectUniqueIDUserControl)Page.LoadControl(_ascxPath); //Инициализация пользовательского контрола
- control.ParentWP = this;
- control.DefaultCustomFieldUid = DefaultCustomFieldUid;
- Controls.Add(control);//Добавляем контрол
- base.PWA_CreateChildControls();//Вызваем метод базового класса
- }
- public override Microsoft.SharePoint.WebPartPages.ToolPart[] GetToolParts()
- {
- ToolPart[] allToolParts = new ToolPart[3]; //Инициализации массива панелей инструментов свойств веб-части
- WebPartToolPart standardToolParts = new WebPartToolPart(); //Стандартная панель инеструментов
- CustomPropertyToolPart customToolParts = new CustomPropertyToolPart();
- allToolParts[0] = standardToolParts;
- allToolParts[1] = customToolParts;
- allToolParts[2] = new ProjectUniqueIDToolPart //Добавляем нашу расширенную панель инструментов
- {
- Title = «Корпоративное настраиваемое поле по умолчанию»
- };
- return allToolParts;
- }
- }
- }
Отредактируем элемент ProjectUniqueIDUserControl.ascx выбрав его двойным щелчком Solution Explorer. Добавим в следующую строку:
- <asp:Label ID=«lblMessage» runat=«server» Text=«NA» style=«font-weight: 700″></asp:Label>.
После чего отредактируем частичный класс ProjectUniqueIDUserControl.ascx.cs
- using System;
- using System.Web.UI;
- using System.Linq;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using Microsoft.SharePoint.WebPartPages;
- using Microsoft.Office.Project.Server.Schema;
- using Microsoft.Office.Project.PWA;
- using System.Web;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Utilities;
- using System.Xml;
- using System.Drawing;
- using System.Collections.Generic;
- using Microsoft.Office.Project.PWA.WebParts;
- using Microsoft.Office.Project.Server.Library;
- using System.Reflection;
- namespace SampleProjectUniqueIDWebPart.ControlTemplates.SampleProjectUniqueIDWebPart
- {
- public partial class ProjectUniqueIDUserControl : UserControl
- {
- protected Label lblMessage;
- public ProjectUniqueIDWebPart.ProjectUniqueIDWebPart ParentWP;
- public string DefaultCustomFieldUid { get; set; }
- protected void Page_Load(object sender, EventArgs e)
- {
- lblMessage.Text = GetDbConnectionString(PJContext.Current.Site.ID);
- }
- public string GetDbConnectionString(Guid pwaSiteId)
- {
- return “”;
- }
- }
- }
Вроде все готово, теперь осталось понять, как получить строку подключения? Так как данной функции нет в документации к стандартному API, я использую инструмент реверс-инжиниринга Reflector для анализа сборок Project Server 2013.
Проанализировав код, мы понимаем, что класс ProjectSite internal sealed class, в котором содержится нужный нам метод, а это означает что в «лоб» его не использовать, но мы не будем вешать нос, а воспользуемся рефлексией. После небольших «мытарств» наш с вами класс выглядит теперь так:
- using System;
- using System.Web.UI;
- using System.Linq;
- using System.Web.UI.WebControls;
- using System.Web.UI.WebControls.WebParts;
- using Microsoft.SharePoint.WebPartPages;
- using Microsoft.Office.Project.Server.Schema;
- using Microsoft.Office.Project.PWA;
- using System.Web;
- using Microsoft.SharePoint;
- using Microsoft.SharePoint.Utilities;
- using System.Xml;
- using System.Drawing;
- using System.Collections.Generic;
- using Microsoft.Office.Project.PWA.WebParts;
- using Microsoft.Office.Project.Server.Library;
- using System.Reflection;
- namespace SampleProjectUniqueIDWebPart.ControlTemplates.SampleProjectUniqueIDWebPart
- {
- public partial class ProjectUniqueIDUserControl : UserControl
- {
- protected Label lblMessage;
- public ProjectUniqueIDWebPart.ProjectUniqueIDWebPart ParentWP;
- public string DefaultCustomFieldUid { get; set; }
- protected void Page_Load(object sender, EventArgs e)
- {
- lblMessage.Text = GetDbConnectionString(PJContext.Current.Site.ID);
- }
- private static object GetProjectSite(Guid pwaSiteId)
- {
- Type psiServiceType = Type.GetType(«Microsoft.Office.Project.Server.Administration.PsiService, Microsoft.Office.Project.Server.Administration, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c»);
- Microsoft.SharePoint.Administration.SPService psiService = (Microsoft.SharePoint.Administration.SPService)psiServiceType.GetProperty(«Local», BindingFlags.Static | BindingFlags.Public).GetValue(null, new object[0]);
- Microsoft.SharePoint.Administration.SPServiceApplication psiServiceApplication = psiService.Applications.Where(item => item.GetType().Name == «PsiServiceApplication»).FirstOrDefault();
- object projectSiteCollection = psiServiceApplication.GetType().GetProperty(«SiteCollection»).GetValue(psiServiceApplication, new object[0]);
- object projectSite = projectSiteCollection.GetType().GetMethod(«GetProjectSite», new Type[] { typeof(Guid) }).Invoke(projectSiteCollection, new object[] { pwaSiteId });
- return projectSite;
- }
- public string GetDbConnectionString(Guid pwaSiteId)
- {
- object projectSite = GetProjectSite(pwaSiteId);
- Microsoft.SharePoint.Administration.SPDatabase projectDataBase = (Microsoft.SharePoint.Administration.SPDatabase)projectSite.GetType().GetProperty(«ProjectServiceDatabase»).GetValue(projectSite, new object[0]);
- return projectDataBase.DatabaseConnectionString;
- }
- }
- }
« Билдим » наш проект и разворачиваем на тестовую ферму.
Проверяем работоспособность нашей веб-части.
Для этого открываем PWA переходим в Центре проектов на тестовый проект, нажимаем отредактировать страницу «Карточки проекта».
Добавляем нашу веб-часть на страницу.
Ура! Все работает. Мы теперь научились получать connection string.
В следующей части мы будем реализовывать логику генерации ID проекта и записи его в настраиваемое поле проекта.