<?xml version="1.0" encoding="utf-8"?>
<AlvaoApplication xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ModelVersion="1">
  <Applications>
    <Application id="1111">
      <Name>Object Create Monitors</Name>
      <UniqueId>bfcd7fa4-3925-4030-9674-6251a12581b0</UniqueId>
      <Version>2</Version>
      <AdvancedSettings>
        <Setting>
          <Name>ObjectCreateMonitors.Setting</Name>
          <Value />
        </Setting>
      </AdvancedSettings>
      <Scripts>
        <Script id="2849">
          <Name>PeriodicallyCreateMonitorsBasedOnDetection</Name>
          <Code>using System;
using Microsoft.Data.SqlClient;
using Alvao.Apps.API;
using Alvao.Context;
using Dapper;

public class PeriodicallyCreateMonitorsBasedOnDetection : IPeriodicAction
{
    public string Name 
    { 
        get =&gt; "PeriodicallyCreateMonitorsBasedOnDetection";
        set { }
    }

    public void OnPeriod(SqlConnection con)
    {
        using (var scope = AlvaoContext.GetConnectionScope())
        {
            Common.GenerationCore(scope, true, 0, 0);
        }
    }
}
</Code>
          <IsLibCode>false</IsLibCode>
          <Codesign>Idad8gOaaMQwczr/USWUAzw9roB0s0QF/GPjMZvp8THeVabXzHjlcaYpVTIOLP/WIiCdaJNnD5ZzqOS8iKrSTChra5uPCib4WO2NYkaHEYD56pS2viHNYKAgsalvIrY/Y8b4Z09JQJhabZBVmzwueeOSfMWBdgGTFuxBmNMuC9MHie+k/j0suXh1J8cIl7i6SYMHmNwwxObL9DeW51jwDaKERv1vSC5udtm2w02URwxS9S1AOsh0mnChY3xh7FJpADRJylWdWZVEqyPm0UXASZ2ZItr9U0/3BPbhalUVd8gJt7ni/dJHXaJ3rsKXLZAd7BwhVbMnI9AYlwpvRL92gg==</Codesign>
        </Script>
        <Script id="2850">
          <Name>Common</Name>
          <Code>using Alvao.Context;
using Dapper;
using System.Linq;
using System.Collections.Generic;
using Alvao.API.Utils;
using Newtonsoft.Json;
using System;
using Microsoft.Data.SqlClient;
using Alvao.API.Common;

public class Common
{
    public static ObjectCreateMonitorsSetting Setting
    {
        get
        {
            return CacheUtil.GetOrCreateCachedItem("ObjectCreateMonitorsSettingCache", FetchSetting, minutesToCache: 1);
            ObjectCreateMonitorsSetting FetchSetting()
            {
                using var scope = AlvaoContext.GetConnectionScope();

                var settingJson = scope.Connection.QueryFirstOrDefault&lt;string&gt;("SELECT TOP 1 sPropertyValue FROM tProperty WHERE sProperty = 'ObjectCreateMonitors.Setting'", null, scope.Transaction);
                if (string.IsNullOrEmpty(settingJson))
                {
                    throw new Exception("Settings ObjectCreateMonitors.Setting is missing");
                }

                return JsonConvert.DeserializeObject&lt;ObjectCreateMonitorsSetting&gt;(settingJson);
            }
        }
    }

