Sunday 9 October 2016

База знаний многофункционального центра

В отличие от форума, где можно обсудить новости, предложения и задать интересующие вопросы, база знаний - скорее справочник, в котором в простой и понятной форме зафиксированы факты, пошаговые инструкции и рекомендации.
В многофункциональных центрах база знаний создается для обучения сотрудников и повышения их квалификации.
Сегодня будет представлена структура реально существующей базы знаний и ее описание.
Схема отношений объектов базы знаний


Требование к программному обеспечению:
1
Платформа
SharePoint  2013
2
Приложение
1.      Решение для фермы (хранение бизнес процессов)
2.      Изолированное решение (хранение списков и библиотек)
3
Контейнер для HTML верстки
Веб часть
4
Бизнес-процессы
Код на C#
5
Хранилище для данных
Списки и библиотеки SharePoint
 Решаемые задачи:
База знаний – комплекс решений SharePoint, предназначенных для выполнения следующих задач:
1.    Пополнение Базы знаний
2.    Редактирование Базы знаний
3.    Организация доступа к Базе знаний для сотрудников МФЦ в соответствии с правилами «Системы ролей Базы знаний»
4.    Разделение обязанностей между редакторами Базы знаний в соответствии с правилами «Системы ролей Базы знаний»
Система ролей Базы знаний
В решении используются три роли:
1.     Все пользователи портала
2.     Младшие управляющие БЗ
3.     Старшие управляющие БЗ
Наименование роли
Действия
Объект
1
Все
Чтение
Список MFC_WikiChita_BZnaniy
Чтение
Список MFC_WikiChita_MList
Чтение
Список MFC_WikiChita_Organ
Чтение
Список MFC_WikiChita_OrganDetail
Чтение
Список MFC_WikiChita_Statistic
Чтение
Список MFC_WikiChita_Uslugi
Чтение
Список MFC_WikiChita_UslugiDetail
Чтение
Список MFC_WikiChita_UslugiInvisible
Чтение
Библиотека MFC_WikiChita_Soglasheniy
Чтение
Библиотека MFC_WikiChita_Blanki
2
Младший управляющий БЗ
Чтение
Список MFC_WikiChita_BZnaniy
Чтение
Список MFC_WikiChita_MList
Чтение
Список MFC_WikiChita_Organ
Чтение, Совместное использование
Список MFC_WikiChita_OrganDetail
Чтение
Список MFC_WikiChita_Statistic
Чтение
Список MFC_WikiChita_Uslugi
Чтение, Совместное использование
Список MFC_WikiChita_UslugiDetail
Чтение, Совместное использование
Список MFC_WikiChita_UslugiInvisible
Чтение
Библиотека MFC_WikiChita_Soglasheniy
Чтение, Совместное использование
Библиотека MFC_WikiChita_Blanki
2
Старший управляющий БЗ
Чтение, Совместное использование
Список MFC_WikiChita_BZnaniy
Чтение, Совместное использование
Список MFC_WikiChita_MList
Чтение, Совместное использование
Список MFC_WikiChita_Organ
Чтение, Совместное использование
Список MFC_WikiChita_OrganDetail
Чтение
Список MFC_WikiChita_Statistic
Чтение, Совместное использование
Список MFC_WikiChita_Uslugi
Чтение, Совместное использование
Список MFC_WikiChita_UslugiDetail
Чтение, Совместное использование
Список MFC_WikiChita_UslugiInvisible
Чтение, Совместное использование
Библиотека MFC_WikiChita_Soglasheniy
Чтение, Совместное использование
Библиотека MFC_WikiChita_Blanki

