Subversion Repositories oidplus

Rev

Rev 416 | Rev 419 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 daniel-mar 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
 
261 daniel-mar 20
abstract class OIDplusObject {
21
        const UUID_NAMEBASED_NS_OidPlusMisc = 'ad1654e6-7e15-11e4-9ef6-78e3b5fc7f22';
150 daniel-mar 22
 
2 daniel-mar 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
227 daniel-mar 25
                foreach (OIDplus::getEnabledObjectTypes() as $ot) {
2 daniel-mar 26
                        if ($obj = $ot::parse($node_id)) return $obj;
27
                }
28
                return null;
29
        }
30
 
228 daniel-mar 31
        public function /*OIDplusAltId[]*/ getAltIds() {
193 daniel-mar 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!
227 daniel-mar 38
                        $sid = OIDplus::getSystemId(true);
193 daniel-mar 39
                        if (!empty($sid)) {
40
                                $oid = $sid . '.' . smallhash($this->nodeId());
360 daniel-mar 41
                                $ids[] = new OIDplusAltId('oid', $oid, _L('OIDplus Information Object ID'));
193 daniel-mar 42
                        }
43
                }
44
                if ($this->ns() != 'guid') {
360 daniel-mar 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'));
193 daniel-mar 47
                }
48
                return $ids;
83 daniel-mar 49
        }
50
 
2 daniel-mar 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
 
247 daniel-mar 61
        public abstract function nodeId($with_ns=true);
2 daniel-mar 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
 
16 daniel-mar 73
        public abstract function isLeafNode();
74
 
34 daniel-mar 75
        public abstract function getContentPage(&$title, &$content, &$icon);
2 daniel-mar 76
 
27 daniel-mar 77
        public static function getRaRoots($ra_email=null) {
115 daniel-mar 78
                if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
79
 
27 daniel-mar 80
                $out = array();
150 daniel-mar 81
 
261 daniel-mar 82
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
150 daniel-mar 83
                        if (is_null($ra_email)) {
261 daniel-mar 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 ".
150 daniel-mar 86
                                                            "order by ".OIDplus::db()->natOrder('oChild.id'));
236 daniel-mar 87
                                while ($row = $res->fetch_array()) {
150 daniel-mar 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 {
261 daniel-mar 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 ".
150 daniel-mar 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));
236 daniel-mar 99
                                while ($row = $res->fetch_array()) {
68 daniel-mar 100
                                        $x = self::parse($row['id']); // can be FALSE if namespace was disabled
150 daniel-mar 101
                                        if ($x) $out[] = self::parse($row['id']);
27 daniel-mar 102
                                }
2 daniel-mar 103
                        }
104
                } else {
150 daniel-mar 105
                        if (is_null($ra_email)) {
415 daniel-mar 106
                                $ra_mails_to_check = OIDplus::authUtils()->loggedInRaList();
150 daniel-mar 107
                                if (count($ra_mails_to_check) == 0) return $out;
108
                        } else {
109
                                $ra_mails_to_check = array($ra_email);
2 daniel-mar 110
                        }
150 daniel-mar 111
 
112
                        self::buildObjectInformationCache();
113
 
114
                        foreach ($ra_mails_to_check as $check_ra_mail) {
193 daniel-mar 115
                                $out_part = array();
150 daniel-mar 116
 
193 daniel-mar 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;
150 daniel-mar 120
                                }
121
 
193 daniel-mar 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]);
150 daniel-mar 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
                        }
2 daniel-mar 138
                }
150 daniel-mar 139
 
2 daniel-mar 140
                return $out;
141
        }
142
 
150 daniel-mar 143
        public static function getAllNonConfidential() {
144
                $out = array();
145
 
261 daniel-mar 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'));
150 daniel-mar 148
 
236 daniel-mar 149
                        while ($row = $res->fetch_array()) {
150 daniel-mar 150
                                $obj = self::parse($row['id']); // will be NULL if the object type is not registered
169 daniel-mar 151
                                if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 152
                                        $out[] = $row['id'];
153
                                }
2 daniel-mar 154
                        }
155
                } else {
150 daniel-mar 156
                        self::buildObjectInformationCache();
2 daniel-mar 157
 
193 daniel-mar 158
                        foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
150 daniel-mar 159
                                if (!$confidential) {
160
                                        $obj = self::parse($id); // will be NULL if the object type is not registered
169 daniel-mar 161
                                        if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 162
                                                $out[] = $id;
163
                                        }
164
                                }
