Как мне реализовать профиль компании в Sensenet? - PullRequest
0 голосов
/ 04 февраля 2019

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

<?xml version="1.0" encoding="utf-8"?>
<ContentType name="UserProfile" parentType="Workspace" handler="SenseNet.ContentRepository.UserProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
  <DisplayName>$Ctd-UserProfile,DisplayName</DisplayName>
  <Description>$Ctd-UserProfile,Description</Description>
  <Icon>UserProfile</Icon>
  <AllowedChildTypes>
    Blog,DocumentLibrary,EventList,MemoList,LinkList,TaskList,ImageLibrary,Posts,CustomList
  </AllowedChildTypes>
  <Fields>
    <Field name="IsWallContainer" type="Boolean">
      <Configuration>
        <VisibleBrowse>Advanced</VisibleBrowse>
        <VisibleEdit>Advanced</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <DefaultValue>true</DefaultValue>
      </Configuration>
    </Field>
    <Field name="IsCritical" type="Boolean">
      <Configuration>
        <VisibleBrowse>Hide</VisibleBrowse>
        <VisibleEdit>Hide</VisibleEdit>
        <VisibleNew>Hide</VisibleNew>
      </Configuration>
    </Field>
    <Field name="Manager" type="Reference">
      <Configuration>
        <VisibleBrowse>Hide</VisibleBrowse>
        <VisibleEdit>Hide</VisibleEdit>
        <VisibleNew>Hide</VisibleNew>
      </Configuration>
    </Field>
    <Field name="Deadline" type="DateTime">
      <Configuration>
        <VisibleBrowse>Hide</VisibleBrowse>
        <VisibleEdit>Hide</VisibleEdit>
        <VisibleNew>Hide</VisibleNew>
      </Configuration>
    </Field>
    <Field name="IsActive" type="Boolean">
      <Configuration>
        <VisibleBrowse>Hide</VisibleBrowse>
        <VisibleEdit>Hide</VisibleEdit>
        <VisibleNew>Hide</VisibleNew>
      </Configuration>
    </Field>
    <Field name="User" type="Reference">
      <DisplayName>$Ctd-UserProfile,User-DisplayName</DisplayName>
      <Configuration>
        <AllowMultiple>false</AllowMultiple>
        <AllowedTypes>
          <Type>User</Type>
        </AllowedTypes>
        <SelectionRoot>
          <Path>/Root/IMS</Path>
        </SelectionRoot>
      </Configuration>
    </Field>
  </Fields>
</ContentType>

Что привлекает мое внимание, так это поле ссылки, которое ссылается на пользователя, который присоединен кПрофиль пользователя.Таким образом, с этим знанием, возможно, было бы лучше создать отдельное определение типа контента с тем же справочным полем, что и это, чтобы оно было привязано к тому же пользователю.Другое направление, в котором я могу пойти, - это расширить XML-файл определения типа контента профиля пользователя, чтобы в него была встроена информация о компании, а не иметь отдельный файл определения для него, но проблема в том, что теперь я буду меняться в отношении того, чтопрофиль пользователя содержит и, вероятно, содержит слишком много информации.

Самое главное, что ни один другой пользователь не может быть привязан к той же компании, поскольку он должен быть уникальным.Учитывая все это, какой из этих методов, по вашему мнению, лучше всего подходит для этого?

Редактировать 1

Итак, я добился определенного прогресса в создании профиля компании для пользователя.,Я создал этот обработчик контента для профиля компании.

using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Workspaces;

namespace DerAssistantService.ContentHandlers
{
    [ContentHandler]
    public class CompanyProfile : Workspace
    {
        public CompanyProfile(Node parent) : this(parent, null) { }

        public CompanyProfile(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }

        protected CompanyProfile(NodeToken nt) : base(nt) { }

        public override string Name
        {
            get { return base.Name;}
            set { base.Name = value;}
        }

        [RepositoryProperty("Address")]
        public string Address
        {
            get { return GetProperty<string>("Address"); }
            set { this["Address"] = value; }
        }

