Subversion Repositories oidplus

Rev

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