Хранилище БД. Списки и библиотеки Решения
Имя
Назначение
1
MFC_WikiChita_BZnaniy
Список, предназначен для хранения наименований территориальных органов МФЦ. В некоторых случаях список может именоваться, как БЗ.
2
MFC_WikiChita_MList
Список, предназначен для хранения служебной информации, используемой системой организации бизнес-процессов Решения. В частности, это касается хранения вторичных ключей от органов и баз знаний. В последующем система организации бизнес-процессов должна гарантировать ссылочную целостность между указанными списками.
3
MFC_WikiChita_Organ
Список, предназначен для хранения информации об органах, оказывающих муниципальные или государственные услуги.
4
MFC_WikiChita_OrganDetail
Список, предназначен для хранения информации о территориальных подразделениях  органов (их филиалы).
5
MFC_WikiChita_Uslugi
Список, предназначен для хранения информации об именах услугах, а также ссылок на бланки услуг и контейнера для вопросов и ответов по услуге.
6
MFC_WikiChita_UslugiDetail
Список, предназначен для хранения информации об услуге.
7
MFC_WikiChita_UslugiInvisible
Список, предназначен для хранения служебной информации, используемой системой организации бизнес-процессов Решения. В частности, это касается хранения вторичных ключей от услуг и баз знаний. В последующем система организации бизнес-процессов должна гарантировать ссылочную целостность между указанными списками.
8
MFC_WikiChita_Statistic
Список, предназначен для хранения информации о просмотрах Базы знаний сотрудниками МФЦ.
9
MFC_WikiChita_Soglasheniy
Библиотека, предназначена для хранения документов – соглашений между МФЦ и органами, оказывающими государственные или муниципальные услуги.
10
MFC_WikiChita_Blanki
Библиотека, предназначена для хранения документов – бланков для услуг.

Организация бизнес-процессов
Бизнес-процесс обязан:
1.     Гарантировать установку решений Баз знаний на любом сайте Портала МФЦ и выполнения ими задач в полном объеме независимо друг от друга, в тоже время, подчиненным единым требованиям и правилам и способным выдавать результаты, как единое целое, например, при сборе статистики о просмотрах Базы знаний сотрудниками МФЦ.  
2.     Контролировать ссылочную целостность между:
2.1. Базой знаний и органами. У одной базы знаний может быть несколько органов. Система должна запрещать удаление базы знаний, если к ней прикреплен хотя бы один орган.
2.2.Орган и услуги.   У одного органа может быть несколько услуг. Система должна запрещать удаление органа, если к нему прикреплена хотя бы одна услуга.
2.3.Услуга и описание услуг. У услуги может быть несколько описаний. Система должна запрещать удаление услуги, если к ней прикреплено хотя бы одно описание.
2.4.Орган и подчиненные органы. У одного органа может быть несколько подчиненных органов. Система должна запрещать удаление органа, если к нему прикреплен хотя бы один подчиненный орган.
3.     Контролировать процесс пополнения и редактирования Базы знаний в соответствии с требованием системы ролей.
4.     Организовывать процесс взаимодействия форм редакторов списков базы знаний с Веб частью решения в «один клик». А именно – гарантировать возврат на место вызова редакторов после успешного создания или редактирования строки у списка.
5.     Гарантировать наличие интернет адреса у каждой услуги Базы знаний. Интернет адрес услуги (в пределах портала МФЦ), по необходимости, может быть включен в ссылку на услугу в любых документах МФЦ. По ссылке пользователи портала МФЦ должны получать доступ к полной информации об услуге в «один клик» без дополнительных действий.
6.     Гарантировать ведение статистики просмотров базы знаний сотрудниками МФЦ. По необходимости, формировать отчет о просмотрах. Структура отчета оговаривается дополнительно. Статистика в части старших и младших редакторов баз знаний, а также администраторов портала МФЦ, не ведется.
7.     Гарантировать редакторам Базы знаний возможность уведомления сотрудников МФЦ об услуге по корпоративной почте МФЦ в «один клик», в случае ее обновления или по другим любым основаниям.
8.     Гарантировать сотрудникам МФЦ возможность уведомления редакторов Базы знаний об услуге по корпоративной почте МФЦ в «один клик», например, в случае обнаружения в ней недостатков.
9.     Гарантировать установку Веб части Базы знаний в любом месте портала МФЦ в неограниченном количестве.
10.  Гарантировать следующие принципы работы Базы знаний:
10.1.      Каждая БЗ имеет собственные, прикрепленные к ней органы, свойства которых наследуются от первоисточника. Изменения любого свойства в органе в любой БЗ изменяют первоисточник и затрагивают все БЗ, куда прикреплен орган 
10.2.      Каждый орган может наследовать, прикрепленные к нему услуги из других БЗ, при этом, наследование услуги может быть прекращено в любое время, тогда орган теряет право на управление ранее унаследованной услугой. Кроме этого, каждый орган может иметь собственные услуги, индивидуальные только для этого органа, прикрепленного к конкретной БЗ.
10.3.      Каждая услуга может наследовать, прикрепленные к ней описания из других БЗ, при этом, наследование описания может быть прекращено в любое время, тогда услуга теряет права на управления ранее унаследованным описанием. Кроме этого, каждая услуга может иметь собственные описания, индивидуальные только для этой услуги, прикрепленной к конкретной БЗ.
10.4.      Описания услуг могут быть созданы в БЗ на основании предопределенных наименований:
10.4.1.  Ответственный орган
10.4.2.  Получатель услуги
10.4.3.  Перечень предоставляемых документов
10.4.4.  Максимально допустимый срок предоставления услуги
10.4.5.  Результат предоставления услуги
10.4.6.  Основания для отказа  в приеме документов
10.4.7.  Основания для отказа в предоставлении услуги
10.4.8.  Реквизиты для оплаты
10.4.9.  Предоставление услуги регламентируется
10.4.10.        Рабочие контакты
10.4.11.        Срок хранения документов
10.4.12.        Прочее
По необходимости, описание услуг могут иметь и другие наименования.