        [RepositoryProperty("City")]
        public string City
        {
            get { return GetProperty<string>("City"); }
            set { this["City"] = value; }
        }

        [RepositoryProperty("State")]
        public string State
        {
            get { return GetProperty<string>("State"); }
            set { this["State"] = value; }
        }

        public override object GetProperty(string name)
        {
            switch (name)
            {
                case "Address":
                    return Address;
                case "City":
                    return City;
                case "State":
                    return State;
                default:
                    return base.GetProperty(name);
            }
        }

        public override void SetProperty(string name, object value)
        {
            switch (name)
            {
                case "Address":
                    Address = (string)value;
                    break;
                case "City":
                    City = (string)value;
                    break;
                case "State":
                    State = (string)value;
                    break;
                default:
                    base.SetProperty(name, value);
                    break;
            }
        }
    }
}

При регистрации профиль компании создается в домене Company.

using System;
using System.Linq;
using SenseNet.ApplicationModel;
using SenseNet.ContentRepository;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Storage.Data;
using SenseNet.ContentRepository.Storage.Security;

namespace DerAssistantService.Actions
{
    public static class UserActions
    {
        [ODataAction]
        public static Content RegisterUser(Content content, string email, string companyname, string password)
        {
            if (string.IsNullOrEmpty(email))
                throw new ArgumentNullException(nameof(email));
            if (string.IsNullOrEmpty(companyname))
                throw new ArgumentNullException(nameof(companyname));
            if (string.IsNullOrEmpty(password))
                throw new ArgumentNullException(nameof(password));

            var username = email.Split('@').First();

            var isUserCreated = Node.LoadNode("Root/IMS/Public/" + username);
            var isCompanyProfileCreated = Node.LoadNode("Root/Profiles/Company" + companyname);

            if (isUserCreated != null)
            {
                throw new NodeAlreadyExistsException("There already exists a user with this name.");
            }

            if (isCompanyProfileCreated != null)
            {
                throw new NodeAlreadyExistsException("There already exists a company with this name.");
            }

            using (new SystemAccount())
            {            
                var user = Content.CreateNew("User", content.ContentHandler, username);

                user["FullName"] = username;
                user["Email"] = email;
                user["LoginName"] = email;
                user["Enabled"] = true;
                user["Password"] = password;
                user.Save();

                var parent = Node.LoadNode("Root/Profiles/Company");
                var companyProfile = Content.CreateNew("CompanyProfile", parent, companyname);

                companyProfile["Name"] = companyname;
                companyProfile.Save();

                var identifiedUsers = Node.Load<Group>("/Root/IMS/BuiltIn/Portal/IdentifiedUsers");
                identifiedUsers.AddMember(user.ContentHandler as IUser);
                identifiedUsers.Save();

                return user;
            }
        }
    }
}

, и я создал этот тип контентаопределение для него.

<ContentType name="CompanyProfile" parentType="Workspace" handler="DerAssistantService.ContentHandlers.CompanyProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
  <DisplayName>CompanyProfile</DisplayName>
  <Description>This content contains basic information on a company</Description>
  <Icon>Company</Icon>
  <Fields>
    <Field name="Name" type="ShortText">
      <DisplayName>Company</DisplayName>
      <Description>The name of the company</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
    <Field name="Address" type="ShortText">
      <DisplayName>Address</DisplayName>
      <Description>The location of the company</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
    <Field name="City" type="ShortText">
      <DisplayName>City</DisplayName>
      <Description>The city where the company is located at</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
    <Field name="State" type="ShortText">
      <DisplayName>State</DisplayName>
      <Description>The state the company resides in</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
  </Fields>
</ContentType>

Проблема в том, что я не могу легко загрузить профиль компании.Между ними нет никакой связи, потому что они находятся в разных доменах, и информация друг о друге отсутствует.Как мне с этим смириться?

Редактировать 2

У меня наконец-то есть рабочее решение после небольшого количества экспериментов.