2 daniel-mar 165
                        }
166
                }
167
 
168
                return $out;
169
        }
170
 
171
        public function isConfidential() {
261 daniel-mar 172
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
150 daniel-mar 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
261 daniel-mar 177
                        while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->num_rows() > 0) {
236 daniel-mar 178
                                $row = $res->fetch_array();
150 daniel-mar 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
169 daniel-mar 200
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 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;
2 daniel-mar 205
                }
206
        }
207
 
208
        public function isChildOf(OIDplusObject $obj) {
261 daniel-mar 209
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
150 daniel-mar 210
                        $curid = $this->nodeId();
261 daniel-mar 211
                        while (($res = OIDplus::db()->query("select parent from ###objects where id = ?", array($curid)))->num_rows() > 0) {
236 daniel-mar 212
                                $row = $res->fetch_array();
150 daniel-mar 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();
169 daniel-mar 221
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 222
                                if ($curid == $obj->nodeId()) return true;
223
                                $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
224
                        }
225
                        return false;
2 daniel-mar 226
                }
150 daniel-mar 227
        }
2 daniel-mar 228
 
150 daniel-mar 229
        public function getChildren() {
230
                $out = array();
261 daniel-mar 231
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
232
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array($this->nodeId()));
236 daniel-mar 233
                        while ($row = $res->fetch_array()) {
150 daniel-mar 234
                                $obj = self::parse($row['id']);
235
                                if (!$obj) continue;
236
                                $out[] = $obj;
237
                        }
238
                } else {
239
                        self::buildObjectInformationCache();
240
 
193 daniel-mar 241
                        foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
150 daniel-mar 242
                                if ($parent == $this->nodeId()) {
243
                                        $obj = self::parse($id);
244
                                        if (!$obj) continue;
245
                                        $out[] = $obj;
246
                                }
247
                        }
248
                }
249
                return $out;
2 daniel-mar 250
        }
251
 
115 daniel-mar 252
        public function getRa() {
150 daniel-mar 253
                return new OIDplusRA($this->getRaMail());
115 daniel-mar 254
        }
255
 
2 daniel-mar 256
        public function userHasReadRights($ra_email=null) {
115 daniel-mar 257
                if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
258
 
2 daniel-mar 259
                // If it is not confidential, everybody can read/see it.
416 daniel-mar 260
                // Note: This also checks if superior OIDs are confidential.
2 daniel-mar 261
                if (!$this->isConfidential()) return true;
262
 
263
                if (is_null($ra_email)) {
416 daniel-mar 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;
2 daniel-mar 269
                } else {
416 daniel-mar 270
                        // If this OID belongs to the requested RA, then they may see it.
150 daniel-mar 271
                        if ($this->getRaMail() == $ra_email) return true;
2 daniel-mar 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) {
20 daniel-mar 286
                $namespace = $this->ns(); // must use $this, not self::, otherwise the virtual method will not be called
2 daniel-mar 287
 
288
                if (is_null($row)) {
150 daniel-mar 289
                        $ra_email = $this->getRaMail();
290
                } else {
291
                        $ra_email = $row['ra_email'];
2 daniel-mar 292
                }
293
                // TODO: have different icons for Leaf-Nodes
150 daniel-mar 294
                if (OIDplus::authUtils()::isRaLoggedIn($ra_email)) {
2 daniel-mar 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
 
12 daniel-mar 303
        public static function exists($id) {
261 daniel-mar 304
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
305
                        $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
236 daniel-mar 306
                        return $res->num_rows() > 0;
150 daniel-mar 307
                } else {
308
                        self::buildObjectInformationCache();
309
                        return isset(self::$object_info_cache[$id]);
310
                }
12 daniel-mar 311
        }
312
 
415 daniel-mar 313
        // Get parent gives the next possible parent which is EXISTING in OIDplus
314
        // It does not give the immediate parent
2 daniel-mar 315
        public function getParent() {
261 daniel-mar 316
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
317
                        $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
251 daniel-mar 318
                        if ($res->num_rows() == 0) return null;
236 daniel-mar 319
                        $row = $res->fetch_array();
150 daniel-mar 320
                        $parent = $row['parent'];
321
                        $obj = OIDplusObject::parse($parent);
322
                        if ($obj) return $obj;
415 daniel-mar 323
                        // TODO: Also implement one_up() like below
150 daniel-mar 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
                        }
20 daniel-mar 331
 
150 daniel-mar 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();
418 daniel-mar 334
                        if (!$cur) return null;
150 daniel-mar 335
                        do {
415 daniel-mar 336
                                // findFitting() checks if that OID exists
150 daniel-mar 337
                                if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
20 daniel-mar 338
 
150 daniel-mar 339
                                $prev = $cur;
340
                                $cur = $cur->one_up();
418 daniel-mar 341
                                if (!$cur) return null;
150 daniel-mar 342
                        } while ($prev != $cur);
343
 
418 daniel-mar 344
                        return null;
150 daniel-mar 345
                }
2 daniel-mar 346
        }