10.5.      Контейнер для описания услуг должен помещать в себя неограниченный объем текста, ничем неограниченную структуру текста, например, текст может быть в виде – таблицы или разного рода списков. Контейнер для описания услуг должен быть снабжен редактором HTML.

Некоторые скриншоты  рабочего решения

список баз знаний
Список Баз знаний


Услуга - первая страниц
Список описаний услуги
Описание услуги

Sunday 17 July 2016

Отношения мастер-деталь в SharePoint 2013 между списками

Как построить связи между списками SharePoint в рамках отношений Один ко Многим, Много ко Многим и организовать между ними нечто подобие ссылочной целостности?
Наверняка этот вопрос задавали себе многие начинающие программисты SharePoin, так как стандартные средства этой великолепной системы управления сайтами не позволяют организовать полноценную ссылочную целостность между списками.

На самом деле построить каскадные отношения между списками несложно. Давайте рассмотрим небольшой пример на эту тему:

1. Создадим список сотрудников:
<?xml version="1.0" encoding="utf-8"?>
<List xmlns:ows="Microsoft SharePoint" Title="MFCEmployees" Name="MFCEmployees"
 Direction="$Resources:Direction;" Url="Lists/MFCEmployees" BaseType="0" 
xmlns="http://schemas.microsoft.com/sharepoint/" EnableContentTypes="TRUE">
  <MetaData>
       <ContentTypes>         
    <ContentType JSLink="~site/_layouts/15/webservice_001/