Вот как выглядит профиль моей компании.

 <?xml version="1.0" encoding="utf-8"?>
 <ContentType name="CompanyProfile" parentType="UserProfile" handler="DerAssistantService.ContentHandlers.CompanyProfile" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
      <DisplayName>CompanyProfile</DisplayName>
      <Description>This profile contains contains a single reference to a company that has registered itself in the web app.</Description>
      <Icon>UserProfile</Icon>
      <AllowedChildTypes>DocumentLibrary,EventList,MemoList,LinkList,TaskList,ImageLibrary,CustomList</AllowedChildTypes>
      <Fields>
        <Field name="Company" type="Reference">
          <DisplayName>Company</DisplayName>
          <Configuration>
            <AllowMultiple>false</AllowMultiple>
            <AllowedTypes>
              <Type>Company</Type>
            </AllowedTypes>
            <SelectionRoot>
              <Path>/Root/IMS</Path>
            </SelectionRoot>
          </Configuration>
        </Field>
      </Fields>
 </ContentType>

Теперь профиль компании расширяет профиль пользователя, так что у меня все еще есть ссылка на объект пользователя и есть новое свойство ссылки для объекта компании.

Теперь у меня есть отдельное определение типа контентафайл для компании.

<?xml version="1.0" encoding="utf-8"?>
<ContentType name="Company" parentType="Workspace" handler="DerAssistantService.ContentHandlers.Company" xmlns="http://schemas.sensenet.com/SenseNet/ContentRepository/ContentTypeDefinition">
  <DisplayName>Company</DisplayName>
  <Description>This content contains basic information on a particular company</Description>
  <Icon>Company</Icon>
  <AllowedChildTypes>Image</AllowedChildTypes>
  <Fields>
    <Field name="Address" type="ShortText">
      <DisplayName>Address</DisplayName>
      <Description>The location of the company</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
    <Field name="City" type="ShortText">
      <DisplayName>City</DisplayName>
      <Description>The city where the company resides in</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
    <Field name="State" type="ShortText">
      <DisplayName>State</DisplayName>
      <Description>The state the company resides in</Description>
      <Configuration>
        <VisibleBrowse>Show</VisibleBrowse>
        <VisibleEdit>Show</VisibleEdit>
        <VisibleNew>Show</VisibleNew>
        <Compulsory>true</Compulsory>
      </Configuration>
    </Field>
  </Fields>
</ContentType>

Вот так выглядит мой обработчик контента для CompanyProfile

using SenseNet.ContentRepository;
using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;

namespace DerAssistantService.ContentHandlers
{
    [ContentHandler]
    public class CompanyProfile : UserProfile
    {
        public CompanyProfile(Node parent) : this(parent, null) { }

        public CompanyProfile(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }

        protected CompanyProfile(NodeToken token) : base(token) { }

        [RepositoryProperty("Company", RepositoryDataType.Reference)]
        public Company Company
        {
            get { return GetReference<Company>("Company"); }
            set { SetReference("Company", value); }
        }

        public override object GetProperty(string name)
        {
            switch (name)
            {
                case "Company":
                    return Company;
                default:
                    return base.GetProperty(name);
            }
        }
        public override void SetProperty(string name, object value)
        {
            switch (name)
            {
                case "Company":
                    Company = (Company)value;
                    break;
                default:
                    base.SetProperty(name, value);
                    break;
            }
        }
    }
}

и как выглядит обработчик контента для класса моей компании.

using SenseNet.ContentRepository.Schema;
using SenseNet.ContentRepository.Storage;
using SenseNet.ContentRepository.Workspaces;

namespace DerAssistantService.ContentHandlers
{
    [ContentHandler]
    public class Company : Workspace
    {
        public Company(Node parent) : this(parent, null) { }

        public Company(Node parent, string nodeTypeName) : base(parent, nodeTypeName) { }

        protected Company(NodeToken token) : base(token) { }

        [RepositoryProperty("Address", RepositoryDataType.String)]
        public string Address
        {
            get { return GetProperty<string>("Address"); }
            set { this["Address"] = value; }
        }

