Subversion Repositories oidplus

Rev

Rev 418 | Rev 426 | 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 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. abstract class OIDplusObject {
  21.         const UUID_NAMEBASED_NS_OidPlusMisc = 'ad1654e6-7e15-11e4-9ef6-78e3b5fc7f22';
  22.  
  23.         public static function parse($node_id) { // please overwrite this function!
  24.                 // TODO: in case we are not calling this class directly, check if function is overwritten and throw exception otherwise
  25.                 foreach (OIDplus::getEnabledObjectTypes() as $ot) {
  26.                         if ($obj = $ot::parse($node_id)) return $obj;
  27.                 }
  28.                 return null;
  29.         }
  30.  
  31.         public function /*OIDplusAltId[]*/ getAltIds() {
  32.                 if ($this->isRoot()) return array();
  33.  
  34.                 $ids = array();
  35.                 if ($this->ns() != 'oid') {
  36.                         // Creates an OIDplus-Hash-OID
  37.                         // If the object type has a better way of defining an OID, please override this method!
  38.                         $sid = OIDplus::getSystemId(true);
  39.                         if (!empty($sid)) {
  40.                                 $oid = $sid . '.' . smallhash($this->nodeId());
  41.                                 $ids[] = new OIDplusAltId('oid', $oid, _L('OIDplus Information Object ID'));
  42.                         }
  43.                 }
  44.                 if ($this->ns() != 'guid') {
  45.                         $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'));
  46.                         $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'));
  47.                 }
  48.                 return $ids;
  49.         }
  50.  
  51.         public abstract static function objectTypeTitle();
  52.  
  53.         public abstract static function objectTypeTitleShort();
  54.  
  55.         public abstract static function ns();
  56.  
  57.         public abstract static function root();
  58.  
  59.         public abstract function isRoot();
  60.  
  61.         public abstract function nodeId($with_ns=true);
  62.  
  63.         public abstract function addString($str);
  64.  
  65.         public abstract function crudShowId(OIDplusObject $parent);
  66.  
  67.         public abstract function crudInsertPrefix();
  68.  
  69.         public abstract function jsTreeNodeName(OIDplusObject $parent = null);
  70.  
  71.         public abstract function defaultTitle();
  72.  
  73.         public abstract function isLeafNode();
  74.  
  75.         public abstract function getContentPage(&$title, &$content, &$icon);
  76.  
  77.         public static function getRaRoots($ra_email=null) {
  78.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  79.  
  80.                 $out = array();
  81.  
  82.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  83.                         if (is_null($ra_email)) {
  84.                                 $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 ".
  85.                                                             "left join ###objects as oParent on oChild.parent = oParent.id ".
  86.                                                             "order by ".OIDplus::db()->natOrder('oChild.id'));
  87.                                 while ($row = $res->fetch_array()) {
  88.                                         if (!OIDplus::authUtils()::isRaLoggedIn($row['parent_mail']) && OIDplus::authUtils()::isRaLoggedIn($row['child_mail'])) {
  89.                                                 $x = self::parse($row['id']); // can be FALSE if namespace was disabled
  90.                                                 if ($x) $out[] = $x;
  91.                                         }
  92.                                 }
  93.                         } else {
  94.                                 $res = OIDplus::db()->query("select oChild.id as id from ###objects as oChild ".
  95.                                                             "left join ###objects as oParent on oChild.parent = oParent.id ".
  96.                                                             "where (ifnull(oParent.ra_email,'') <> ? and ifnull(oChild.ra_email,'') = ?) or ".
  97.                                                             "      (oParent.ra_email is null and ifnull(oChild.ra_email,'') = ?) ".
  98.                                                             "order by ".OIDplus::db()->natOrder('oChild.id'), array($ra_email, $ra_email, $ra_email));
  99.                                 while ($row = $res->fetch_array()) {
  100.                                         $x = self::parse($row['id']); // can be FALSE if namespace was disabled
  101.                                         if ($x) $out[] = self::parse($row['id']);
  102.                                 }
  103.                         }
  104.                 } else {
  105.                         if (is_null($ra_email)) {
  106.                                 $ra_mails_to_check = OIDplus::authUtils()->loggedInRaList();
  107.                                 if (count($ra_mails_to_check) == 0) return $out;
  108.                         } else {
  109.                                 $ra_mails_to_check = array($ra_email);
  110.                         }
  111.  
  112.                         self::buildObjectInformationCache();
  113.  
  114.                         foreach ($ra_mails_to_check as $check_ra_mail) {
  115.                                 $out_part = array();
  116.  
  117.                                 foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
  118.                                         // If the OID RA is the RA we are searching, then add the object to the choice list
  119.                                         if ($ra_email == $check_ra_mail) $out_part[] = $id;
  120.                                 }
  121.  
  122.                                 foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
  123.                                         if (isset(self::$object_info_cache[$parent])) {
  124.                                                 if (self::$object_info_cache[$parent][self::CACHE_RA_EMAIL] == $ra_email) {
  125.                                                         // if the parent has the same RA, then this OID cannot be a root => remove the element from the choice list
  126.                                                         foreach (array_keys($out_part, $id) as $key) unset($out_part[$key]);
  127.                                                 }
  128.                                         }
  129.                                 }
  130.  
  131.                                 natsort($out_part);
  132.  
  133.                                 foreach ($out_part as $id) {
  134.                                         $obj = self::parse($id);
  135.                                         if ($obj) $out[] = $obj;
  136.                                 }
  137.                         }
  138.                 }
  139.  
  140.                 return $out;
  141.         }
  142.  
  143.         public static function getAllNonConfidential() {
  144.                 $out = array();
  145.  
  146.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  147.                         $res = OIDplus::db()->query("select id from ###objects where confidential = '0' order by ".OIDplus::db()->natOrder('id'));
  148.  
  149.                         while ($row = $res->fetch_array()) {
  150.                                 $obj = self::parse($row['id']); // will be NULL if the object type is not registered
  151.                                 if ($obj && (!$obj->isConfidential())) {
  152.                                         $out[] = $row['id'];
  153.                                 }
  154.                         }
  155.                 } else {
  156.                         self::buildObjectInformationCache();
  157.  
  158.                         foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
  159.                                 if (!$confidential) {
  160.                                         $obj = self::parse($id); // will be NULL if the object type is not registered
  161.                                         if ($obj && (!$obj->isConfidential())) {
  162.                                                 $out[] = $id;
  163.                                         }
  164.                                 }
  165.                         }
  166.                 }
  167.  
  168.                 return $out;
  169.         }
  170.  
  171.         public function isConfidential() {
  172.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  173.                         $curid = $this->nodeId();
  174.                         $orig_curid = $curid;
  175.                         if (isset(self::$object_info_cache[$curid])) return self::$object_info_cache[$curid];
  176.                         // Recursively search for the confidential flag in the parents
  177.                         while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->num_rows() > 0) {
  178.                                 $row = $res->fetch_array();
  179.                                 if ($row['confidential']) {
  180.                                         self::$object_info_cache[$curid] = true;
  181.                                         self::$object_info_cache[$orig_curid] = true;
  182.                                         return true;
  183.                                 } else {
  184.                                         self::$object_info_cache[$curid] = false;
  185.                                 }
  186.                                 $curid = $row['parent'];
  187.                                 if (isset(self::$object_info_cache[$curid])) {
  188.                                         self::$object_info_cache[$orig_curid] = self::$object_info_cache[$curid];
  189.                                         return self::$object_info_cache[$curid];
  190.                                 }
  191.                         }
  192.  
  193.                         self::$object_info_cache[$orig_curid] = false;
  194.                         return false;
  195.                 } else {
  196.                         self::buildObjectInformationCache();
  197.  
  198.                         $curid = $this->nodeId();
  199.                         // Recursively search for the confidential flag in the parents
  200.                         while (isset(self::$object_info_cache[$curid])) {
  201.                                 if (self::$object_info_cache[$curid][self::CACHE_CONFIDENTIAL]) return true;
  202.                                 $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
  203.                         }
  204.                         return false;
  205.                 }
  206.         }
  207.  
  208.         public function isChildOf(OIDplusObject $obj) {
  209.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  210.                         $curid = $this->nodeId();
  211.                         while (($res = OIDplus::db()->query("select parent from ###objects where id = ?", array($curid)))->num_rows() > 0) {
  212.                                 $row = $res->fetch_array();
  213.                                 if ($curid == $obj->nodeId()) return true;
  214.                                 $curid = $row['parent'];
  215.                         }
  216.                         return false;
  217.                 } else {
  218.                         self::buildObjectInformationCache();
  219.  
  220.                         $curid = $this->nodeId();
  221.                         while (isset(self::$object_info_cache[$curid])) {
  222.                                 if ($curid == $obj->nodeId()) return true;
  223.                                 $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
  224.                         }
  225.                         return false;
  226.                 }
  227.         }
  228.  
  229.         public function getChildren() {
  230.                 $out = array();
  231.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  232.                         $res = OIDplus::db()->query("select id from ###objects where parent = ?", array($this->nodeId()));
  233.                         while ($row = $res->fetch_array()) {
  234.                                 $obj = self::parse($row['id']);
  235.                                 if (!$obj) continue;
  236.                                 $out[] = $obj;
  237.                         }
  238.                 } else {
  239.                         self::buildObjectInformationCache();
  240.  
  241.                         foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
  242.                                 if ($parent == $this->nodeId()) {
  243.                                         $obj = self::parse($id);
  244.                                         if (!$obj) continue;
  245.                                         $out[] = $obj;
  246.                                 }
  247.                         }
  248.                 }
  249.                 return $out;
  250.         }
  251.  
  252.         public function getRa() {
  253.                 return new OIDplusRA($this->getRaMail());
  254.         }
  255.  
  256.         public function userHasReadRights($ra_email=null) {
  257.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  258.  
  259.                 // If it is not confidential, everybody can read/see it.
  260.                 // Note: This also checks if superior OIDs are confidential.
  261.                 if (!$this->isConfidential()) return true;
  262.  
  263.                 if (is_null($ra_email)) {
  264.                         // Admin may do everything
  265.                         if (OIDplus::authUtils()::isAdminLoggedIn()) return true;
  266.  
  267.                         // If the RA is logged in, then they can see the OID.
  268.                         if (OIDplus::authUtils()::isRaLoggedIn($this->getRaMail())) return true;
  269.                 } else {
  270.                         // If this OID belongs to the requested RA, then they may see it.
  271.                         if ($this->getRaMail() == $ra_email) return true;
  272.                 }
  273.  
  274.                 // If someone has rights to an object below our confidential node,
  275.                 // we let him see the confidential node,
  276.                 // Otherwise he could not browse through to his own node.
  277.                 $roots = $this->getRaRoots($ra_email);
  278.                 foreach ($roots as $root) {
  279.                         if ($root->isChildOf($this)) return true;
  280.                 }
  281.  
  282.                 return false;
  283.         }
  284.  
  285.         public function getIcon($row=null) {
  286.                 $namespace = $this->ns(); // must use $this, not self::, otherwise the virtual method will not be called
  287.  
  288.                 if (is_null($row)) {
  289.                         $ra_email = $this->getRaMail();
  290.                 } else {
  291.                         $ra_email = $row['ra_email'];
  292.                 }
  293.                 // TODO: have different icons for Leaf-Nodes
  294.                 if (OIDplus::authUtils()::isRaLoggedIn($ra_email)) {
  295.                         $icon = 'plugins/objectTypes/'.$namespace.'/img/treeicon_own.png';
  296.                 } else {
  297.                         $icon = 'plugins/objectTypes/'.$namespace.'/img/treeicon_general.png';
  298.                 }
  299.                 if (!file_exists($icon)) $icon = null; // default icon (folder)
  300.                 return $icon;
  301.         }
  302.  
  303.         public static function exists($id) {
  304.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  305.                         $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
  306.                         return $res->num_rows() > 0;
  307.                 } else {
  308.                         self::buildObjectInformationCache();
  309.                         return isset(self::$object_info_cache[$id]);
  310.                 }
  311.         }
  312.  
  313.         // Get parent gives the next possible parent which is EXISTING in OIDplus
  314.         // It does not give the immediate parent
  315.         public function getParent() {
  316.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  317.                         $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
  318.                         if ($res->num_rows() == 0) return null;
  319.                         $row = $res->fetch_array();
  320.                         $parent = $row['parent'];
  321.                         $obj = OIDplusObject::parse($parent);
  322.                         if ($obj) return $obj;
  323.                         // TODO: Also implement one_up() like below
  324.                 } else {
  325.                         self::buildObjectInformationCache();
  326.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  327.                                 $parent = self::$object_info_cache[$this->nodeId()][self::CACHE_PARENT];
  328.                                 $obj = OIDplusObject::parse($parent);
  329.                                 if ($obj) return $obj;
  330.                         }
  331.  
  332.                         // 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()
  333.                         $cur = $this->one_up();
  334.                         if (!$cur) return null;
  335.                         do {
  336.                                 // findFitting() checks if that OID exists
  337.                                 if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
  338.  
  339.                                 $prev = $cur;
  340.                                 $cur = $cur->one_up();
  341.                                 if (!$cur) return null;
  342.                         } while ($prev != $cur);
  343.  
  344.                         return null;
  345.                 }
  346.         }
  347.  
  348.         public function getRaMail() {
  349.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  350.                         $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
  351.                         if ($res->num_rows() == 0) return null;
  352.                         $row = $res->fetch_array();
  353.                         return $row['ra_email'];
  354.                 } else {
  355.                         self::buildObjectInformationCache();
  356.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  357.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_RA_EMAIL];
  358.                         }
  359.                         return false;
  360.                 }
  361.         }
  362.  
  363.         public function getTitle() {
  364.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  365.                         $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
  366.                         if ($res->num_rows() == 0) return null;
  367.                         $row = $res->fetch_array();
  368.                         return $row['title'];
  369.                 } else {
  370.                         self::buildObjectInformationCache();
  371.                         if (isset(self::$object_info_cache[$this->nodeId()])) {
  372.                                 return self::$object_info_cache[$this->nodeId()][self::CACHE_TITLE];
  373.                         }
  374.                         return false;
  375.                 }
  376.         }
  377.  
  378.         public function userHasParentalWriteRights($ra_email=null) {
  379.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  380.  
  381.                 if (is_null($ra_email)) {
  382.                         if (OIDplus::authUtils()::isAdminLoggedIn()) return true;
  383.                 }
  384.  
  385.                 $objParent = $this->getParent();
  386.                 if (!$objParent) return false;
  387.                 return $objParent->userHasWriteRights($ra_email);
  388.         }
  389.  
  390.         public function userHasWriteRights($ra_email=null) {
  391.                 if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
  392.  
  393.                 if (is_null($ra_email)) {
  394.                         if (OIDplus::authUtils()::isAdminLoggedIn()) return true;
  395.                         return OIDplus::authUtils()::isRaLoggedIn($this->getRaMail());
  396.                 } else {
  397.                         return $this->getRaMail() == $ra_email;
  398.                 }
  399.         }
  400.  
  401.         public function distance($to) {
  402.                 return null; // not implemented
  403.         }
  404.  
  405.         public function equals($obj) {
  406.                 if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
  407.                 if (!($obj instanceof $this)) return false;
  408.  
  409.                 $distance = $this->distance($obj);
  410.                 if (is_numeric($distance)) return $distance === 0; // if the distance function is implemented, use it
  411.  
  412.                 return $this->nodeId() == $obj->nodeId(); // otherwise compare the node id case-sensitive
  413.         }
  414.  
  415.         public static function findFitting($id) {
  416.                 $obj = OIDplusObject::parse($id);
  417.                 if (!$obj) throw new OIDplusException(_L('findFitting: Parse failed'));
  418.  
  419.                 if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
  420.                         $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
  421.                         while ($row = $res->fetch_object()) {
  422.                                 $test = OIDplusObject::parse($row->id);
  423.                                 if ($obj->equals($test)) return $test;
  424.                         }
  425.                         return false;
  426.                 } else {
  427.                         self::buildObjectInformationCache();
  428.                         foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
  429.                                 if (strpos($id, $obj->ns().':') === 0) {
  430.                                         $test = OIDplusObject::parse($id);
  431.                                         if ($obj->equals($test)) return $test;
  432.                                 }
  433.                         }
  434.                         return false;
  435.                 }
  436.         }
  437.  
  438.         public function one_up() {
  439.                 return null; // not implemented
  440.         }
  441.  
  442.         // Caching stuff
  443.  
  444.         protected static $object_info_cache = null;
  445.  
  446.         public static function resetObjectInformationCache() {
  447.                 self::$object_info_cache = null;
  448.         }
  449.  
  450.         const CACHE_CONFIDENTIAL = 0; // TODO: An object would be better so you can use $cacheitem->isConfidential() etc.
  451.         const CACHE_PARENT = 1;
  452.         const CACHE_RA_EMAIL = 2;
  453.         const CACHE_TITLE = 3;
  454.  
  455.         private static function buildObjectInformationCache() {
  456.                 if (is_null(self::$object_info_cache)) {
  457.                         self::$object_info_cache = array();
  458.                         $res = OIDplus::db()->query("select id, parent, confidential, ra_email, title from ###objects");
  459.                         while ($row = $res->fetch_array()) {
  460.                                 self::$object_info_cache[$row['id']] = array($row['confidential'], $row['parent'], $row['ra_email'], $row['title']);
  461.                         }
  462.                 }
  463.         }
  464. }
  465.