347
 
348
        public function getRaMail() {
261 daniel-mar 349
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
350
                        $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
251 daniel-mar 351
                        if ($res->num_rows() == 0) return null;
236 daniel-mar 352
                        $row = $res->fetch_array();
150 daniel-mar 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
                }
2 daniel-mar 361
        }
362
 
192 daniel-mar 363
        public function getTitle() {
261 daniel-mar 364
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
365
                        $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
251 daniel-mar 366
                        if ($res->num_rows() == 0) return null;
236 daniel-mar 367
                        $row = $res->fetch_array();
192 daniel-mar 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
 
2 daniel-mar 378
        public function userHasParentalWriteRights($ra_email=null) {
115 daniel-mar 379
                if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
380
 
2 daniel-mar 381
                if (is_null($ra_email)) {
382
                        if (OIDplus::authUtils()::isAdminLoggedIn()) return true;
383
                }
384
 
385
                $objParent = $this->getParent();
386
                if (is_null($objParent)) return false;
387
                return $objParent->userHasWriteRights($ra_email);
388
        }
389
 
390
        public function userHasWriteRights($ra_email=null) {
115 daniel-mar 391
                if ($ra_email instanceof OIDplusRA) $ra_email = $ra_email->raEmail();
392
 
2 daniel-mar 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
        }
12 daniel-mar 400
 
401
        public function distance($to) {
402
                return null; // not implemented
403
        }
20 daniel-mar 404
 
405
        public function equals($obj) {
406
                if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
28 daniel-mar 407
                if (!($obj instanceof $this)) return false;
408
 
20 daniel-mar 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);
360 daniel-mar 417
                if (!$obj) throw new OIDplusException(_L('findFitting: Parse failed'));
20 daniel-mar 418
 
261 daniel-mar 419
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
420
                        $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
236 daniel-mar 421
                        while ($row = $res->fetch_object()) {
150 daniel-mar 422
                                $test = OIDplusObject::parse($row->id);
423
                                if ($obj->equals($test)) return $test;
424
                        }
425
                        return false;
426
                } else {
427
                        self::buildObjectInformationCache();
193 daniel-mar 428
                        foreach (self::$object_info_cache as $id => list($confidential, $parent, $ra_email, $title)) {
150 daniel-mar 429
                                if (strpos($id, $obj->ns().':') === 0) {
430
                                        $test = OIDplusObject::parse($id);
431
                                        if ($obj->equals($test)) return $test;
432
                                }
433
                        }
434
                        return false;
20 daniel-mar 435
                }
436
        }
437
 
438
        public function one_up() {
439
                return null; // not implemented
440
        }
150 daniel-mar 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
 
193 daniel-mar 450
        const CACHE_CONFIDENTIAL = 0; // TODO: An object would be better so you can use $cacheitem->isConfidential() etc.
150 daniel-mar 451
        const CACHE_PARENT = 1;
452
        const CACHE_RA_EMAIL = 2;
192 daniel-mar 453
        const CACHE_TITLE = 3;
150 daniel-mar 454
 
455
        private static function buildObjectInformationCache() {
456
                if (is_null(self::$object_info_cache)) {
457
                        self::$object_info_cache = array();
261 daniel-mar 458
                        $res = OIDplus::db()->query("select id, parent, confidential, ra_email, title from ###objects");
236 daniel-mar 459
                        while ($row = $res->fetch_array()) {
192 daniel-mar 460
                                self::$object_info_cache[$row['id']] = array($row['confidential'], $row['parent'], $row['ra_email'], $row['title']);
150 daniel-mar 461
                        }
462
                }
463
        }
415 daniel-mar 464
}