        [RepositoryProperty("City", RepositoryDataType.String)]
        public string City
        {
            get { return GetProperty<string>("City"); }
            set { this["City"] = value; }
        }

        [RepositoryProperty("State", RepositoryDataType.String)]
        public string State
        {
            get { return GetProperty<string>("State"); }
            set { this["State"] = value; }
        }

        public override object GetProperty(string name)
        {
            switch (name)
            {
                case "Address":
                    return Address;
                case "City":
                    return City;
                case "State":
                    return State;
                default:
                    return base.GetProperty(name);
            }
        }

        public override void SetProperty(string name, object value)
        {
            switch (name)
            {
                case "Address":
                    Address = (string)value;
                    break;
                case "City":
                    City = (string)value;
                    break;
                case "State":
                    State = (string)value;
                    break;
                default:
                    base.SetProperty(name, value);
                    break;
            }
        }
    }
}

Я разделил логику в своем методе RegisterUser, поскольку он делал слишком много.

Моя функция RegisterUser теперь выглядит следующим образом.

        [ODataAction]
        public static Content RegisterUser(Content content, string email, string password)
        {
            using (new SystemAccount())
            {
                var username = email.Split('@').First();
                var user = CreateUser("Public", email, password, username, true);

                var identifiedUsers = Node.Load<Group>("/Root/IMS/BuiltIn/Portal/IdentifiedUsers");
                identifiedUsers.AddMember(user.ContentHandler as IUser);
                identifiedUsers.Save();

                return user;
            }
        }

        private static Content CreateUser(string domainName, string username, string password, string fullname, bool enabled, Dictionary<string, object> properties = null)
        {
            var domainPath = RepositoryPath.Combine(RepositoryStructure.ImsFolderPath, domainName);
            var domain = Node.LoadNode(domainPath);
            var user = Content.CreateNew("User", domain, username);
            user["Name"] = username;
            user["Password"] = password;
            user["FullName"] = fullname;
            user["Enabled"] = enabled;

            if (properties != null)
            {
                foreach (var key in properties.Keys)
                {
                    user[key] = properties[key];
                }
            }

            user.Save();
            return user;
        }
    }

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

        [ODataAction]
        public static Content RegisterCompany(Content content, string companyName, string userEmail)
        {
            using (new SystemAccount())
            {
                CompanyProfile companyProfile = Node.LoadNode("/Root/Profiles/Public/" + userEmail) as CompanyProfile;

                Company company = CreateCompany(companyName, companyProfile);

                var companyContent = Content.Create(company);
                companyContent.Save();

                companyProfile.Company = company;
                companyProfile.Save();

                return companyContent;
            }
        }

        private static Company CreateCompany(string companyName, CompanyProfile companyProfile)
        {
            var parent = Node.LoadNode("/Root/IMS/Company");

            Company company = new Company(parent);

            company.Name = companyName;
            company.Address = "N/A";
            company.City = "N/A";
            company.State = "N/A";
            company.VersionCreatedBy = companyProfile.User;
            company.VersionModifiedBy = companyProfile.User;
            company.CreatedBy = companyProfile.User;
            company.ModifiedBy = companyProfile.User;
            company.Owner = companyProfile.User;

            return company;
        }
    }

При этом, если бы я должен был сделатьзапросите RegisterUser и RegisterCompany в таком порядке, профиль компании теперь будет иметь ссылку на созданный объект компании.Пожалуйста, скажите мне, есть ли другой способ, которым я могу это реструктурировать.

1 Ответ

0 голосов
/ 11 февраля 2019

Я думаю, вам нужно поменять направление отсчета.Например, пользователь работает в компании, компания может иметь профиль.Таким образом, пользователь может использовать одно поле ссылки для ориентации на компанию или профиль компании.Этот механизм обеспечивает легкий доступ в обоих направлениях: если у вас есть пользовательский экземпляр user.Company или user.CompanyProfile, возвращает целевой объект.Обратное направление можно получить с помощью простого запроса, например, такого: Content.All.OfType<User>().Where(c => c.Company == company).FirstOrDefault().

...