    public static string GenerationCore(Alvao.Context.DB.IConnectionScope scope, bool isPeriodicAction, int computerId, int personId)
    {
        var setting = Setting;
        
        int monitorClassId = 17;
        int serialNumberKindId = 13;
        int modelKindId = 97;
        int manufacturerKindId = 17;
        string returnMessage = "";
        DateTime? lastCheckedDetectionDate = null;
        
        if (isPeriodicAction)
        {
            scope.Connection.Execute($@"
                if not exists(select 1 from tProperty where sProperty='ObjectCreateMonitors.LastCheckedDetectionImportedDatetime')
                    EXEC spUpdateInsertProperty 'ObjectCreateMonitors.LastCheckedDetectionImportedDatetime', NULL, NULL, NULL, '1900-01-01';
            ", null, scope.Transaction);

            lastCheckedDetectionDate = scope.Connection.QueryFirstOrDefault&lt;DateTime?&gt;("SELECT TOP 1 dPropertyValue FROM tProperty WHERE sProperty = 'ObjectCreateMonitors.LastCheckedDetectionImportedDatetime'", null, scope.Transaction);
            if (lastCheckedDetectionDate == null)
            {
                throw new Exception("ObjectCreateMonitors.LastCheckedDetectionImportedDatetime is missing in tProperty");
            }
        }

        List&lt;int&gt; listOfParentObjects = new List&lt;int&gt;();
        if (string.IsNullOrEmpty(setting.ParentObjectIds))
        {
            listOfParentObjects.Add(0);
        }
        else 
        {
            listOfParentObjects = setting.ParentObjectIds.Split(',').Select(int.Parse).ToList();
        }
        
        if (!isPeriodicAction)
        {
            listOfParentObjects.Clear();
            listOfParentObjects.Add(0); // For action run on command click, just to have one cycle
        }
        else if (!listOfParentObjects.Any())
        {
            listOfParentObjects.Add(-1); // If empty, than automatic creation is turned off
        }

        string actionDifferentiation = isPeriodicAction ? "and p.lintParentNodeId = @parentObject and d.dteImported &gt; @lastCheckedDetectionDate" : "and d.lintComputerNodeId = @computerId";
        foreach (var parentObject in listOfParentObjects)
        {
            if (parentObject != -1 || !isPeriodicAction) {
                List&lt;DetectedMonitor&gt; detectedMonitorsList = scope.Connection.Query&lt;DetectedMonitor&gt;($@"
                select distinct
                    wo.Caption, 
                    wo.SerialNumber, 
                    wo.MonitorManufacturer, 
                    wo.PNPDeviceID, 
                    wo.Active,
                    d.lintComputerNodeId,
                    n.lintParentId
                from 
                    tblWbemObject wo
                    join tblDetect d on wo.lintDetectId = d.intDetectId
                    join tblNode n on d.lintComputerNodeId = n.intNodeId
                    join tblNodeParent p on p.lintNodeId = n.intNodeId
                where 
                    __CLASS='WmiMonitorID' 
                    and n.IsActive = 1
                    and wo.SerialNumber != '0'
                    {actionDifferentiation}
                    and d.dteImported in (
                        select max(d2.dteImported)
                        from tblDetect d2
                        where 
                            d2.lintDetectKindId = 1  -- HW
                            and d2.lintComputerNodeId = d.lintComputerNodeId
                    )
                ", new { computerId, parentObject, lastCheckedDetectionDate }, scope.Transaction).ToList();

                if (detectedMonitorsList.Any())
                {
                    var createdMonitors = new List&lt;DetectedMonitor&gt;();
                    var existingMonitors = new List&lt;DetectedMonitor&gt;();
                    string serialNumbers;
                    
                    foreach (DetectedMonitor detectedMonitor in detectedMonitorsList)
                    {
                        bool monitorAlreadyExists = scope.Connection.QueryFirstOrDefault&lt;int&gt;($@"
                            select 
                                top 1 1
                            from 
                                tblNode n
                                join NodeCust v ON v.NodeId = n.intNodeId
                            where
                                n.lintClassId = @monitorClassId
                                and v.SerialNumber = @serialNumberValue
                        ", new { monitorClassId, serialNumberKindId, serialNumberValue = detectedMonitor.SerialNumber }, scope.Transaction) &gt; 0;

                        if (!monitorAlreadyExists)
                        {
                            int monitorId =Alvao.API.AM.Object.CreateByClass(monitorClassId, detectedMonitor.Caption, detectedMonitor.lintParentId, null);

                            scope.Connection.Execute($@"
                                UPDATE NodeCust
                                SET Model = @model, SerialNumber = @serialNumber, Manufacturer = @manufacturer
                                where NodeId = @monitorId;

                                EXEC spCalcNodeName @monitorId;
                            ", new { monitorId, modelKindId, serialNumberKindId, manufacturerKindId, model = detectedMonitor.Caption, serialNumber = detectedMonitor.SerialNumber, manufacturer = detectedMonitor.MonitorManufacturer  }, scope.Transaction);
                            createdMonitors.Add(detectedMonitor);
                        }
                        else
                        {
                            existingMonitors.Add(detectedMonitor);
                        }
                    }

                    if (!isPeriodicAction)
                    {
                        if (createdMonitors.Any())
                        {
                            serialNumbers = string.Join(", ", createdMonitors.Select(m =&gt; m.SerialNumber));
                            returnMessage = Localization.GetLocalization(personId, "MonitorsCreated") + serialNumbers + ". \r\n";
                        }
                        
                        if (existingMonitors.Any())
                        {
                            serialNumbers = string.Join(", ", existingMonitors.Select(m =&gt; m.SerialNumber));
                            returnMessage += Localization.GetLocalization(personId, "MonitorsExist") + serialNumbers;
                        }
                    }
                }
            }
        }

        if (isPeriodicAction)
        {
            scope.Connection.Execute($@"
                DECLARE @lastDetectionImportedDatetime DATETIME = (SELECT MAX(dteImported) FROM tblDetect)

                EXEC spUpdateInsertProperty 'ObjectCreateMonitors.LastCheckedDetectionImportedDatetime', NULL, NULL, NULL, @lastDetectionImportedDatetime;
            ", null, scope.Transaction);
        }

        return returnMessage;
    }
}

public class ObjectCreateMonitorsSetting
{
    public string ParentObjectIds { get; set; }
}

public class DetectedMonitor
{
    public string Caption { get; set; }
    public string SerialNumber { get; set; }
    public string MonitorManufacturer { get; set; }
    public string PNPDeviceID { get; set; }
    public int Active { get; set; }
    public int lintComputerNodeId { get; set; }
    public int lintParentId { get; set; }
}

public class Localization 
{
    public static List&lt;LocalizationItem&gt; Localizations {get {
        return new List&lt;LocalizationItem&gt;() {
            new LocalizationItem(1029, "CommandCreateMonitor", "Vytvořit monitor"),
            new LocalizationItem(1033, "CommandCreateMonitor", "Create monitor"),
            new LocalizationItem(1029, "MonitorsCreated", "Monitory vytvořeny. Sériová čísla: "),
            new LocalizationItem(1033, "MonitorsCreated", "Monitors created. Serial numbers: "),
            new LocalizationItem(1029, "MonitorsExist", "Některé monitory již existují. Sériová čísla: "),
            new LocalizationItem(1033, "MonitorsExist", "Monitors already exist. Serial numbers: "),
        };
    } }

    public static string GetLocalization(int personId, string name)
    {
        var localeId = GetPersonLocaleId(personId);
        var translation = FindInList(localeId, name);

        if (string.IsNullOrEmpty(translation))
        {
            localeId = GetDefaultLocaleId();
            translation = FindInList(localeId, name);
            if (string.IsNullOrEmpty(translation))
            {
                localeId = Locale.GetDatabaseLocaleId();
                translation = FindInList(localeId, name); 
            }
        }

        return translation;
    }

    private static string FindInList(int localeId, string name)
    {
        return Localizations.FirstOrDefault(x=&gt;x.LocaleId == localeId &amp;&amp; x.Name == name)?.Translation;
    }

    private static int GetDefaultLocaleId()
    {
        var locale = Person.GetCultureInfoOrDefault(new Alvao.API.Common.Model.Database.tPerson()).LCID;
        return locale;
    }

    private static int GetPersonLocaleId(int personId)
    {
        var locale = Person.GetById(personId).iPersonLocaleId;

        if (!locale.HasValue)
        {
            return GetDefaultLocaleId();
        }

        return locale.Value;
    }
}

public class LocalizationItem
{
    public int LocaleId {get; set;}
    public string Name {get; set;}
    public string Translation {get; set;}

    public LocalizationItem(int localeId, string name, string translation)
    {
        this.LocaleId = localeId;
        this.Name = name;
        this.Translation = translation;
    }
}</Code>
          <IsLibCode>true</IsLibCode>
          <Codesign>bBKh73DT1ppTZL31ZFY9UiXI1mWQ7vh7PV8QwiE//IRv+zz1Qx1aqPsg5XMoYg1hjsqm4UJ0W7/CKWEhwVB11OzWJNwttQcq5ZzWuABKq4wdHbevF3yPztRrHiS+gEKT7H2qpG6OFaJRfiBJsssg6bBHuSdHkMuAhmgsMl/FMhkhEiehNI+vPDDasz7lwQmfQk5cmfRKOIOz/CFu2tyNokCrwSzfu1xTY78Rfc8keyCiw8xDH9E6RfKNEKGA0gUithCHjgWog+XEDPDzfLT1GyW6NQIPeltRLGx9NjjFsU/vENQ96ySDKmZlktjmRfEU7nj7DOWxSpelkhXovZruPg==</Codesign>
        </Script>
        <Script id="2851">
          <Name>CreateMonitorCommand</Name>
          <Code>using System;
using Dapper;
using Alvao.API.Common;
using Alvao.API.Common.Model.CustomApps;
using Alvao.API.AM.Model;
using Alvao.Context;
using Alvao.Apps.API;

public class CreateMonitorCommand : IEntityCommand 
{
    public string Id {get; set;}
    public Entity Entity {get; set;}

    public CreateMonitorCommand()
    {
        Id = "CreateMonitorCommand".GetHashCode().ToString();
        Entity = Entity.Object;
    }

    public EntityCommandShowResult Show(int entityId, int personId)
    {   
        int position = 3;
        string icon = "tv_28_regular";
        string name = Localization.GetLocalization(personId, "CommandCreateMonitor"); 

        var shownObject = Alvao.API.AM.Object.GetById(personId, entityId);
        bool show = shownObject != null &amp;&amp; (shownObject.lintClassId == 5 || shownObject.lintClassId == 47 || shownObject.lintClassId == 50); //Desktop, Laptop, Server
        show = show &amp;&amp; Alvao.API.AM.ObjectRight.CheckForUser(personId, shownObject.intNodeId, Alvao.API.AM.Model.ObjectRight.Right.CanObjectCreate);
        if(show)
        {
            using var scope = AlvaoContext.GetConnectionScope();
            bool hasValidHwDetection = scope.Connection.QueryFirstOrDefault&lt;int&gt;($@"
            select
                top 1 1
            from 
                tblWbemObject wo
                join tblDetect d on wo.lintDetectId = d.intDetectId
            where 
                __CLASS='WmiMonitorID'
                and d.lintComputerNodeId = @computerId
                and dteImported is not null
                and wo.SerialNumber != '0'
                and d.dteImported in (
                    select max(d2.dteImported)
                    from tblDetect d2
                    where 
                        d2.lintDetectKindId = 1 -- HW
                        and d2.lintComputerNodeId = d.lintComputerNodeId
                )
            ", new { computerId = shownObject.intNodeId }, scope.Transaction) == 1;
            show = hasValidHwDetection;
        }

        return new EntityCommandShowResult (show, name, icon, position);
    }

    public CommandResult Run(int entityId, int personId)
    {
        MessageType messageType = MessageType.Info;
        string navigateToUrl = string.Empty;
        string messageText = string.Empty;

        try {
            using (var scope = AlvaoContext.GetConnectionScope())
            {
                var shownObject = Alvao.API.AM.Object.GetById(personId, entityId); 
                var objectId = shownObject.intNodeId;
                messageText = Common.GenerationCore(scope, false, objectId, personId);
            }
        } catch (Exception ex) {
            messageType = MessageType.Error;
            messageText = ex.Message;
        }

        return new CommandResult(messageType, messageText, navigateToUrl);
    }
}</Code>
          <IsLibCode>false</IsLibCode>
          <Codesign>APQDOX/rd1iom+FDwMrJ6B4k8UZ07TSWzp9IYV5baQijUeLR/kUGfR188fLCNjio3a+lQ9tYZ5gZ2/AnV2a0ouh0ME5hD952mAIX2wbucTEEGwZspipu2pb01lDojUeIHksYQyfkWPz/ej7PArwuthpzvLJb136iUaiRTeOeHE1pm8C8IemkrKhvAJLme4ktcoH7MaN9DOx/6AAbJDLRdWeRMtrswfZ0AQ/3emBut/1R67l6kYVol8f6tWSA90SUqA9AJSdtPQGCQbBktNHHVSezjp3LM2c3o+5sM+NTbc/7igoeUhl/RzmQIsoqeUR31cZhM61THqfuvXKiir7dYQ==</Codesign>
        </Script>
      </Scripts>
    </Application>
  </Applications>
</AlvaoApplication>