MyContent001/JS/MFCEmployees/MFCEmployees.js" 
ID="0x01005174e8c58336491db85ea917c7f65031" Name="Полное представление">
       <FieldRefs>                               
       <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" />
       <FieldRef ID="{ca828b4b-d9cf-4653-b4a5-2d44d3e9b9df}" Name="EmployeeName" />
       <FieldRef ID="{66cf8cfe-39b9-4123-bdaf-c36b7774dcd9}" Name="Birthdate" />
       <FieldRef ID="{254aabea-f690-4a88-b258-4fe87c61c612}" Name="Date_MFC_Employment" />
       <FieldRef ID="{163c8a49-88be-46e4-837c-57f3e0c1d96c}" Name="EmployeeStatus" />
       <FieldRef ID="{6d47a98d-c144-44ba-9912-339119c25681}" Name="Situation" />
       <FieldRef ID="{0655a999-cdc3-4c35-8b53-3bbd49e8192d}" Name="StatusEmployee" />
       <FieldRef ID="{6345b705-0bce-4e42-8b3e-0b3e3d37fe94}" Name="EndDateWork" />
       <FieldRef ID="{b5544a5a-5950-485e-8b3f-4286adbecb78}" Name="CompensatoryLeave" />
       <FieldRef ID="{234309e1-1823-4ad4-aba6-3c6390a378c6}" Name="Otdel" />
    </FieldRefs>
    </ContentType><ContentTypeRef ID="0x0120" /></ContentTypes>

    ФАЙЛ Schema.xml СОКРАЩЕН

  </MetaData>
</List>
Теперь создадим подчиненный список статусов сотрудников:
<?xml version="1.0" encoding="utf-8"?>
<List Id="{ca222b4b-d9cf-4653-b4a5-2d44d3e9b9df}" xmlns:ows="Microsoft SharePoint" 
Title="MFCEmployeesStatus" FolderCreation="FALSE" Direction="$Resources:Direction;"
 Url="Lists/MFCEmployeesStatus" BaseType="0" 
xmlns="http://schemas.microsoft.com/sharepoint/" EnableContentTypes="TRUE">
  <MetaData>
    <ContentTypes>        
      <ContentType ID="0x01005fec9402a88147e6937d2c0a3fb6303b" 
Name="Даты текущего состояния">
        <FieldRefs>                   
        <FieldRef ID="{4f243fe7-3f34-4e48-9c29-49f538bd4885}" Name="StatusEmployee" />
<FieldRef ID="{41790c3c-762d-4f2d-b570-939ab8281679}" Name="StartDateWork" />
<FieldRef ID="{e5da87f0-b02b-47f1-88d9-ce2cfc511ff1}" Name="EndDateWork" />
<FieldRef ID="{a50cd5f7-67ab-40d6-b9be-3ffcc6ff54e7}" Name="MFCEmployeesId" />
</FieldRefs>
      </ContentType><ContentTypeRef ID="0x01" /><ContentTypeRef ID="0x0120" />
</ContentTypes>

ФАЙЛ Schema.xml СОКРАЩЕН

  </MetaData>
</List>

Обратите внимание - у списка MFCEmployeesStatus объявлен столбец MFCEmployeesId. Это вторичный ключ списка MFCEmployees. По нему мы и будем строить отношения между списками по принципу Один ко Многим, где мастер - MFCEmployees, а деталь - MFCEmployeesStatus.

Теперь. когда у нас готовы списки нам необходимо создать механизм связывания двух списков по вторичному ключу. Для этого мы воспользуемся API SharePoint REST.
Без долгих объяснений прилагаю код соответствующих функций:
1. MFCEmployeesId.js:
SP.SOD.executeFunc("clienttemplates.js""SPClientTemplates"function () {
    SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
        Templates: {
            Fields: {
                //имя поля
                'MFCEmployeesId':
                {
                    NewForm: function (ctx) {
                        var qur = GetUrlKeyValue('MFCEmployeesId');
                        var val = 0;
                        if (typeof qur !== "undefined" && qur.length > 0)
                            val = qur;
                        else
                            val = 0;
                        ctx.CurrentFieldValue = val;
                        return SPFieldNumber_Edit(ctx);
                    }
                }
            }
        },
        OnPostRender: function (ctx) {
            //после загрузки
            var f = ctx.ListSchema.Field[0];
            if (f.Name == "MFCEmployeesId") {
                InputHiden(f, "Field",true);
            }
        },
        ListTemplateType: 10006
    });
});
Функция GetUrlKeyValue читает параметр запроса по имени MFCEmployeesId. 
Если находит значение, то подставляет его в поле вторичного ключа. В свою очередь функция 
InputHiden в событии OnPostRender скрывает поле от глаз пользователей.
Как видите, все очень просто. Самое главное:
1. Передать в строке запроса значение ключа сотрудника.
2. Перед сохранение статуса у сотрудника прочитать соответствующий параметр, где храниться
ключ сотрудника и сохранить его в соответствующем input-е.

