Community
    • Categories
    • Recent
    • Popular
    • Users
    • Search
    • Register
    • Login
    1. Home
    2. LFischer
    3. Best
    Offline
    • Profile
    • Following 1
    • Followers 5
    • Topics 0
    • Posts 591
    • Groups 2

    Posts

    Recent Best Controversial
    • RE: Probleme mit der JSON RPC API - Immer code -32600

      Hallo @Pippo

      ein valider JSON RPC Request hat ein paar Voraussetzungen, unter anderem:

      • Es muss sich um einen POST Request handeln
      • Der "Content-Type" muss "application/json" lauten
      • Bei den übergebenen Daten muss es sich um valides JSON handeln (das muss im Request Body stehen)

      In unserem PHP API Client (siehe hier: https://packagist.org/packages/idoit/apiclient#dev-main ) kannst du dir die Header Daten ausgeben lassen. Diese lauten bei mir z.B.

      POST /idoit/src/jsonrpc.php HTTP/1.1
      Host: localhost
      User-Agent: idoit/apiclient 0.11-dev
      Accept: */*
      Accept-Encoding: application/json
      Content-Type: application/json
      X-RPC-Auth-Username: admin
      X-RPC-Auth-Password: admin
      Content-Length: 90
      

      (X-RPC-Auth-* Header sind nur notwendig, wenn man sich mit einem User anmelden möchte)

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: Design der 1.19

      Hallo zusammen,

      ein kurzer Hinweis aus dem "Maschinenraum" 😉 Wenn ihr eigene CSS Dateien verwenden möchtet könnt ihr diese für gewöhnlich einfach nur in den Ordner src/themes/default/css/ legen - i-doit wird diese dann sammeln und cachen (wobei die style.css immer zuerst und mit Priorität geladen wird).

      Wir lesen uns dieses Thema natürlich durch und möchten in den kommenden Versionen darauf eingehen. Das Redesign ist nicht mit i-doit 1.19 "abgeschlossen".

      Aber zurück zum eigentlichen Thema:
      Alternativ wäre es auch möglich die eigenen Stylesheets mittels Add-on bereitzustellen, dafür braucht ihr eigentlich nur eine PHP Datei mit folgendem Inhalt und eine entsprechende CSS Datei:

      <?php
      
      // file "src/classes/modules/custom-style/init.php".
      isys_application::instance()->container->get('signals')->connect('mod.css.attachStylesheet', function () {
          // file "src/classes/modules/custom-style/style.css".
          return __DIR__ . '/style.css';
      });
      

      Das ist eine stark vereinfachte Form eines Add-ons, die sich auch nicht mittels Admin-Center installieren, aktivieren, deaktivieren oder deinstallieren lässt. Wenn man es "richtig" machen möchte kann man sich den entsprechenden KB Artikel durchlesen 🙂

      Der Vorteil dies mittels Add-on zu lösen ist in erster Linie, das Add-ons (bzw. deren Code) nicht von i-doit Updates angepasst werden - somit könnte diese Lösung über mehrere Versionen erhalten bleiben. Und es hilft natürlich auch solche Anpassungen an eine "dafür vorgesehene" Stelle zu packen anstatt direkt Änderungen im Kern Quellcode vorzunehmen... Davon möchte ich dringend abraten 😄

      Viele Grüße
      Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: Probleme mit der JSON RPC API - Immer code -32600

      Hey @Pippo

      ich habe noch mal im Code nachgesehen - die Meldung "Provided request is not a valid json rpc" wird nur in zwei Situationen ausgegeben:

      • Wenn der übergebene Request kein "valides" JSON beinhaltet bzw. nicht aus dem Request-Body heraus dekodiert werden konnte (siehe JSON lint)
      • Wenn das dekodierte JSON kein Array ist

      Ich denke den zweiten Fall können wir ausschließen, denn dein gegebener Payload ist sowohl valides JSON als auch ein Array. Es kann also eigentlich nur noch der Fall sein, das dieses nicht als Request Body übergeben wird 🤔

      Hast du mal im i-doit den API Log auf "debug" geschaltet und nach dem Request geschaut was im {i-doit}/log Verzeichnis geschrieben wird?

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: "Personengruppen Mitglieger"

      Hallo @lord_helmchen

      dieser Tippfehler wird in i-doit 33 korrigiert, aktuell planen wir diese Version in KW 41 zu veröffentlichen 🙂

      Du kannst alternativ natürlich die Übersetzung selbst korrigieren über "Verwaltung > Datenansicht > Sprachprofile"

      Da kannst du die Sprachkonstante LC__CONTACT__TREE__MEMBERS mit der korrigierten Übersetzung Personengruppen Mitglieder ersetzen.

      VG Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: XML template for import

      Hello @finaria

      for this you can export a object from i-doit and use the resulting XML 🙂

      Best regards
      Leo

      posted in General
      LFischerL
      LFischer
    • RE: REST API - Parent oder Children finden

      Hallo zusammen,

      @MartinV hatte es richtig erkannt - um die zugewiesenen Standorte eines Objekts zu erhalten braucht man die Methode cmdb.location_tree.read hier kann man mittels Parameter id entscheiden wessen "children" man sehen möchte.

      Also z.B.

      {
          "version": "2.0",
          "method": "cmdb.location_tree.read",
          "params": {
              "id": 1234,
              "apikey": "<key>"
          },
          "id": 1
      }
      

      Im Ergebnis erhält man dann ein Array mit allen direkt zugewiesenen Objekten, die Daten schauen dabei so aus (pro Objekt):

      [
          {
              "id": 302,
              "title": "0.01 Office",
              "sysid": "ROOM_00000302",
              "type": 26,
              "type_title": "Room",
              "status": 2,
              "cmdb_status": 6,
              "cmdb_status_title": "in operation"
          },
          {
              "id": 1073,
              "title": "pool.ntp.org",
              "sysid": "CLOUD_0001073",
              "type": 39,
              "type_title": "Host",
              "status": 2,
              "cmdb_status": 6,
              "cmdb_status_title": "in operation"
          },
      
          ...
      ]
      

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: Abhängige Dialog+ Felder?

      Hallo @stephan ,

      aktuell ist das noch nicht möglich, wir haben dieses Feature aber im Auge und für dieses Jahr geplant 🙂

      Viele Grüße
      Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: Zeilenabstände und Abstände in Menübäumen für alle Benutzer definieren

      Hey @MarcelP

      also eine dedizierte Einstellung gibt es dafür nicht - du könntest aber probieren die folgenden zwei "Expertensetting" hinzuzufügen:

      Die "keys" müssen gui.tree.spacing und gui.category.padding lauten und deren Werte ein einfaches s. Achte darauf das es eine "Tenant Setting" sein sollte.

      Das müsste dazu führen das User, die noch keine persönliche Einstellung vorgenommen haben, die "Mandanten"-Einstellung erben 🙂

      Lass mich wissen ob es geklappt hat!

      Viele Grüße
      Leo

      posted in Betrieb
      LFischerL
      LFischer
    • RE: Signal für \isys_cmdb_dao_category::save_data

      Hallo @steven_c24

      ich glaube um dir besser helfen zu können müsste ich zuerst verstehen wieso nur die save_data Methode so wichtig ist 🤔 Was ist dein Use-case? Wenn du nur bei bestimmten Kategorien "reagieren" möchtest kannst du einfach die übergebene "DAO" prüfen (z.B. if ($dao instanceof isys_cmdb_dao_category_g_location) ).

      Wenn du nur in einem bestimmten Kontext reagieren möchtest könntest du auch die Context Komponente nutzen - diese beinhaltet Informationen "von wo" der aktuelle Request kommt. Also von der GUI, aus der API oder einem CLI Command. Zusätzlich sind dort auch Infos hinterlegt welche Funktion genutzt wurde, wie z.B. "Import", "Templates", "LDAP", ...

      Neben den genannten Signalen fällt mir gerade auch nichts ein - aber vielleicht gibt es ja andere Möglichkeiten 🙂

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: unexpected token "::", expecting "(" bei OCS-Inventory Import

      Hey @apfel-jan

      welche PHP Version hast du im Einsatz? Ich kann an der entsprechenden Stelle keinen "Fehler" finden, vor allem da es ja bei dir auf dem System über die Web GUI läuft 🤔

      Ist es möglich das Apache und das CLI verschiedene PHP Versionen nutzen?

      VG Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: Update auf Version 25 Wo sind die Menüpunkte hin?

      Hallo @tabacha

      die beiden Punkte sind in der neuen Verwaltung hier verortet:

      a1776c61-628b-42e2-854d-7ef39ecb90ac-image.png

      Also der "{mandant} Verwaltung" beinhaltet, neben der Lizenz-Info, die Systemübersicht und in der Navbar ist der "i-doit Update" Button 🙂

      Viele Grüße
      Leo

      posted in Betrieb
      LFischerL
      LFischer
    • RE: Neues Attribute (property) für Listenansicht

      Hallo @steven_c24

      Ich fürchte ich habe hier etwas vertauscht und dir den Weg für ein Attribut genannt das nur im Report Manager auftaucht 🙈

      Aktuell gibt es für dein Vorhaben nur die folgende Möglichkeit:
      Jedes Kategorieattribut kann einen eigenen Callback Code für die Objektlisten mitbringen, dafür gibt es aber aktuell keine Schnittstelle - somit ist das nur über Umwege machbar.

      Zur Zeit funktioniert die Logik über den Namen:

      Aus dem Attribut guarantee_status in der Klasse isys_cmdb_dao_category_g_accounting würde der folgende Namespace erstellt werden:

      idoit\Module\Cmdb\Model\Ci\Category\G\Accounting\GuaranteeStatus - die zusammensetzung sieht dabei folgendermaßen aus:

      Kategorie Art: "G" für Global oder "S" für Spezifisch
      Kategorie Identifier in CamelCase "Accounting"
      Attribut Identifier in CamelCase "GuaranteeStatus"

      idoit\Module\Cmdb\Model\Ci\Category\{Kategorie Art}\{Kategorie Identifier}\{Attribut Identifier}

      Wenn diese Klasse existiert und das Interface idoit\Module\Cmdb\Model\Ci\Category\DynamicCallbackInterface implementiert kann es für die Objektliste verwendet werden.

      Diese Klasse benötigt nur eine statische render Methode 🙂 Als Beispiel kannst du dir src/classes/modules/cmdb/src/Model/Ci/Category/G/Accounting/GuaranteeStatus.php ansehen.

      Das einzige Problem stellt also aktuell nur der statische Namespace dar... Im eigenen Add-on könntest du vermutlich sowas machen wie:

      \idoit\Psr4AutoloaderClass::factory()
         ->addNamespace(
            'idoit\Module\Cmdb\Model\Ci\Category\G\MyCustomCategory', 
            __DIR__ . '/src/MyCustomCategory'
         );
      

      Ich glaube das sollte funktionieren - ist aber natürlich nicht der "sauberste" Weg 🤔

      Ich habe einen Entwickler-Task erstellt um für diesen Use-Case eine Schnittstelle zu implementieren, damit solche Callbacks auch "von außerhalb" hinzugefügt werden können.

      Ich hoffe das Hilft weiter!

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: PHP Fatal Error Memory exhausted beim search-index nach C__CATG__ADRESS

      Hi @jimwendrich

      das Problem ist tatsächlich sehr merkwürdig, vor allem wenn man betrachtet wie "wenig" Daten indexiert werden. Gibt es ggf. andere Kategorien welche mehr Datensätze indexieren?

      Hast du vielleicht ein paar Logs mit weiteren Informationen oder ggf. Stacktrace?

      Alternativ würde mich auch mal interessieren wie groß der Index ist - du könntest hierzu auf der Datenbank die folgende Query ausführen:

      SELECT COUNT(1) AS cnt FROM isys_search_idx;
      

      Anschließend könntest du den Suchindex neu aufbauen lassen und erneut die Größe auslesen... Eigentlich sollte sich diese nicht ändern 😉 Wächst sie allerdings kontinuierlich scheint es tatsächlich ein Problem zu geben!

      Viele Grüße
      Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: Bug mit API Addon 1.15

      Hallo @spirit21ms

      der Bug ist uns bereits bekannt und kann mit nur einer kleinen Änderung repariert werden. Dazu müsste die Zeile 148 in der Datei {i-doit}/src/classes/modules/api/model/isys_api_model_cmdb.class.php getauscht werden.

      Diese Zeile

      $l_return[$l_map] = $routeGenerator->generate('cmdb.object-type.image', ['objectTypeId' => $p_mapping['isys_obj__isys_obj_type__id']]);
      

      Muss durch diese hier ersetzt werden

      $l_return[$l_map] = $routeGenerator->generate('cmdb.object-type.image', ['objectTypeId' => $p_row['isys_obj__isys_obj_type__id']]);
      

      Der Fix wird natürlich auch in der nächsten API Version gelöst sein.

      Viele Grüße
      Leo

      posted in Betrieb
      LFischerL
      LFischer
    • RE: Neues Attribute (property) für Listenansicht

      Hallo @steven_c24

      verstehe ich es richtig das es sich dabei um ein "virtuelles" Attribut handeln soll? Also eines, das keine eigenen Daten beinhaltet und nur in der Tabelle auftauchen soll?

      Dafür müsstest du das folgendermaßen machen:

      use idoit\Component\Property\Type\DynamicProperty;
      
      protected function dynamic_properties()
      {
          return [
              '_prop' => new DynamicProperty(
                  'LC__CMDB__CATG__GLOBAL_PRICE',
                  'isys_catg_accounting_list__isys_obj__id',
                  'isys_catg_accounting_list',
                  [
                      $this,
                      'dynamic_property_callback_prop'
                  ]
              )
          ];
      }
      

      Diese sogenannten "dynamischen properties" funktionieren mit Code-Callbacks 🙂 Leider ist die Formatierung im Forum etwas kaputt, aber es dürfte trotzdem verständlich sein. Der Callback muss eine public Methode sein, als Parameter wird die komplette Zeile übergeben:

      public function dynamic_property_callback_prop(array $row)
      {
         return 'custom content :)';
      }
      

      Hilft das weiter?

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: Import CSV Software

      Hallo zusammen,

      manche Objekt Browser zeigen nur eine bestimmte Auswahl an verfügbaren Objekttypen an, das hat in der Regel mit einem internen Filter zu tun, der auf zugewiesene Kategorien reagiert.

      Bedeutet: Wenn dein Objekttyp "SAP System" z.B. die spezifische Kategorie "Anwendungen" beinhaltet sollte diese hier aufgeführt werden. Weitere Informationen zu diesen Filtern (bzw. zur Frage "Warum taucht mein Objekttyp in Objekt Browser XY nicht auf?") findest du hier:

      Verwaltung > CMDB Einstellungen > Objekt-Browser

      Hier wählst du den entsprechenden Objekt Browser aus "Softwarezuweisung / Anwendung" und solltest anschließend folgende Information sehen:
      26b0e8fe-544f-426d-b768-eb512194755d-image.png

      Viele Grüße
      Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: HE bei Schrank erweitern, ohne Neupositionierung

      Hallo zusammen,

      ich fürchte das wird leider nicht klappen - die (technische) Positionierung im Schrank zählt immer von oben nach unten.
      Wenn also eine neue HE dazu kommt wird diese (optisch) unten drangesetzt. Wenn ein Objekt in der "obersten" HE positioniert ist wird dieses auch dort bleiben. Ein Objekt in der "untersten" HE wird danach allerdings in der vorletzten HE positioniert sein.

      Es ändert sich eigentlich nur die (optische) nummerierung der HEs.

      Viele Grüße
      Leo

      posted in Betrieb
      LFischerL
      LFischer
    • RE: Neuer Controller über Symfony Routing component -> Benutzerrechte

      Hallo @steven_c24

      in i-doit sind alle Routen und Parameter erst nach dem Login verfügbar, das ist das gewollte Verhalten 🙂 Es sollte auch nicht möglich sein dieses Verhalten mittels OAuth o.Ä. zu umgehen.

      Das wäre genau der Punkt an dem die API übernimmt. Um dann auf eigenen Code zuzugreifen müsste die API um einen eigenen Endpunkt erweitert werden, dazu gibt es in der Knowledge Base einen Artikel, siehe https://kb.i-doit.com/de/software-entwicklung/add-ons-entwickeln/api-erweitern.html

      Ich hoffe das Hilft weiter 😉

      Viele Grüße
      Leo

      posted in Entwicklung
      LFischerL
      LFischer
    • RE: PHP Fatal Error Memory exhausted beim search-index nach C__CATG__ADRESS

      Hey @jimwendrich

      ich habe eben einen Kollegen gefragt - der Bug bzgl. den "JDISC Custom Attributes" ist bekannt, aber es gibt noch keinen Fix dafür. So wie es aktuell aussieht wird es auch nicht mehr den Weg in die i-doit 1.17.2 finden.

      Die Anmerkung zur White- bzw Blacklist verstehe ich sehr gut. Ich werde das mal mitnehmen, ggf. bekommen wir hier in der Zukunft eine zufriedenstellende Lösung hin 😉

      Wenn ich es richtig sehe sollte es (ohne Seiteneffekte) möglich sein die DB Tabelle isys_catg_jdisc_ca_list zu leeren. Vorher aber bitte unbedingt eine Sicherung machen!

      Zum letzten Punkt: es kann natürlich sehr gut sein, das die zusätzlichen ~135k Einträge auftauchen weil der Prozess vorher abgebrochen ist... Ich fürchte das kann man nicht wirklich nachvollziehen 😕

      Viele Grüße
      Leo

      posted in Allgemein
      LFischerL
      LFischer
    • RE: Switch Ports Sortierung

      Hey @StefanP74

      ich war ein wenig fleißig und habe etwas erarbeitet 😉 Dieses mal betrifft es aber ein paar mehr Zeilen - ich glaube es ist einfacher die komplette Datei zu tauschen. Leider ist es im Forum nicht möglich die ganze Datei hochzuladen...

      /**
       * i-doit cabling visualization javascript base class.
       *
       * @author  Leonard Fischer <lfischer@i-doit.com>
       */
      window.Cabling = Class.create({
          $element:      null,
          data:          null,
          cache:         null,
          svg:           null,
          vis:           null,
          zoom:          null,
          options:       {},
          indexMapping:  [],
          rootContainer: {
              height:   0,
              matching: []
          },
      
          getOptions: function () {
              return this.options;
          },
      
          getOption: function (option) {
              return this.options[option];
          },
      
          setOption: function (option, value) {
              this.options[option] = value;
      
              return this;
          },
      
          getSelectedElements: function () {
              // This method should be used to get all selected cable paths.
              // this.svg.selectAll('.selected');
          },
      
          initialize: function ($el, data, options) {
              var that = this;
      
              that.$element = $el;
              that.data = data || [];
              that.options = {
                  authEdit:               false, // Defines if the cabling view will allow any editing.
                  minZoomLevel:           0.1,   // Defines the minimal zoom level.
                  maxZoomLevel:           1.5,   // Defines the maximal zoom level.
                  objectTypeData:         {},    // JSON with all necessary object type data (name, color, ...).
                  connectorTypeData:      {},    // JSON with all necessary connector type data (name, color, ...).
                  objectTypeFilter:       [],    // Array pf object types, that shall not be displayed.
                  onAfterProcess:         null,  // 'Complete' event for when the rendering has finished.
                  onObjSelect:            null,  // Click event for a object node.
                  onObjUnselect:          null,  // Event for when a object gets unselected.
                  onCableSelect:          null,  // Click event for a cable.
                  onConnectorSelect:      null,  // Click event for a connector.
                  onConnectorUnselect:    null,  // Event for when a connector gets unselected.
                  onObjAcceptDrag:        null,  // Event for when a object gets unselected.
                  onObjAbortDrag:         null,  // Event for when a object gets unselected.
                  width:                  null,  // This can be used to change the viewpoints width. The SVG element will always remain 100%x100%.
                  height:                 null,  // This can be used to change the viewpoints height. The SVG element will always remain 100%x100%.
                  undefinedConnectorType: {
                      color: '#fff',
                      title: '-'
                  },                             // Default 'connector type' object.
                  undefinedObjectType:    {
                      color: '#fff',
                      title: '-'
                  },                             // Default 'object type' object.
                  nodeWidth:              150,   // Define the node width.
                  nodeHeight:             20,    // Define the node heigt.
                  nodeMarginX:            25,    // Define the horizontal margin between nodes.
                  nodeMarginY:            25,    // Define the vertical margin between nodes.
                  displayWiring:          false, // Define if the internal wiring shall be displayed (object boxes will get transparent).
                  showCableLabels:        false, // Define if cables shall be displayed.
                  clickableConnectors:    false  // Define if connectors shall be clickable.
              };
      
              that.rootContainer = {
                  height:   0,
                  matching: []
              };
      
              // Setting the default width and height.
              that.options.width = that.$element.getWidth();
              that.options.height = that.$element.getHeight() + 16;
      
              Object.extend(that.options, options || {});
      
              that.svg = d3.select($el).append('svg')
                           .attr('width', that.options.width)
                           .attr('height', that.options.height);
      
              that
                  .svg.append('rect')
                  .attr('width', that.options.width)
                  .attr('height', that.options.height)
                  .style('fill', 'none')
                  .style('pointer-events', 'all');
      
              // Here we set the <g> after the <rect>, this is necessary for dragging and zooming but still clicking elements inside <g>.
              that.vis = this.svg.append("g");
      
              that.zoom = d3.zoom()
                  .scaleExtent([
                      that.options.minZoomLevel,
                      that.options.maxZoomLevel
                  ])
                  .on('zoom', function () {
                      that.vis.attr('transform', d3.event.transform);
                  });
      
              that.svg
                  .call(that.zoom)
                  .call(that.zoom.transform, d3.zoomIdentity.translate(that.options.width / 2, that.options.height / 2));
      
              this.appendStyle();
      
              return this;
          },
      
          appendStyle: function () {
              // This method is necessary to include the SVG styles - this will be used in the SVG export.
              var defs   = this.svg.select('defs'),
                  isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
      
              if (defs.node() === null) {
                  defs = this.svg.append('defs');
      
                  defs.append('style').attr('type', 'text/css').text(
                      ".cabling-object { \
                          fill: none;\
                          stroke: #444;\
                          stroke-width: 1px;\
                      }\
                      text {\
                          font-family: \"Helvetica Neue\", \"Lucida Grande\", \"Tahoma\", Arial, serif;\
                          font-size: 11px;\
                          paint-order: stroke;\
                          stroke: transparent;\
                          stroke-width: 0;\
                          fill: #000;\
                      }\
                      text.connector-left-title,\
                      text.connector-right-title {\
                          stroke-width: 5px;\
                          stroke: transparent;\
                      }\
                      text.connector-left-title.stroked,\
                      text.connector-right-title.stroked {\
                          " + (isIE11 ? "stroke-width: 0;" : "stroke-width: 5px;") + "\
                          stroke: #fff;\
                      }\
                      text.title,\
                      .linkname text,\
                      .linkname.clicked rect,\
                      .clicked rect.title {\
                          fill: #fff;\
                      }\
                      text.title {\
                          font-weight: bold;\
                      }\
                      .linkname rect,\
                      .linkname.clicked text {\
                          fill: #000;\
                      }\
                      rect.title,\
                      .clicked text.title {\
                          fill: #222;\
                      }\
                      circle.connector {\
                          stroke: rgba(0, 0, 0, 0.75);\
                          stroke-width: 1;\
                      }\
                      circle.connector-inner {\
                          fill: rgba(0, 0, 0, 0.75);\
                          stroke: none;\
                      }\
                      path {\
                          fill: none;\
                          stroke: #555;\
                          stroke-width: 2;\
                      }"
                  );
              }
          },
      
          textFormat: function (text, width) {
              var result = text;
      
              if (width <= 0) {
                  return '';
              }
      
              d3.select(this).text(result);
      
              while (this.getComputedTextLength() > width) {
                  result = result.substring(0, (result.length - 2));
                  d3.select(this).text(result);
              }
      
              if (result !== text && width > 2) {
                  result += '..';
              }
      
              return result;
          },
      
          clearCanvas: function () {
              this.vis.selectAll('*').remove();
      
              this.rootContainer = {
                  height:   0,
                  matching: []
              };
          },
      
          responsive: function () {
              this.options.width = this.$element.getWidth();
              this.options.height = this.$element.getHeight() + 16;
      
              return this;
          },
      
          preProcess: function () {
              return this;
          },
      
          process: function () {
              this.indexMapping = [];
      
              return this.preProcess().processSide('left').processSide('right').postProcess();
          },
      
          processSide: function (dir) {
              var that    = this,
                  invertX = (dir === 'right'),
                  cache = this.data[dir];
      
              // @see CABLING-49 Implement proper calculation for 'separation'.
              var tree = d3.tree()
                  .separation((a, b) => Math.max(1, (a.children || []).length, (b.children || []).length))
                  .nodeSize([
                      (that.options.nodeHeight + that.options.nodeMarginY),
                      (that.options.nodeWidth + that.options.nodeMarginX)
                  ]);
      
              var root = d3.stratify()(cache)
                  .sort(function (a, b) {
                      // First sort by 'connected-index' to keep internally wired connectors near by each other.
                      if (dir === 'right' && a.data.siblingId && b.data.siblingId && a.data.siblingId !== b.data.siblingId) {
                          // @see CABLING-58 Use the newly created indexMapping to find the proper index.
                          return this.indexMapping.findIndex((d) => d == a.data.siblingId) - this.indexMapping.findIndex((d) => d == b.data.siblingId);
                      }
      
                      // @see  CABLING-36  Any "unconnected" connector will be moved down, so that no wires cross each other.
                      if (a.data.siblingId && !b.data.siblingId) {
                          return -1;
                      }
      
                      if (!a.data.siblingId && b.data.siblingId) {
                          return 1;
                      }
      
                      // @see CABLING-19 Following code will try to sort the connectors in a more natural way!
      
                      // First we split by "non-word" characters (like dots, slashes etc.).
                      var aParts = a.data.title.split(/\W/),
                          bParts = b.data.title.split(/\W/),
                          minParts = Math.min(aParts.length, bParts.length), comparison, i;
      
                      for (i = 0; i < minParts; i++) {
                          if (isNumeric(aParts[i]) && isNumeric(bParts[i])) {
                              comparison = aParts[i] - bParts[i];
                          } else {
                              comparison = aParts[i].localeCompare(bParts[i]);
                          }
      
                          if (comparison !== 0) {
                              return comparison;
                          }
                      }
      
                      // If nothing worked out, use the default:
                      return a.data.title.localeCompare(b.data.title);
                  }.bind(this));
      
              if (dir === 'left') {
                  this.indexMapping = root.children.map((d) => d.data.id);
              }
      
              var connectorTranslate = function (d) {
                  if (that.options.clickableConnectors) {
                      if (d.parent === null || d.parent && d.parent.id === 'root') {
                          return 'translate(' + (invertX ? 5 : -5) + ',0)';
                      }
      
                      if (d.data.inner) {
                          return 'translate(' + (invertX ? -5 : that.options.nodeWidth+5) + ',0)';
                      } else {
                          return 'translate(' + (invertX ? that.options.nodeWidth+5 : -(that.options.nodeWidth+5)) + ',0)';
                      }
                  } else {
                      if (d.parent === null || d.parent && d.parent.id === 'root') {
                          return 'translate(0,0)';
                      }
      
                      if (d.data.inner) {
                          return 'translate(' + (invertX ? 0 : that.options.nodeWidth) + ',0)';
                      } else {
                          return 'translate(' + (invertX ? that.options.nodeWidth : -that.options.nodeWidth) + ',0)';
                      }
                  }
              };
      
              var linkData = tree(root).links();
      
              var cable = that.vis
                  .selectAll(".cable.cable-" + dir)
                  .data(linkData);
      
              cable
                  .enter().append("path")
                  .style("opacity", 0)
                  .attr("class", "cable cable-" + dir);
      
              // After the link data is assigned, we reduce the data to only hold cable information.
              linkData = linkData.filter(function (d) {
                  return d.source.id !== 'root' && d.source.data.outer;
              });
      
              // @see  CABLING-35  Show internal cabling
              that.vis
                  .selectAll('.internal-link.internal-link-' + dir)
                  .data((root.children || []).filter((d) => d.data.wiredToItself))
                  .enter()
                  .append("path")
                  .style("opacity", 0)
                  .attr('class', 'internal-link internal-link-' + dir)
                  .attr('d', function (d) {
                      const connection = (root.children || []).find(function (conn) { return conn.id == d.data.connectorId});
      
                      if (!connection) {
                          return '';
                      }
      
                      d.y -= 20;
                      var xBetween = d.y + 30;
                      const yBetween = ((d.x + connection.x) / 2);
      
                      // Mirror the coordinates for the left side.
                      if (dir === 'left') {
                          d.y *= -1;
                          xBetween *= -1;
                      }
      
      
                      return 'M' + d.y + ',' + d.x +
                             ' C' + xBetween + ',' + d.x + ' ' + xBetween + ',' + yBetween + ' ' + xBetween + ',' + yBetween;
                  })
                  .interrupt()
                  .transition().duration(500)
                  .transition().duration(250)
                  .style("opacity", 1);
      
              var cableLabel = that.vis
                  .selectAll(".linkname.linkname-" + dir)
                  .data(linkData)
                  .enter()
                  .append("g")
                  .style("opacity", 0)
                  .attr("class", "linkname linkname-" + dir)
                  .attr('data-id', function (d) {
                      return d.source.data.cableId;
                  });
      
              that.vis
                  .selectAll(".cable.cable-" + dir)
                  .classed('cable-root', function (d) {
                      return (d.source.id === 'root');
                  })
                  .interrupt().transition().duration(500)
                  .attr("d", function (d) {
                      var fromX  = (d.source.y + (that.options.showCableLabels ? 50 : 125)),
                          fromX2 = (fromX+20),
                          fromX3 = (fromX+40),
                          fromY  = d.source.x,
                          toX    = (d.target.y + (that.options.showCableLabels ? 50 : 125)),
                          toY    = d.target.x;
      
                      if (d.target.data.doubling) {
                          toX += 20;
                      }
      
                      if (!invertX) {
                          fromX *= -1;
                          fromX2 *= -1;
                          fromX3 *= -1;
                          toX *= -1;
                      }
      
                      return "M" + fromX + "," + fromY +
                             "C" + fromX2 + "," + fromY + " " + fromX2 + "," + toY + " " + fromX3 + "," + toY +
                             "L" + toX + "," + toY;
                  })
                  .transition().duration(250)
                  .style("opacity", 1)
                  .style('stroke-dasharray', function (d) {
                      if (d.target.data.doubling) {
                          return '2,2';
                      }
      
                      return null;
                  });
      
              cable
                  .exit()
                  .interrupt().transition().duration(500)
                  .style("opacity", 0)
                  .remove();
      
              cableLabel
                  .append('rect')
                  .attr('class', 'mouse-pointer')
                  .attr('height', 14)
                  .attr('width', 125)
                  .attr('data-id', function (d) {
                      return d.source.cableId
                  })
                  .on('click', that.selectCableObject.bind(this));
      
              cableLabel
                  .append('text')
                  .attr('class', 'mouse-pointer')
                  .attr('dy', 10)
                  .attr('x', 62.5)
                  .style('text-anchor', 'middle')
                  .text(function (d) {
                      return d.source.data.cableTitle;
                  })
                  .on('click', that.selectCableObject.bind(this));
      
              that.vis
                  .selectAll(".linkname.linkname-" + dir)
                  .classed('hide', !that.options.showCableLabels)
                  .interrupt().transition().duration(550)
                  .style('opacity', (that.options.showCableLabels ? 1 : 0))
                  .attr('transform', function (d) {
                      var translateX = d.source.y,
                          translateY = d.source.x;
      
                      if (invertX) {
                          translateX = translateX + 85;
                      } else {
                          translateX = translateX + that.options.nodeWidth + 65;
                      }
      
                      if (d.source.parent.id === 'root') {
                          translateX = translateX - 65;
                      }
      
                      return 'translate(' + (invertX ? '' : '-') + translateX + ',' + translateY + ')';
                  });
      
              var node = that.vis
                  .selectAll(".node.node-" + dir)
                  .data(root.descendants());
      
              var nodeEnter = node
                  .enter().append("g")
                  .style("opacity", 0)
                  .attr("class", "node node-" + dir)
                  .classed('root', function (d) {
                      return d.parent === null;
                  })
                  .attr('data-id', function (d) {
                      return d.id;
                  });
      
              // This will append newly created and already existing nodes.
              this.vis
                  .selectAll('.node.node-' + dir)
                  .transition().duration(500)
                  .style("opacity", 1)
                  .attr("transform", function (d) {
                      var translateX = d.y,
                          translateY = d.x;
      
                      if (d.parent === null) {
                          return "translate(" + (invertX ? 0 : -that.options.nodeWidth) + "," + translateY + ")";
                      }
      
                      if (d.parent && d.parent.id === 'root') {
                          return "translate(" + (invertX ? that.options.nodeWidth : -that.options.nodeWidth) + "," + translateY + ")";
                      }
      
                      if (d.data.inner) {
                          if (!invertX) {
                              translateX = -(translateX + that.options.nodeWidth);
                          }
                      } else {
                          if (!invertX) {
                              translateX = -(translateX + that.options.nodeWidth) + (that.options.nodeWidth + that.options.nodeMarginX);
                          } else {
                              translateX -= that.options.nodeMarginX;
                          }
                      }
      
                      return "translate(" + translateX + "," + translateY + ")";
                  });
      
              node.exit()
                  .interrupt().transition().duration(500)
                  .style("opacity", 0)
                  .remove();
      
              var container = nodeEnter
                  .filter(function (d) {
                      // Only draw boxes for the "root" element and "left" connectors.
                      return d.parent === null || d.data.inner;
                  })
                  .each(function (d) {
                      // Prepare some data for each node.
                      d.childNum = (d.children ? d.children.length : 1);
      
                      d.minX = d.y;
                      d.maxX = d.y + (that.options.nodeWidth * 2);
      
                      if (d.children) {
                          d.minY = d.children.first().x;
                          d.maxY = d.children.last().x + that.options.nodeHeight;
                      } else {
                          d.minY = d.x;
                          d.maxY = d.x + that.options.nodeHeight;
                      }
      
                      d.width = d.maxX - d.minX;
                      d.height = d.maxY - d.minY;
                      d.translateX = 0;
                      d.translateY = -(d.height / 2);
      
                      if (d.parent === null) {
                          if (that.rootContainer.height < d.height) {
                              that.rootContainer.height = d.height;
                              that.rootContainer.translateX = d.translateX;
                              that.rootContainer.translateY = d.translateY;
                          }
      
                          d.width = that.options.nodeWidth;
                      }
                  });
      
              container
                  .append('rect')
                  .attr('class', 'title mouse-pointer')
                  .attr('width', function (d) {
                      return d.width;
                  })
                  .attr('height', function (d) {
                      return 20;
                  })
                  .attr('data-object-id', function (d) {
                      return d.data.objectId;
                  })
                  .attr('transform', function (d) {
                      var translateX = d.translateX,
                          translateY = d.translateY;
      
                      if (d.parent && d.parent !== 'root') {
                          if (!invertX) {
                              translateX = -that.options.nodeWidth;
                          }
                      }
      
                      return 'translate(' + translateX + ',' + (translateY - 20) + ')';
                  })
                  .on('click', that.selectObject.bind(this));
      
              container
                  .append('rect')
                  .attr('class', 'body')
                  .attr('width', function (d) {
                      return d.width;
                  })
                  .attr('height', function (d) {
                      return d.height;
                  })
                  .attr('data-conn', function (d) {
                      return d.data.title;
                  })
                  .attr('transform', function (d) {
                      var translateX = d.translateX,
                          translateY = d.translateY;
      
                      if (d.parent && d.parent !== 'root') {
                          if (!invertX) {
                              translateX = -that.options.nodeWidth;
                          }
                      }
      
                      return 'translate(' + translateX + ',' + translateY + ')';
                  })
                  .attr('fill', function (d) {
                      if (!that.options.objectTypeData.hasOwnProperty(d.data.objectTypeId)) {
                          return that.options.undefinedObjectType.color;
                      }
      
                      return that.options.objectTypeData[d.data.objectTypeId].color || that.options.undefinedObjectType.color
                  });
      
              nodeEnter
                  .filter(function (d) {
                      return d.parent !== null;
                  })
                  .append('circle')
                  .attr('data-connector-id', function (d) {
                      return d.data.id;
                  })
                  .attr('class', 'connector connector-' + dir)
                  .attr('transform', connectorTranslate)
                  .on('click', that.selectConnector.bind(this));
      
              nodeEnter
                  .filter(function (d) {
                      return d.parent !== null;
                  })
                  .append('circle')
                  .attr('data-connector-id', function (d) {
                      return d.data.id;
                  })
                  .style('opacity', 0)
                  .attr('class', 'connector-inner hide connector-inner-' + dir)
                  .attr('r', 4)
                  .attr('transform', connectorTranslate)
                  .on('click', that.selectConnector.bind(this));
      
              that.vis
                  .selectAll('circle.connector.connector-' + dir)
                  .classed('mouse-pointer', that.options.clickableConnectors)
                  .interrupt().transition().duration(500)
                  .attr('r', (that.options.clickableConnectors ? 10: 5))
                  .attr('fill', function (d) {
                      var connectorType = that.options.connectorTypeData[d.data.connectorType];
      
                      return connectorType ? connectorType.color : that.options.undefinedConnectorType.color;
                  })
                  .attr('transform', connectorTranslate)
                  .style('stroke-width', (that.options.clickableConnectors ? 3 : 1));
      
              that.vis
                  .selectAll('circle.connector-inner.connector-inner-' + dir)
                  .classed('mouse-pointer', that.options.clickableConnectors)
                  .interrupt().transition().duration(500)
                  .attr('transform', connectorTranslate)
                  .style('opacity', (that.options.clickableConnectors ? 1 : 0));
      
              container
                  .filter(function (d) {
                      return (d.parent !== null || invertX);
                  })
                  .append('text')
                  .attr('class', 'title mouse-pointer')
                  .attr("dy", function (d) {
                      if (d.id === 'root') {
                          return -((that.rootContainer.height / 2) + 7);
                      }
      
                      return -((d.height / 2) + 7);
                  })
                  .attr("x", function (d) {
                      if (d.parent === null) {
                          return 0
                      }
      
                      return invertX ? that.options.nodeWidth : 0;
                  })
                  .style("text-anchor", 'middle')
                  .text(function (d) {
                      return d.data.objectTitle;
                  })
                  .on('click', that.selectObject.bind(this));
      
              nodeEnter
                  .append("text")
                  .attr('class', 'connector-' + dir + '-title')
                  .attr("dy", 3)
                  .attr("x", function (d) {
                      if (d.parent !== null && d.parent.id === 'root') {
                          return invertX ? -25 : 25;
                      }
      
                      if (d.data.outer) {
                          return invertX ? that.options.nodeWidth-10 : -(that.options.nodeWidth-10);
                      }
      
                      return invertX ? 10 : (that.options.nodeWidth-10);
                  })
                  .style("text-anchor", function (d) {
                      if (d.parent !== null && d.parent.id === 'root' || d.data.outer) {
                          return invertX ? 'end' : 'start';
                      }
      
                      return invertX ? "start" : "end";
                  })
                  .text(function (d) {
                      return that.textFormat.call(this, d.data.title, (that.options.nodeWidth - 15));
                  });
      
              // @see CABLING-56 Show the full title when hovering connector.
              nodeEnter.append('title').text(d => d.data.title);
      
              // After all calculations have been done, we set the root height.
              this.vis.selectAll('g.root rect.title')
                  .attr('transform', function (d) {
                      return 'translate(' + that.rootContainer.translateX + ',' + (that.rootContainer.translateY - 20) + ')';
                  });
      
              this.vis.selectAll('g.root rect.body')
                  .attr('height', function () {
                      return that.rootContainer.height;
                  })
                  .attr('transform', function (d) {
                      return 'translate(' + that.rootContainer.translateX + ',' + that.rootContainer.translateY + ')';
                  });
      
              this.vis.selectAll('rect.body')
                  .transition().duration(250)
                  .style('opacity', (this.options.displayWiring ? 0.5 : 1));
      
              this.vis
                  .selectAll(".cable.cable-root.cable-" + dir)
                  .each(function (d) {
                      d.fromX = (invertX ? '' : '-') + 100;
                      d.fromY = d.target.x;
                      d.toX = (invertX ? '' : '-') + (d.target.y + (that.options.showCableLabels ? 50 : 125));
                      d.toY = d.target.x;
      
                      // If a connector is internally wired, we draw the line to the middle (to visually "connect" them).
                      if (d.target.data.wired) {
                          d.fromX = 0; // -25;
                      }
      
                      // If a root connector is not connected, only show a small "stump".
                      if (!d.target.children) {
                          d.toX = parseInt((invertX ? '' : '-') + 200);
                      }
                  })
                  .attr('data-index', (d, i) => i)
                  .interrupt().transition().delay(500).duration(250)
                  .style('opacity', 1)
                  .attr('d', function (d) {
                      // @see  CABLING-35  Cut the 'self wired' connectors more short
                      if (d.target.data.wiredToItself || false) {
                          if (dir === 'right') {
                              d.toX -= 45;
                          } else {
                              d.toX += 45;
                          }
                      }
      
                      if (invertX && d.target.data.wired) {
                          const index = this.indexMapping.findIndex((i) => i == d.target.data.siblingId);
                          const $selection = this.vis.select('[data-index="' + index + '"]');
      
                          if (index >= 0 && $selection.size()) {
                              d.fromY = $selection.data()[0].fromY;
      
                              return "M" + d.fromX + "," + d.fromY +
                                     "C" + (d.fromX+20) + "," + d.fromY + " " + (d.fromX+20) + "," + d.toY + " " + (d.fromX+40) + "," + d.toY +
                                     "L" + d.toX + "," + d.toY;
                          }
                      }
      
                      return "M" + d.fromX + "," + d.fromY +
                             "L" + d.toX + "," + d.toY;
                  }.bind(this));
      
              this.vis.selectAll('.connector-' + dir + '-title')
                  .classed('stroked', this.options.displayWiring);
      
              return this;
          },
      
          postProcess: function () {
              if (Object.isFunction(this.options.onAfterProcess)) {
                  this.options.onAfterProcess.call(this);
              }
      
              return this;
          },
      
          setData: function (data) {
              this.data = data;
      
              return this;
          },
      
          selectObject: function (d) {
              var $object = this.vis.selectAll('[data-id="' + d.id + '"]');
      
              if (!$object.classed('clicked')) {
                  this.vis.selectAll('.clicked').classed('clicked', false);
      
                  $object.classed('clicked', true);
      
                  if (Object.isFunction(this.options.onObjSelect)) {
                      this.options.onObjSelect.call(this, d);
                  }
              }
      
              return this;
          },
      
          selectCableObject: function (d) {
              var $object = this.vis.selectAll('[data-id="' + d.source.data.cableId + '"]');
      
              if (!$object.classed('clicked')) {
                  this.vis.selectAll('.clicked').classed('clicked', false);
      
                  $object.classed('clicked', true);
      
                  if (Object.isFunction(this.options.onCableSelect)) {
                      this.options.onCableSelect.call(this, d);
                  }
              }
      
              return this;
          },
      
          selectConnector: function (d) {
              var $connector = this.vis.select('circle.connector-inner[data-connector-id="' + d.data.id + '"]');
      
              if (! this.options.clickableConnectors) {
                  return;
              }
      
              $connector.classed('hide', !$connector.classed('hide'));
      
              if (Object.isFunction(this.options.onConnectorSelect)) {
                  this.options.onConnectorSelect.call(this, d);
              }
      
              return this;
          },
      
          unselectConnectors: function (d) {
              var data = this.vis.selectAll('.connector-inner:not(.hide)').classed('hide', true).data();
      
              if (Object.isFunction(this.options.onConnectorUnselect)) {
                  this.options.onConnectorUnselect.call(this, data);
              }
      
              return this;
          },
      
          unselectObject: function (data) {
              if (Object.isFunction(this.options.onObjUnselect)) {
                  this.options.onObjUnselect.call(this, data);
              }
      
              return this;
          },
      
          center: function () {
              var translateX = (this.options.width / 2),
                  translateY = (this.options.height / 2);
      
              this.vis.attr('transform', "translate(" + translateX + "," + translateY + ")");
      
              this.svg.select('rect').call(this.zoom.transform, d3.zoomIdentity.translate(translateX, translateY));
      
              return this;
          }
      });
      

      Die Datei müsste mit diesem neuen Inhalt überschrieben werden 😄

      Das dürfte weiterhelfen!

      4b3a1091-f515-4bae-bfc1-f35baeca93b8-image.png

      VG Leo

      posted in Betrieb
      LFischerL
      LFischer