Subversion Repositories oidplus

Rev

Rev 1078 | Rev 1116 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. <?php
  2.  
  3. /*
  4.  * OIDplus 2.0
  5.  * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
  6.  *
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *     http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  */
  19.  
  20. namespace ViaThinkSoft\OIDplus;
  21.  
  22. // phpcs:disable PSR1.Files.SideEffects
  23. \defined('INSIDE_OIDPLUS') or die;
  24. // phpcs:enable PSR1.Files.SideEffects
  25.  
  26. abstract class OIDplusObject extends OIDplusBaseClass {
  27.         const UUID_NAMEBASED_NS_OidPlusMisc = 'ad1654e6-7e15-11e4-9ef6-78e3b5fc7f22';
  28.  
  29.         public static function parse($node_id) { // please overwrite this function!
  30.                 foreach (OIDplus::getEnabledObjectTypes() as $ot) {
  31.                         try {
  32.                                 $good = false;
  33.                                 if (get_parent_class($ot) == OIDplusObject::class) {
  34.                                         $reflector = new \ReflectionMethod($ot, 'parse');
  35.                                         $isImplemented = ($reflector->getDeclaringClass()->getName() === $ot);
  36.                                         if ($isImplemented) { // avoid endless loop if parse is not overriden
  37.                                                 $good = true;
  38.                                         }
  39.                                 }
  40.                                 // We need to do the workaround with "$good", otherwise PHPstan shows
  41.                                 // "Call to an undefined static method object::parse()"
  42.                                 if ($good && $obj = $ot::parse($node_id)) return $obj;
  43.                         } catch (\Exception $e) {}
  44.                 }
  45.                 return null;
  46.         }
  47.  
  48.         public function /*OIDplusAltId[]*/ getAltIds() {
  49.                 if ($this->isRoot()) return array();
  50.  
  51.                 $ids = array();
  52.  
  53.                 // Creates an OIDplus-Hash-OID
  54.                 if ($this->ns() != 'oid') {
  55.                         $sid = OIDplus::getSystemId(true);
  56.                         if (!empty($sid)) {
  57.                                 $ns_oid = $this->getPlugin()->getManifest()->getOid();
  58.                                 if (str_starts_with($ns_oid, '1.3.6.1.4.1.37476.2.5.2.')) {
  59.                                         // Official ViaThinkSoft object type plugins
  60.                                         // For backwards compatibility with existing IDs,
  61.                                         // set the hash_payload as '<namespace>:<id>'
  62.                                         $hash_payload = $this->nodeId(true);
  63.                                 } else {
  64.                                         // Third-party object type plugins
  65.                                         // Set the hash_payload as '<plugin oid>:<id>'
  66.                                         $hash_payload = $ns_oid.':'.$this->nodeId(false);
  67.                                 }
  68.                                 $oid = $sid . '.' . smallhash($hash_payload);
  69.                                 $ids[] = new OIDplusAltId('oid', $oid, _L('OIDplus Information Object OID'));
  70.                         }
  71.                 }
  72.  
  73.                 // Make a namebased UUID, but...
  74.                 // ... exclude GUID, because a GUID is already a GUID
  75.                 // ... exclude OID, because an OID already has a record UUID_NAMEBASED_NS_OID (defined by IETF) set by class OIDplusOid
  76.                 if (($this->ns() != 'guid') && ($this->ns() != 'oid')) {
  77.                         $ids[] = new OIDplusAltId('guid', gen_uuid_md5_namebased(self::UUID_NAMEBASED_NS_OidPlusMisc, $this->nodeId()), _L('Name based version 3 / MD5 UUID with namespace %1','UUID_NAMEBASED_NS_OidPlusMisc'));
  78.                         $ids[] = new OIDplusAltId('guid', gen_uuid_sha1_namebased(self::UUID_NAMEBASED_NS_OidPlusMisc, $this->nodeId()), _L('Name based version 5 / SHA1 UUID with namespace %1','UUID_NAMEBASED_NS_OidPlusMisc'));
  79.                 }
  80.  
  81.                 // Make a AID based on ViaThinkSoft schema
  82.                 // ... but not for OIDs below oid:1.3.6.1.4.1.37476.30.9, because these are the definition of these Information Object AIDs (which will be decoded in the OID object type plugin)
  83.                 if (($this->ns() != 'aid') && !str_starts_with($this->nodeId(true), 'oid:1.3.6.1.4.1.37476.30.9.')) {
  84.                         $sid = OIDplus::getSystemId(false);
  85.                         if (!empty($sid)) {
  86.                                 $ns_oid = $this->getPlugin()->getManifest()->getOid();
  87.                                 if (str_starts_with($ns_oid, '1.3.6.1.4.1.37476.2.5.2.')) {
  88.                                         // Official ViaThinkSoft object type plugins
  89.                                         // For backwards compatibility with existing IDs,
  90.                                         // set the hash_payload as '<namespace>:<id>'
  91.                                         $hash_payload = $this->nodeId(true);
  92.                                 } else {
  93.                                         // Third-party object type plugins
  94.                                         // Set the hash_payload as '<plugin oid>:<id>'
  95.                                         $hash_payload = $ns_oid.':'.$this->nodeId(false);
  96.                                 }
  97.  
  98.                                 $sid_hex = strtoupper(str_pad(dechex($sid),8,'0',STR_PAD_LEFT));
  99.                                 $obj_hex = strtoupper(str_pad(dechex(smallhash($hash_payload)),8,'0',STR_PAD_LEFT));
  100.                                 $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
  101.                                 $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
  102.                         }
  103.                 }
  104.  
  105.                 return $ids;
  106.         }
  107.  
  108.         public abstract static function objectTypeTitle();
  109.  
  110.         public abstract static function objectTypeTitleShort();
  111.  
  112.         public function getPlugin()/*: ?OIDplusObjectTypePlugin */ {
  113.                 $res = null;
  114.                 $plugins = OIDplus::getObjectTypePlugins();
  115.                 foreach ($plugins as $plugin) {
  116.                         if (get_class($this) == $plugin::getObjectTypeClassName($this)) {
  117.                                 return $plugin;
  118.                         }
  119.                 }
  120.                 return $res;
  121.         }
  122.  
  123.         public abstract static function ns();
  124.  
  125.         public abstract static function root();
  126.  
  127.         public abstract function isRoot();
  128.  
  129.         public abstract function nodeId($with_ns=true);
  130.  
  131.         public abstract function addString($str);
  132.  
  133.         public abstract function crudShowId(OIDplusObject $parent);
  134.  
  135.         public function crudInsertPrefix() {
  136.                 return '';
  137.         }
  138.  
  139.         public function crudInsertSuffix() {
  140.                 return '';
  141.         }
  142.  
  143.         public abstract function jsTreeNodeName(OIDplusObject $parent = null);
  144.  
  145.         public abstract function defaultTitle();
  146.  
  147.         public abstract function isLeafNode();
  148.  
  149.         public abstract function getContentPage(&$title, &$content, &$icon);
  150.  
  151.         public static function getRaRoots($ra_email=null) {
  152.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  153.  
  154.                 $out = array();
  155.  
  156.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  157.                         if (!$ra_email) {
  158.                                 $res = OIDplus::db()->query("select oChild.id as id, oChild.ra_email as child_mail, oParent.ra_email as parent_mail from ###objects as oChild ".
  159.                                                             "left join ###objects as oParent on oChild.parent = oParent.id ".
  160.                                                             "order by ".OIDplus::db()->natOrder('oChild.id'));
  161.                                 while ($row = $res->fetch_array()) {
  162.                                         if (!OIDplus::authUtils()->isRaLoggedIn($row['parent_mail']) && OIDplus::authUtils()->isRaLoggedIn($row['child_mail'])) {
  163.                                                 $x = self::parse($row['id']); // can be FALSE if namespace was disabled
  164.                                                 if ($x) $out[] = $x;
  165.                                         }
  166.                                 }
  167.                         } else {
  168.                                 $res = OIDplus::db()->query("select oChild.id as id from ###objects as oChild ".
  169.                                                             "left join ###objects as oParent on oChild.parent = oParent.id ".
  170.                                                             "where (".OIDplus::db()->getSlang()->isNullFunction('oParent.ra_email',"''")." <> ? and ".
  171.                                                             OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) or ".
  172.                                                             "      (oParent.ra_email is null and ".OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) ".
  173.                                                             "order by ".OIDplus::db()->natOrder('oChild.id'), array($ra_email, $ra_email, $ra_email));
  174.                                 while ($row = $res->fetch_array()) {
  175.                                         $x = self::parse($row['id']); // can be FALSE if namespace was disabled
  176.                                         if ($x) $out[] = $x;
  177.                                 }
  178.                         }
  179.                 } else {
  180.                         if (!$ra_email) {
  181.                                 $ra_mails_to_check = OIDplus::authUtils()->loggedInRaList();
  182.                                 if (count($ra_mails_to_check) == 0) return $out;
  183.                         } else {
  184.                                 $ra_mails_to_check = array($ra_email);
  185.                         }
  186.  
  187.                         self::buildObjectInformationCache();
  188.  
  189.                         foreach ($ra_mails_to_check as $check_ra_mail) {
  190.                                 $out_part = array();
  191.  
  192.                                 foreach (self::$object_info_cache as $id => $cacheitem) {
  193.                                         if ($cacheitem[self::CACHE_RA_EMAIL] == $check_ra_mail) {
  194.                                                 $parent = $cacheitem[self::CACHE_PARENT];
  195.                                                 if (!isset(self::$object_info_cache[$parent]) || (self::$object_info_cache[$parent][self::CACHE_RA_EMAIL] != $check_ra_mail)) {
  196.                                                         $out_part[] = $id;
  197.                                                 }
  198.                                         }
  199.                                 }
  200.  
  201.                                 natsort($out_part);
  202.  
  203.                                 foreach ($out_part as $id) {
  204.                                         $obj = self::parse($id);
  205.                                         if ($obj) $out[] = $obj;
  206.                                 }
  207.                         }
  208.                 }
  209.  
  210.                 return $out;
  211.         }
  212.  
  213.         public static function getAllNonConfidential() {
  214.                 $out = array();
  215.  
  216.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  217.                         $res = OIDplus::db()->query("select id from ###objects where confidential = ? order by ".OIDplus::db()->natOrder('id'), array(false));
  218.  
  219.                         while ($row = $res->fetch_array()) {
  220.                                 $obj = self::parse($row['id']); // will be NULL if the object type is not registered
  221.                                 if ($obj && (!$obj->isConfidential())) {
  222.                                         $out[] = $row['id'];
  223.                                 }
  224.                         }
  225.                 } else {
  226.                         self::buildObjectInformationCache();
  227.  
  228.                         foreach (self::$object_info_cache as $id => $cacheitem) {
  229.                                 $confidential = $cacheitem[self::CACHE_CONFIDENTIAL];
  230.                                 if (!$confidential) {
  231.                                         $obj = self::parse($id); // will be NULL if the object type is not registered
  232.                                         if ($obj && (!$obj->isConfidential())) {
  233.                                                 $out[] = $id;
  234.                                         }
  235.                                 }
  236.                         }
  237.                 }
  238.  
  239.                 return $out;
  240.         }
  241.  
  242.         public function isConfidential() {
  243.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  244.                         //static $confidential_cache = array();
  245.                         $curid = $this->nodeId();
  246.                         //$orig_curid = $curid;
  247.                         //if (isset($confidential_cache[$curid])) return $confidential_cache[$curid];
  248.                         // Recursively search for the confidential flag in the parents
  249.                         while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->any()) {
  250.                                 $row = $res->fetch_array();
  251.                                 if ($row['confidential']) {
  252.                                         //$confidential_cache[$curid] = true;
  253.                                         //$confidential_cache[$orig_curid] = true;
  254.                                         return true;
  255.                                 } else {
  256.                                         //$confidential_cache[$curid] = false;
  257.                                 }
  258.                                 $curid = $row['parent'];
  259.                                 //if (isset($confidential_cache[$curid])) {
  260.                                         //$confidential_cache[$orig_curid] = $confidential_cache[$curid];
  261.                                         //return $confidential_cache[$curid];
  262.                                 //}
  263.                         }
  264.  
  265.                         //$confidential_cache[$orig_curid] = false;
  266.                         return false;
  267.                 } else {
  268.                         self::buildObjectInformationCache();
  269.  
  270.                         $curid = $this->nodeId();
  271.                         // Recursively search for the confidential flag in the parents
  272.                         while (isset(self::$object_info_cache[$curid])) {
  273.                                 if (self::$object_info_cache[$curid][self::CACHE_CONFIDENTIAL]) return true;
  274.                                 $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
  275.                         }
  276.                         return false;
  277.                 }
  278.         }
  279.  
  280.         public function isChildOf(OIDplusObject $obj) {
  281.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  282.                         $curid = $this->nodeId();
  283.                         while (($res = OIDplus::db()->query("select parent from ###objects where id = ?", array($curid)))->any()) {
  284.                                 $row = $res->fetch_array();
  285.                                 if ($curid == $obj->nodeId()) return true;
  286.                                 $curid = $row['parent'];
  287.                         }
  288.                         return false;
  289.                 } else {
  290.                         self::buildObjectInformationCache();
  291.  
  292.                         $curid = $this->nodeId();
  293.                         while (isset(self::$object_info_cache[$curid])) {
  294.                                 if ($curid == $obj->nodeId()) return true;
  295.                                 $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
  296.                         }
  297.                         return false;
  298.                 }
  299.         }
  300.  
  301.         public function getChildren() {
  302.                 $out = array();
  303.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  304.                         $res = OIDplus::db()->query("select id from ###objects where parent = ?", array($this->nodeId()));
  305.                         while ($row = $res->fetch_array()) {
  306.                                 $obj = self::parse($row['id']);
  307.                                 if (!$obj) continue;
  308.                                 $out[] = $obj;
  309.                         }
  310.                 } else {
  311.                         self::buildObjectInformationCache();
  312.  
  313.                         foreach (self::$object_info_cache as $id => $cacheitem) {
  314.                                 $parent = $cacheitem[self::CACHE_PARENT];
  315.                                 if ($parent == $this->nodeId()) {
  316.                                         $obj = self::parse($id);
  317.                                         if (!$obj) continue;
  318.                                         $out[] = $obj;
  319.                                 }
  320.                         }
  321.                 }
  322.                 return $out;
  323.         }
  324.  
  325.         public function getRa() {
  326.                 return new OIDplusRA($this->getRaMail());
  327.         }
  328.  
  329.         public function userHasReadRights($ra_email=null) {
  330.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  331.  
  332.                 // If it is not confidential, everybody can read/see it.
  333.                 // Note: This also checks if superior OIDs are confidential.
  334.                 if (!$this->isConfidential()) return true;
  335.  
  336.                 if (!$ra_email) {
  337.                         // Admin may do everything
  338.                         if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
  339.  
  340.                         // If the RA is logged in, then they can see the OID.
  341.                         if (OIDplus::authUtils()->isRaLoggedIn($this->getRaMail())) return true;
  342.                 } else {
  343.                         // If this OID belongs to the requested RA, then they may see it.
  344.                         if ($this->getRaMail() == $ra_email) return true;
  345.                 }
  346.  
  347.                 // If someone has rights to an object below our confidential node,
  348.                 // we let him see the confidential node,
  349.                 // Otherwise he could not browse through to his own node.
  350.                 $roots = $this->getRaRoots($ra_email);
  351.                 foreach ($roots as $root) {
  352.                         if ($root->isChildOf($this)) return true;
  353.                 }
  354.  
  355.                 return false;
  356.         }
  357.  
  358.         public function getIcon($row=null) {
  359.                 $namespace = $this->ns(); // must use $this, not self::, otherwise the virtual method will not be called
  360.  
  361.                 if (is_null($row)) {
  362.                         $ra_email = $this->getRaMail();
  363.                 } else {
  364.                         $ra_email = $row['ra_email'];
  365.                 }
  366.                 // TODO: have different icons for Leaf-Nodes
  367.  
  368.                 $dirs = glob(OIDplus::localpath().'plugins/'.'*'.'/objectTypes/'.$namespace.'/');
  369.  
  370.                 if (count($dirs) == 0) return null; // default icon (folder)
  371.  
  372.                 $dir = substr($dirs[0], strlen(OIDplus::localpath()));
  373.  
  374.                 // We use $this:: instead of self:: , because we want to call the overridden methods
  375.                 if (OIDplus::authUtils()->isRaLoggedIn($ra_email)) {
  376.                         $icon = $dir.'/'.$this::treeIconFilename('own');
  377.                 } else {
  378.                         $icon = $dir.'/'.$this::treeIconFilename('general');
  379.                 }
  380.  
  381.                 if (!file_exists($icon)) return null; // default icon (folder)
  382.  
  383.                 return $icon;
  384.         }
  385.  
  386.         public static function exists(string $id) {
  387.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  388.                         $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
  389.                         return $res->any();
  390.                 } else {
  391.                         self::buildObjectInformationCache();
  392.                         return isset(self::$object_info_cache[$id]);
  393.                 }
  394.         }
  395.  
  396.         // Get parent gives the next possible parent which is EXISTING in OIDplus
  397.         // It does not give the immediate parent
  398.         public function getParent()/*: ?OIDplusObject*/ {
  399.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  400.                         $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
  401.                         if (!$res->any()) return null;
  402.                         $row = $res->fetch_array();
  403.                         $parent = $row['parent'];
  404.                         $obj = OIDplusObject::parse($parent);
  405.                         if ($obj) return $obj;
  406.                         // TODO: Also implement one_up() like below
  407.                 } else {
  408.                         self::buildObjectInformationCache();
  409.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  410.                                 $parent = self::$object_info_cache[$this->nodeId()][self::CACHE_PARENT];
  411.                                 $obj = OIDplusObject::parse($parent);
  412.                                 if ($obj) return $obj;
  413.                         }
  414.  
  415.                         // If this OID does not exist, the SQL query "select parent from ..." does not work. So we try to find the next possible parent using one_up()
  416.                         $cur = $this->one_up();
  417.                         if (!$cur) return null;
  418.                         do {
  419.                                 // findFitting() checks if that OID exists
  420.                                 if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
  421.  
  422.                                 $prev = $cur;
  423.                                 $cur = $cur->one_up();
  424.                                 if (!$cur) return null;
  425.                         } while ($prev != $cur);
  426.                 }
  427.                 return null;
  428.         }
  429.  
  430.         public function getRaMail() {
  431.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  432.                         $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
  433.                         if (!$res->any()) return null;
  434.                         $row = $res->fetch_array();
  435.                         return $row['ra_email'];
  436.                 } else {
  437.                         self::buildObjectInformationCache();
  438.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  439.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_RA_EMAIL];
  440.                         }
  441.                         return false;
  442.                 }
  443.         }
  444.  
  445.         public function getTitle() {
  446.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  447.                         $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
  448.                         if (!$res->any()) return null;
  449.                         $row = $res->fetch_array();
  450.                         return $row['title'];
  451.                 } else {
  452.                         self::buildObjectInformationCache();
  453.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  454.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_TITLE];
  455.                         }
  456.                         return false;
  457.                 }
  458.         }
  459.  
  460.         public function getDescription() {
  461.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  462.                         $res = OIDplus::db()->query("select description from ###objects where id = ?", array($this->nodeId()));
  463.                         if (!$res->any()) return null;
  464.                         $row = $res->fetch_array();
  465.                         return $row['description'];
  466.                 } else {
  467.                         self::buildObjectInformationCache();
  468.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  469.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_DESCRIPTION];
  470.                         }
  471.                         return false;
  472.                 }
  473.         }
  474.  
  475.         public function getComment() {
  476.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  477.                         $res = OIDplus::db()->query("select comment from ###objects where id = ?", array($this->nodeId()));
  478.                         if (!$res->any()) return null;
  479.                         $row = $res->fetch_array();
  480.                         return $row['comment'];
  481.                 } else {
  482.                         self::buildObjectInformationCache();
  483.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  484.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_COMMENT];
  485.                         }
  486.                         return false;
  487.                 }
  488.         }
  489.  
  490.         public function getCreatedTime() {
  491.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  492.                         $res = OIDplus::db()->query("select created from ###objects where id = ?", array($this->nodeId()));
  493.                         if (!$res->any()) return null;
  494.                         $row = $res->fetch_array();
  495.                         return $row['created'];
  496.                 } else {
  497.                         self::buildObjectInformationCache();
  498.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  499.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_CREATED];
  500.                         }
  501.                         return false;
  502.                 }
  503.         }
  504.  
  505.         public function getUpdatedTime() {
  506.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  507.                         $res = OIDplus::db()->query("select updated from ###objects where id = ?", array($this->nodeId()));
  508.                         if (!$res->any()) return null;
  509.                         $row = $res->fetch_array();
  510.                         return $row['updated'];
  511.                 } else {
  512.                         self::buildObjectInformationCache();
  513.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  514.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_UPDATED];
  515.                         }
  516.                         return false;
  517.                 }
  518.         }
  519.  
  520.         public function userHasParentalWriteRights($ra_email=null) {
  521.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  522.  
  523.                 if (!$ra_email) {
  524.                         if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
  525.                 }
  526.  
  527.                 $objParent = $this->getParent();
  528.                 if (!$objParent) return false;
  529.                 return $objParent->userHasWriteRights($ra_email);
  530.         }
  531.  
  532.         public function userHasWriteRights($ra_email=null) {
  533.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  534.  
  535.                 if (!$ra_email) {
  536.                         if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
  537.                         return OIDplus::authUtils()->isRaLoggedIn($this->getRaMail());
  538.                 } else {
  539.                         return $this->getRaMail() == $ra_email;
  540.                 }
  541.         }
  542.  
  543.         public function distance($to) {
  544.                 return null; // not implemented
  545.         }
  546.  
  547.         public function equals($obj) {
  548.                 if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
  549.                 if (!($obj instanceof $this)) return false;
  550.  
  551.                 $distance = $this->distance($obj);
  552.                 if (is_numeric($distance)) return $distance === 0; // if the distance function is implemented, use it
  553.  
  554.                 return $this->nodeId() == $obj->nodeId(); // otherwise compare the node id case-sensitive
  555.         }
  556.  
  557.         public static function findFitting(string $id) {
  558.                 $obj = OIDplusObject::parse($id);
  559.                 if (!$obj) return false; // e.g. if ObjectType plugin is disabled
  560.  
  561.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  562.                         $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
  563.                         while ($row = $res->fetch_object()) {
  564.                                 $test = OIDplusObject::parse($row->id);
  565.                                 if ($obj->equals($test)) return $test;
  566.                         }
  567.                         return false;
  568.                 } else {
  569.                         self::buildObjectInformationCache();
  570.                         foreach (self::$object_info_cache as $id => $cacheitem) {
  571.                                 if (strpos($id, $obj->ns().':') === 0) {
  572.                                         $test = OIDplusObject::parse($id);
  573.                                         if ($obj->equals($test)) return $test;
  574.                                 }
  575.                         }
  576.                         return false;
  577.                 }
  578.         }
  579.  
  580.         public function one_up() {
  581.                 return null; // not implemented
  582.         }
  583.  
  584.         // Caching stuff
  585.  
  586.         protected static $object_info_cache = null;
  587.  
  588.         public static function resetObjectInformationCache() {
  589.                 self::$object_info_cache = null;
  590.         }
  591.  
  592.         const CACHE_ID = 'id';
  593.         const CACHE_PARENT = 'parent';
  594.         const CACHE_TITLE = 'title';
  595.         const CACHE_DESCRIPTION = 'description';
  596.         const CACHE_RA_EMAIL = 'ra_email';
  597.         const CACHE_CONFIDENTIAL = 'confidential';
  598.         const CACHE_CREATED = 'created';
  599.         const CACHE_UPDATED = 'updated';
  600.         const CACHE_COMMENT = 'comment';
  601.  
  602.         private static function buildObjectInformationCache() {
  603.                 if (is_null(self::$object_info_cache)) {
  604.                         self::$object_info_cache = array();
  605.                         $res = OIDplus::db()->query("select * from ###objects");
  606.                         while ($row = $res->fetch_array()) {
  607.                                 self::$object_info_cache[$row['id']] = $row;
  608.                         }
  609.                 }
  610.         }
  611.  
  612.         // override this function if you want your object type to save
  613.         // attachments in directories with easy names.
  614.         // Take care that your custom directory name will not allow jailbreaks (../) !
  615.         public function getDirectoryName() {
  616.                 if ($this->isRoot()) return $this->ns();
  617.                 return $this->getLegacyDirectoryName();
  618.         }
  619.  
  620.         public final function getLegacyDirectoryName() {
  621.                 if ($this::ns() == 'oid') {
  622.                         $oid = $this->nodeId(false);
  623.                 } else {
  624.                         $oid = null;
  625.                         $alt_ids = $this->getAltIds();
  626.                         foreach ($alt_ids as $alt_id) {
  627.                                 if ($alt_id->getNamespace() == 'oid') {
  628.                                         $oid = $alt_id->getId();
  629.                                         break; // we prefer the first OID (for GUIDs, the first OID is the OIDplus-OID, and the second OID is the UUID OID)
  630.                                 }
  631.                         }
  632.                 }
  633.  
  634.                 if (!is_null($oid) && ($oid != '')) {
  635.                         // For OIDs, it is the OID, for other identifiers
  636.                         // it it the OID alt ID (generated using the SystemID)
  637.                         return str_replace('.', '_', $oid);
  638.                 } else {
  639.                         // Can happen if you don't have a system ID (due to missing OpenSSL plugin)
  640.                         return md5($this->nodeId(true)); // we don't use $id, because $this->nodeId(true) is possibly more canonical than $id
  641.                 }
  642.         }
  643.  
  644.         public static function treeIconFilename($mode) {
  645.                 // for backwards-compatibility with older plugins
  646.                 return 'img/treeicon_'.$mode.'.png';
  647.         }
  648.  
  649. }
  650.