Пока все. Я показал, как можно прочитать и сохранить значение вторичного ключа.
В следующий раз я продемонстрирую как можно организовать ссылочную целостность 
на практике. Следите за обновлениями :)

Фильтр авторизации ASP.NET MVC >= 5

Начиная с ASP.NET MVC 4 стало возможным применять обновленный фильтр авторизации класс AuthorizationFilter c проверкой подлинности ролей.

Этот фильтр реализует тип IAuthorizationFilter и определяет необходимость выполнения метода действия (например, выполнение проверки подлинности или проверки свойств запроса) с точки зрения безопасности.
Классы AuthorizeAttribute и RequireHttpsAttribute являются примерами фильтров проверки подлинности. Фильтры проверки подлинности выполняются перед выполнением любых других фильтров.
Вот, немножко сплагиатил с официальной документации о фильтрах. Теперь по сути:

Лично я использовал в своих проектах указанный фильтр, в том числе и для проверки подлинности на основании определенных ролей. Это, впрочем, самое главное новшество фильтра авторизации, которое появилось с 4-ой версией MVC.

Я был удовлетворен работой класса и, особенно не вникал в суть его работы :) до тех пор, пока не воспользовался новой системой авторизации и профилей Microsoft ASP.NET Identity 2.0. Тогда выяснилось, что класс AuthorizeAttribute полноценно использовать нельзя. Например, для проверка ролей класс взаимодействует с интерфейсом IPrincipal, который реализует собственный, отличающийся от Identity 2.0, интерфейс. Вывод - сочинить собственный фильтр авторизации. Далее речь пойдет об этом :)

Итак, с начало код фильтра:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System.Web;
using System.Web.Mvc;
 
namespace avk_commerce.web.Filters
{
    public class RoleAuthorizationAttribute : ActionFilterAttributeIActionFilter
    {
        public string Roles { getset; }
        #region вызывается перед обращением к методу действия
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {                                 
            bool Match = false;
            var user = filterContext.HttpContext.User.Identity;
            //роли, переданные через параметр
            string[] _Roles = Roles.Split(',');
            if (user.IsAuthenticated)
            {
                var UserManager = 
     filterContext.HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
                int UserId = user.GetUserId<int>();
                foreach (var Role in _Roles)
                {
                    Match = UserManager.IsInRole(UserId, Role);                    
                }
                if (!Match)
                {
                    filterContext.Result = new ViewResult { ViewName = "AccessDenied" };
                }
            }
            else {
                filterContext.Result = new ViewResult { ViewName= "LoginNow" };
            }            
            base.OnActionExecuting(filterContext);
        }
        #endregion
    }
}
Как видим, класс переопределяет метод OnActionExecuting. Класс содержит свойство
Roles. Это список ролей через запятую. Мы в первую очередь проверяем прошел ли текущий 
пользователь авторизацию на сайте. Потом проверяем - соответствуют ли задекларированные
в методе контролера разрешенные роли, тем ролям, которые присвоены текущему пользователю.
Выводы сохраняем в локальной переменной Match. Далее в зависимости от значения в переменной 
Match принимаем решения. Как видите, все очень просто :)

Пример практического применение:

using System.Web.Mvc;
using avk_commerce.web.Filters;
using avk_commerce.web.Models;
 
namespace avk_commerce.web.Controllers.Admin
{
    [RoleAuthorization(Roles = CustomRoles.Admin)]
    [Culture]    
    public class AdminController : BaseController
    {
        #region Index
        public ActionResult Index()
        {
            var item = this.GetHomeData(null);
            return View(item);
        }
        #endregion
               
        #region Dispose
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                //base.db.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion
    }
}