Subversion Repositories oidplus

Rev

Rev 1126 | Rev 1130 | 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
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
2 daniel-mar 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
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
511 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
730 daniel-mar 26
abstract class OIDplusObject extends OIDplusBaseClass {
261 daniel-mar 27
        const UUID_NAMEBASED_NS_OidPlusMisc = 'ad1654e6-7e15-11e4-9ef6-78e3b5fc7f22';
150 daniel-mar 28
 
1116 daniel-mar 29
        /**
30
         * Please overwrite this function!
31
         * @param string $node_id
32
         * @return OIDplusObject|null
33
         */
34
        public static function parse(string $node_id)/*: ?OIDplusObject*/ {
227 daniel-mar 35
                foreach (OIDplus::getEnabledObjectTypes() as $ot) {
954 daniel-mar 36
                        try {
956 daniel-mar 37
                                $good = false;
1050 daniel-mar 38
                                if (get_parent_class($ot) == OIDplusObject::class) {
956 daniel-mar 39
                                        $reflector = new \ReflectionMethod($ot, 'parse');
40
                                        $isImplemented = ($reflector->getDeclaringClass()->getName() === $ot);
41
                                        if ($isImplemented) { // avoid endless loop if parse is not overriden
42
                                                $good = true;
43
                                        }
44
                                }
45
                                // We need to do the workaround with "$good", otherwise PHPstan shows
46
                                // "Call to an undefined static method object::parse()"
47
                                if ($good && $obj = $ot::parse($node_id)) return $obj;
1050 daniel-mar 48
                        } catch (\Exception $e) {}
2 daniel-mar 49
                }
50
                return null;
51
        }
52
 
1116 daniel-mar 53
        /**
54
         * @return OIDplusAltId[]
55
         * @throws OIDplusException
56
         */
57
        public function getAltIds(): array {
193 daniel-mar 58
                if ($this->isRoot()) return array();
59
 
60
                $ids = array();
1078 daniel-mar 61
 
62
                // Creates an OIDplus-Hash-OID
193 daniel-mar 63
                if ($this->ns() != 'oid') {
227 daniel-mar 64
                        $sid = OIDplus::getSystemId(true);
193 daniel-mar 65
                        if (!empty($sid)) {
817 daniel-mar 66
                                $ns_oid = $this->getPlugin()->getManifest()->getOid();
835 daniel-mar 67
                                if (str_starts_with($ns_oid, '1.3.6.1.4.1.37476.2.5.2.')) {
817 daniel-mar 68
                                        // Official ViaThinkSoft object type plugins
69
                                        // For backwards compatibility with existing IDs,
70
                                        // set the hash_payload as '<namespace>:<id>'
71
                                        $hash_payload = $this->nodeId(true);
72
                                } else {
73
                                        // Third-party object type plugins
74
                                        // Set the hash_payload as '<plugin oid>:<id>'
75
                                        $hash_payload = $ns_oid.':'.$this->nodeId(false);
76
                                }
77
                                $oid = $sid . '.' . smallhash($hash_payload);
1078 daniel-mar 78
                                $ids[] = new OIDplusAltId('oid', $oid, _L('OIDplus Information Object OID'));
193 daniel-mar 79
                        }
1078 daniel-mar 80
                }
929 daniel-mar 81
 
1078 daniel-mar 82
                // Make a namebased UUID, but...
83
                // ... exclude GUID, because a GUID is already a GUID
84
                // ... exclude OID, because an OID already has a record UUID_NAMEBASED_NS_OID (defined by IETF) set by class OIDplusOid
85
                if (($this->ns() != 'guid') && ($this->ns() != 'oid')) {
86
                        $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'));
87
                        $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'));
88
                }
89
 
90
                // Make a AID based on ViaThinkSoft schema
91
                // ... 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)
92
                if (($this->ns() != 'aid') && !str_starts_with($this->nodeId(true), 'oid:1.3.6.1.4.1.37476.30.9.')) {
93
                        $sid = OIDplus::getSystemId(false);
1116 daniel-mar 94
                        if ($sid !== false) {
1078 daniel-mar 95
                                $ns_oid = $this->getPlugin()->getManifest()->getOid();
96
                                if (str_starts_with($ns_oid, '1.3.6.1.4.1.37476.2.5.2.')) {
97
                                        // Official ViaThinkSoft object type plugins
98
                                        // For backwards compatibility with existing IDs,
99
                                        // set the hash_payload as '<namespace>:<id>'
100
                                        $hash_payload = $this->nodeId(true);
101
                                } else {
102
                                        // Third-party object type plugins
103
                                        // Set the hash_payload as '<plugin oid>:<id>'
104
                                        $hash_payload = $ns_oid.':'.$this->nodeId(false);
105
                                }
106
 
1116 daniel-mar 107
                                $sid_hex = strtoupper(str_pad(dechex((int)$sid),8,'0',STR_PAD_LEFT));
1078 daniel-mar 108
                                $obj_hex = strtoupper(str_pad(dechex(smallhash($hash_payload)),8,'0',STR_PAD_LEFT));
109
                                $aid = 'D276000186B20005'.$sid_hex.$obj_hex;
110
                                $ids[] = new OIDplusAltId('aid', $aid, _L('OIDplus Information Object Application Identifier (ISO/IEC 7816)'), ' ('._L('No PIX allowed').')');
929 daniel-mar 111
                        }
193 daniel-mar 112
                }
1078 daniel-mar 113
 
193 daniel-mar 114
                return $ids;
83 daniel-mar 115
        }
116
 
1116 daniel-mar 117
        /**
118
         * @return string
119
         */
120
        public abstract static function objectTypeTitle(): string;
2 daniel-mar 121
 
1116 daniel-mar 122
        /**
123
         * @return string
124
         */
125
        public abstract static function objectTypeTitleShort(): string;
2 daniel-mar 126
 
1116 daniel-mar 127
        /**
128
         * @return OIDplusObjectTypePlugin|null
129
         */
817 daniel-mar 130
        public function getPlugin()/*: ?OIDplusObjectTypePlugin */ {
131
                $plugins = OIDplus::getObjectTypePlugins();
132
                foreach ($plugins as $plugin) {
1116 daniel-mar 133
                        if (get_class($this) == $plugin::getObjectTypeClassName()) {
817 daniel-mar 134
                                return $plugin;
135
                        }
136
                }
1116 daniel-mar 137
                return null;
817 daniel-mar 138
        }
139
 
1116 daniel-mar 140
        /**
141
         * @return string
142
         */
143
        public abstract static function ns(): string;
2 daniel-mar 144
 
1116 daniel-mar 145
        /**
146
         * @return string
147
         */
148
        public abstract static function root(): string;
2 daniel-mar 149
 
1116 daniel-mar 150
        /**
151
         * @return bool
152
         */
153
        public abstract function isRoot(): bool;
2 daniel-mar 154
 
1116 daniel-mar 155
        /**
156
         * @param bool $with_ns
157
         * @return string
158
         */
159
        public abstract function nodeId(bool $with_ns=true): string;
2 daniel-mar 160
 
1116 daniel-mar 161
        /**
162
         * @param string $str
163
         * @return string mixed
164
         * @throws OIDplusException
165
         */
166
        public abstract function addString(string $str): string;
2 daniel-mar 167
 
1116 daniel-mar 168
        /**
169
         * @param OIDplusObject $parent
170
         * @return string
171
         */
172
        public abstract function crudShowId(OIDplusObject $parent): string;
2 daniel-mar 173
 
1116 daniel-mar 174
        /**
175
         * @return string
176
         */
177
        public function crudInsertPrefix(): string {
707 daniel-mar 178
                return '';
179
        }
2 daniel-mar 180
 
1116 daniel-mar 181
        /**
182
         * @return string
183
         */
184
        public function crudInsertSuffix(): string {
707 daniel-mar 185
                return '';
186
        }
187
 
1116 daniel-mar 188
        /**
189
         * @param OIDplusObject|null $parent
190
         * @return string
191
         */
192
        public abstract function jsTreeNodeName(OIDplusObject $parent = null): string;
2 daniel-mar 193
 
1116 daniel-mar 194
        /**
195
         * @return string
196
         */
197
        public abstract function defaultTitle(): string;
2 daniel-mar 198
 
1116 daniel-mar 199
        /**
200
         * @return bool
201
         */
202
        public abstract function isLeafNode(): bool;
16 daniel-mar 203
 
1116 daniel-mar 204
        /**
205
         * @param string $title
206
         * @param string $content
207
         * @param string $icon
208
         * @return void
209
         */
210
        public abstract function getContentPage(string &$title, string &$content, string &$icon);
2 daniel-mar 211
 
1116 daniel-mar 212
        /**
213
         * @param OIDplusRA|string|null $ra
214
         * @return array
215
         * @throws OIDplusConfigInitializationException
216
         * @throws OIDplusException
217
         */
218
        public static function getRaRoots($ra=null) : array{
219
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 220
 
27 daniel-mar 221
                $out = array();
150 daniel-mar 222
 
261 daniel-mar 223
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
1116 daniel-mar 224
                        if (!$ra) {
261 daniel-mar 225
                                $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 ".
226
                                                            "left join ###objects as oParent on oChild.parent = oParent.id ".
150 daniel-mar 227
                                                            "order by ".OIDplus::db()->natOrder('oChild.id'));
236 daniel-mar 228
                                while ($row = $res->fetch_array()) {
549 daniel-mar 229
                                        if (!OIDplus::authUtils()->isRaLoggedIn($row['parent_mail']) && OIDplus::authUtils()->isRaLoggedIn($row['child_mail'])) {
1121 daniel-mar 230
                                                $x = self::parse($row['id']); // can be NULL if namespace was disabled
150 daniel-mar 231
                                                if ($x) $out[] = $x;
232
                                        }
233
                                }
234
                        } else {
261 daniel-mar 235
                                $res = OIDplus::db()->query("select oChild.id as id from ###objects as oChild ".
236
                                                            "left join ###objects as oParent on oChild.parent = oParent.id ".
433 daniel-mar 237
                                                            "where (".OIDplus::db()->getSlang()->isNullFunction('oParent.ra_email',"''")." <> ? and ".
238
                                                            OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) or ".
239
                                                            "      (oParent.ra_email is null and ".OIDplus::db()->getSlang()->isNullFunction('oChild.ra_email',"''")." = ?) ".
1116 daniel-mar 240
                                                            "order by ".OIDplus::db()->natOrder('oChild.id'), array($ra, $ra, $ra));
236 daniel-mar 241
                                while ($row = $res->fetch_array()) {
1121 daniel-mar 242
                                        $x = self::parse($row['id']); // can be NULL if namespace was disabled
1028 daniel-mar 243
                                        if ($x) $out[] = $x;
27 daniel-mar 244
                                }
2 daniel-mar 245
                        }
246
                } else {
1116 daniel-mar 247
                        if (!$ra) {
415 daniel-mar 248
                                $ra_mails_to_check = OIDplus::authUtils()->loggedInRaList();
150 daniel-mar 249
                                if (count($ra_mails_to_check) == 0) return $out;
250
                        } else {
1116 daniel-mar 251
                                $ra_mails_to_check = array($ra);
2 daniel-mar 252
                        }
150 daniel-mar 253
 
254
                        self::buildObjectInformationCache();
255
 
256
                        foreach ($ra_mails_to_check as $check_ra_mail) {
193 daniel-mar 257
                                $out_part = array();
150 daniel-mar 258
 
975 daniel-mar 259
                                foreach (self::$object_info_cache as $id => $cacheitem) {
1028 daniel-mar 260
                                        if ($cacheitem[self::CACHE_RA_EMAIL] == $check_ra_mail) {
261
                                                $parent = $cacheitem[self::CACHE_PARENT];
262
                                                if (!isset(self::$object_info_cache[$parent]) || (self::$object_info_cache[$parent][self::CACHE_RA_EMAIL] != $check_ra_mail)) {
263
                                                        $out_part[] = $id;
150 daniel-mar 264
                                                }
265
                                        }
266
                                }
267
 
268
                                natsort($out_part);
269
 
270
                                foreach ($out_part as $id) {
271
                                        $obj = self::parse($id);
272
                                        if ($obj) $out[] = $obj;
273
                                }
274
                        }
2 daniel-mar 275
                }
150 daniel-mar 276
 
2 daniel-mar 277
                return $out;
278
        }
279
 
1116 daniel-mar 280
        /**
281
         * @return array
282
         * @throws OIDplusException
283
         */
284
        public static function getAllNonConfidential(): array {
150 daniel-mar 285
                $out = array();
286
 
261 daniel-mar 287
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
443 daniel-mar 288
                        $res = OIDplus::db()->query("select id from ###objects where confidential = ? order by ".OIDplus::db()->natOrder('id'), array(false));
150 daniel-mar 289
 
236 daniel-mar 290
                        while ($row = $res->fetch_array()) {
150 daniel-mar 291
                                $obj = self::parse($row['id']); // will be NULL if the object type is not registered
169 daniel-mar 292
                                if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 293
                                        $out[] = $row['id'];
294
                                }
2 daniel-mar 295
                        }
296
                } else {
150 daniel-mar 297
                        self::buildObjectInformationCache();
2 daniel-mar 298
 
975 daniel-mar 299
                        foreach (self::$object_info_cache as $id => $cacheitem) {
300
                                $confidential = $cacheitem[self::CACHE_CONFIDENTIAL];
150 daniel-mar 301
                                if (!$confidential) {
302
                                        $obj = self::parse($id); // will be NULL if the object type is not registered
169 daniel-mar 303
                                        if ($obj && (!$obj->isConfidential())) {
150 daniel-mar 304
                                                $out[] = $id;
305
                                        }
306
                                }
2 daniel-mar 307
                        }
308
                }
309
 
310
                return $out;
311
        }
312
 
1116 daniel-mar 313
        /**
314
         * @return bool
315
         * @throws OIDplusException
316
         */
317
        public function isConfidential(): bool {
261 daniel-mar 318
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
426 daniel-mar 319
                        //static $confidential_cache = array();
150 daniel-mar 320
                        $curid = $this->nodeId();
426 daniel-mar 321
                        //$orig_curid = $curid;
322
                        //if (isset($confidential_cache[$curid])) return $confidential_cache[$curid];
150 daniel-mar 323
                        // Recursively search for the confidential flag in the parents
790 daniel-mar 324
                        while (($res = OIDplus::db()->query("select parent, confidential from ###objects where id = ?", array($curid)))->any()) {
236 daniel-mar 325
                                $row = $res->fetch_array();
150 daniel-mar 326
                                if ($row['confidential']) {
426 daniel-mar 327
                                        //$confidential_cache[$curid] = true;
328
                                        //$confidential_cache[$orig_curid] = true;
150 daniel-mar 329
                                        return true;
330
                                } else {
426 daniel-mar 331
                                        //$confidential_cache[$curid] = false;
150 daniel-mar 332
                                }
333
                                $curid = $row['parent'];
426 daniel-mar 334
                                //if (isset($confidential_cache[$curid])) {
335
                                        //$confidential_cache[$orig_curid] = $confidential_cache[$curid];
336
                                        //return $confidential_cache[$curid];
337
                                //}
150 daniel-mar 338
                        }
339
 
426 daniel-mar 340
                        //$confidential_cache[$orig_curid] = false;
150 daniel-mar 341
                        return false;
342
                } else {
343
                        self::buildObjectInformationCache();
344
 
345
                        $curid = $this->nodeId();
346
                        // Recursively search for the confidential flag in the parents
169 daniel-mar 347
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 348
                                if (self::$object_info_cache[$curid][self::CACHE_CONFIDENTIAL]) return true;
349
                                $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
350
                        }
351
                        return false;
2 daniel-mar 352
                }
353
        }
354
 
1116 daniel-mar 355
        /**
356
         * @param OIDplusObject $obj
357
         * @return bool
358
         * @throws OIDplusException
359
         */
360
        public function isChildOf(OIDplusObject $obj): bool {
261 daniel-mar 361
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
150 daniel-mar 362
                        $curid = $this->nodeId();
790 daniel-mar 363
                        while (($res = OIDplus::db()->query("select parent from ###objects where id = ?", array($curid)))->any()) {
236 daniel-mar 364
                                $row = $res->fetch_array();
150 daniel-mar 365
                                if ($curid == $obj->nodeId()) return true;
366
                                $curid = $row['parent'];
367
                        }
368
                        return false;
369
                } else {
370
                        self::buildObjectInformationCache();
371
 
372
                        $curid = $this->nodeId();
169 daniel-mar 373
                        while (isset(self::$object_info_cache[$curid])) {
150 daniel-mar 374
                                if ($curid == $obj->nodeId()) return true;
375
                                $curid = self::$object_info_cache[$curid][self::CACHE_PARENT];
376
                        }
377
                        return false;
2 daniel-mar 378
                }
150 daniel-mar 379
        }
2 daniel-mar 380
 
1116 daniel-mar 381
        /**
382
         * @return array
383
         * @throws OIDplusException
384
         */
385
        public function getChildren(): array {
150 daniel-mar 386
                $out = array();
261 daniel-mar 387
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
388
                        $res = OIDplus::db()->query("select id from ###objects where parent = ?", array($this->nodeId()));
236 daniel-mar 389
                        while ($row = $res->fetch_array()) {
150 daniel-mar 390
                                $obj = self::parse($row['id']);
391
                                if (!$obj) continue;
392
                                $out[] = $obj;
393
                        }
394
                } else {
395
                        self::buildObjectInformationCache();
396
 
975 daniel-mar 397
                        foreach (self::$object_info_cache as $id => $cacheitem) {
398
                                $parent = $cacheitem[self::CACHE_PARENT];
150 daniel-mar 399
                                if ($parent == $this->nodeId()) {
400
                                        $obj = self::parse($id);
401
                                        if (!$obj) continue;
402
                                        $out[] = $obj;
403
                                }
404
                        }
405
                }
406
                return $out;
2 daniel-mar 407
        }
408
 
1116 daniel-mar 409
        /**
410
         * @return OIDplusRA
411
         * @throws OIDplusException
412
         */
413
        public function getRa(): OIDplusRA {
150 daniel-mar 414
                return new OIDplusRA($this->getRaMail());
115 daniel-mar 415
        }
416
 
1116 daniel-mar 417
        /**
418
         * @param OIDplusRA|string|null $ra
419
         * @return bool
420
         * @throws OIDplusConfigInitializationException
421
         * @throws OIDplusException
422
         */
423
        public function userHasReadRights($ra=null): bool {
424
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 425
 
2 daniel-mar 426
                // If it is not confidential, everybody can read/see it.
416 daniel-mar 427
                // Note: This also checks if superior OIDs are confidential.
2 daniel-mar 428
                if (!$this->isConfidential()) return true;
429
 
1116 daniel-mar 430
                if (!$ra) {
416 daniel-mar 431
                        // Admin may do everything
549 daniel-mar 432
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
416 daniel-mar 433
 
434
                        // If the RA is logged in, then they can see the OID.
549 daniel-mar 435
                        if (OIDplus::authUtils()->isRaLoggedIn($this->getRaMail())) return true;
2 daniel-mar 436
                } else {
416 daniel-mar 437
                        // If this OID belongs to the requested RA, then they may see it.
1116 daniel-mar 438
                        if ($this->getRaMail() == $ra) return true;
2 daniel-mar 439
                }
440
 
441
                // If someone has rights to an object below our confidential node,
442
                // we let him see the confidential node,
443
                // Otherwise he could not browse through to his own node.
1116 daniel-mar 444
                $roots = $this->getRaRoots($ra);
2 daniel-mar 445
                foreach ($roots as $root) {
446
                        if ($root->isChildOf($this)) return true;
447
                }
448
 
449
                return false;
450
        }
451
 
1116 daniel-mar 452
        /**
453
         * @param array|null $row
454
         * @return string|null
455
         * @throws OIDplusException
456
         */
457
        public function getIcon(array $row=null) {
20 daniel-mar 458
                $namespace = $this->ns(); // must use $this, not self::, otherwise the virtual method will not be called
2 daniel-mar 459
 
460
                if (is_null($row)) {
150 daniel-mar 461
                        $ra_email = $this->getRaMail();
462
                } else {
463
                        $ra_email = $row['ra_email'];
2 daniel-mar 464
                }
632 daniel-mar 465
 
1127 daniel-mar 466
                // $dirs = glob(OIDplus::localpath().'plugins/'.'*'.'/objectTypes/'.$namespace.'/');
467
                // if (count($dirs) == 0) return null; // default icon (folder)
468
                // $dir = substr($dirs[0], strlen(OIDplus::localpath()));
469
                $reflection = new \ReflectionClass($this);
470
                $dir = dirname($reflection->getFilename());
471
                $dir = substr($dir, strlen(OIDplus::localpath()));
632 daniel-mar 472
 
1124 daniel-mar 473
                if ($this->isRoot()) {
1127 daniel-mar 474
                        $icon = $dir . '/' . $this::treeIconFilename('root');
2 daniel-mar 475
                } else {
1124 daniel-mar 476
                        // We use $this:: instead of self:: , because we want to call the overridden methods
1126 daniel-mar 477
                        if ($ra_email && OIDplus::authUtils()->isRaLoggedIn($ra_email)) {
1124 daniel-mar 478
                                if ($this->isLeafNode()) {
479
                                        $icon = $dir . '/' . $this::treeIconFilename('own_leaf');
480
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('own');
481
                                } else {
482
                                        $icon = $dir . '/' . $this::treeIconFilename('own');
483
                                }
484
                        } else {
485
                                if ($this->isLeafNode()) {
486
                                        $icon = $dir . '/' . $this::treeIconFilename('general_leaf');
487
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('general');
488
                                } else {
489
                                        $icon = $dir . '/' . $this::treeIconFilename('general');
490
                                }
491
                        }
2 daniel-mar 492
                }
632 daniel-mar 493
 
494
                if (!file_exists($icon)) return null; // default icon (folder)
495
 
2 daniel-mar 496
                return $icon;
497
        }
498
 
1116 daniel-mar 499
        /**
500
         * @param string $id
501
         * @return bool
502
         * @throws OIDplusException
503
         */
504
        public static function exists(string $id): bool {
261 daniel-mar 505
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
506
                        $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
790 daniel-mar 507
                        return $res->any();
150 daniel-mar 508
                } else {
509
                        self::buildObjectInformationCache();
510
                        return isset(self::$object_info_cache[$id]);
511
                }
12 daniel-mar 512
        }
513
 
1116 daniel-mar 514
        /**
515
         * Get parent gives the next possible parent which is EXISTING in OIDplus
516
         * It does not give the immediate parent
517
         * @return OIDplusObject|null
518
         * @throws OIDplusException
519
         */
979 daniel-mar 520
        public function getParent()/*: ?OIDplusObject*/ {
261 daniel-mar 521
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
522
                        $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 523
                        if (!$res->any()) return null;
236 daniel-mar 524
                        $row = $res->fetch_array();
150 daniel-mar 525
                        $parent = $row['parent'];
526
                        $obj = OIDplusObject::parse($parent);
527
                        if ($obj) return $obj;
415 daniel-mar 528
                        // TODO: Also implement one_up() like below
150 daniel-mar 529
                } else {
530
                        self::buildObjectInformationCache();
531
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
532
                                $parent = self::$object_info_cache[$this->nodeId()][self::CACHE_PARENT];
533
                                $obj = OIDplusObject::parse($parent);
534
                                if ($obj) return $obj;
535
                        }
20 daniel-mar 536
 
150 daniel-mar 537
                        // 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()
538
                        $cur = $this->one_up();
418 daniel-mar 539
                        if (!$cur) return null;
150 daniel-mar 540
                        do {
415 daniel-mar 541
                                // findFitting() checks if that OID exists
150 daniel-mar 542
                                if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
20 daniel-mar 543
 
150 daniel-mar 544
                                $prev = $cur;
545
                                $cur = $cur->one_up();
418 daniel-mar 546
                                if (!$cur) return null;
1116 daniel-mar 547
                        } while ($prev !== $cur);
150 daniel-mar 548
                }
979 daniel-mar 549
                return null;
2 daniel-mar 550
        }
551
 
1116 daniel-mar 552
        /**
553
         * @return false|string|null
554
         * @throws OIDplusException
555
         */
2 daniel-mar 556
        public function getRaMail() {
261 daniel-mar 557
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
558
                        $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 559
                        if (!$res->any()) return null;
236 daniel-mar 560
                        $row = $res->fetch_array();
150 daniel-mar 561
                        return $row['ra_email'];
562
                } else {
563
                        self::buildObjectInformationCache();
564
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
565
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_RA_EMAIL];
566
                        }
567
                        return false;
568
                }
2 daniel-mar 569
        }
570
 
1116 daniel-mar 571
        /**
572
         * @return false|string|null
573
         * @throws OIDplusException
574
         */
192 daniel-mar 575
        public function getTitle() {
261 daniel-mar 576
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
577
                        $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 578
                        if (!$res->any()) return null;
236 daniel-mar 579
                        $row = $res->fetch_array();
192 daniel-mar 580
                        return $row['title'];
581
                } else {
582
                        self::buildObjectInformationCache();
583
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
584
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_TITLE];
585
                        }
586
                        return false;
587
                }
588
        }
589
 
1116 daniel-mar 590
        /**
591
         * @return false|string|null
592
         * @throws OIDplusException
593
         */
975 daniel-mar 594
        public function getDescription() {
595
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
596
                        $res = OIDplus::db()->query("select description from ###objects where id = ?", array($this->nodeId()));
597
                        if (!$res->any()) return null;
598
                        $row = $res->fetch_array();
599
                        return $row['description'];
600
                } else {
601
                        self::buildObjectInformationCache();
602
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
603
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_DESCRIPTION];
604
                        }
605
                        return false;
606
                }
607
        }
608
 
1116 daniel-mar 609
        /**
610
         * @return false|string|null
611
         * @throws OIDplusException
612
         */
975 daniel-mar 613
        public function getComment() {
614
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
615
                        $res = OIDplus::db()->query("select comment from ###objects where id = ?", array($this->nodeId()));
616
                        if (!$res->any()) return null;
617
                        $row = $res->fetch_array();
618
                        return $row['comment'];
619
                } else {
620
                        self::buildObjectInformationCache();
621
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
622
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_COMMENT];
623
                        }
624
                        return false;
625
                }
626
        }
627
 
1116 daniel-mar 628
        /**
629
         * @return false|string|null
630
         * @throws OIDplusException
631
         */
975 daniel-mar 632
        public function getCreatedTime() {
633
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
634
                        $res = OIDplus::db()->query("select created from ###objects where id = ?", array($this->nodeId()));
635
                        if (!$res->any()) return null;
636
                        $row = $res->fetch_array();
637
                        return $row['created'];
638
                } else {
639
                        self::buildObjectInformationCache();
640
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
641
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_CREATED];
642
                        }
643
                        return false;
644
                }
645
        }
646
 
1116 daniel-mar 647
        /**
648
         * @return false|string|null
649
         * @throws OIDplusException
650
         */
975 daniel-mar 651
        public function getUpdatedTime() {
652
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
653
                        $res = OIDplus::db()->query("select updated from ###objects where id = ?", array($this->nodeId()));
654
                        if (!$res->any()) return null;
655
                        $row = $res->fetch_array();
656
                        return $row['updated'];
657
                } else {
658
                        self::buildObjectInformationCache();
659
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
660
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_UPDATED];
661
                        }
662
                        return false;
663
                }
664
        }
665
 
1116 daniel-mar 666
        /**
667
         * @param OIDplusRA|string|null $ra
668
         * @return bool
669
         * @throws OIDplusException
670
         */
671
        public function userHasParentalWriteRights($ra=null) {
672
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 673
 
1116 daniel-mar 674
                if (!$ra) {
549 daniel-mar 675
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
2 daniel-mar 676
                }
677
 
678
                $objParent = $this->getParent();
419 daniel-mar 679
                if (!$objParent) return false;
1116 daniel-mar 680
                return $objParent->userHasWriteRights($ra);
2 daniel-mar 681
        }
682
 
1116 daniel-mar 683
        /**
684
         * @param OIDplusRA|string|null $ra
685
         * @return bool
686
         * @throws OIDplusException
687
         */
688
        public function userHasWriteRights($ra=null): bool {
689
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 690
 
1116 daniel-mar 691
                if (!$ra) {
549 daniel-mar 692
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
693
                        return OIDplus::authUtils()->isRaLoggedIn($this->getRaMail());
2 daniel-mar 694
                } else {
1116 daniel-mar 695
                        return $this->getRaMail() == $ra;
2 daniel-mar 696
                }
697
        }
12 daniel-mar 698
 
1116 daniel-mar 699
        /**
700
         * @param string|OIDplusObject $to
701
         * @return int|null
702
         */
703
        public function distance($to)/*: ?int*/ {
12 daniel-mar 704
                return null; // not implemented
705
        }
20 daniel-mar 706
 
1116 daniel-mar 707
        /**
708
         * @param OIDplusObject $obj
709
         * @return bool
710
         */
711
        public function equals(OIDplusObject $obj): bool {
1121 daniel-mar 712
                if (!$obj) return false;
20 daniel-mar 713
                if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
1121 daniel-mar 714
                if (!$obj) return false;
28 daniel-mar 715
                if (!($obj instanceof $this)) return false;
716
 
20 daniel-mar 717
                $distance = $this->distance($obj);
718
                if (is_numeric($distance)) return $distance === 0; // if the distance function is implemented, use it
719
 
720
                return $this->nodeId() == $obj->nodeId(); // otherwise compare the node id case-sensitive
721
        }
722
 
1116 daniel-mar 723
        /**
724
         * @param string $id
725
         * @return OIDplusObject|false
726
         * @throws OIDplusException
727
         */
977 daniel-mar 728
        public static function findFitting(string $id) {
20 daniel-mar 729
                $obj = OIDplusObject::parse($id);
969 daniel-mar 730
                if (!$obj) return false; // e.g. if ObjectType plugin is disabled
20 daniel-mar 731
 
261 daniel-mar 732
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
733
                        $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
236 daniel-mar 734
                        while ($row = $res->fetch_object()) {
150 daniel-mar 735
                                $test = OIDplusObject::parse($row->id);
736
                                if ($obj->equals($test)) return $test;
737
                        }
738
                        return false;
739
                } else {
740
                        self::buildObjectInformationCache();
975 daniel-mar 741
                        foreach (self::$object_info_cache as $id => $cacheitem) {
150 daniel-mar 742
                                if (strpos($id, $obj->ns().':') === 0) {
743
                                        $test = OIDplusObject::parse($id);
744
                                        if ($obj->equals($test)) return $test;
745
                                }
746
                        }
747
                        return false;
20 daniel-mar 748
                }
749
        }
750
 
1116 daniel-mar 751
        /**
752
         * @return OIDplusObject|null
753
         */
754
        public function one_up()/*: ?OIDplusObject*/ {
20 daniel-mar 755
                return null; // not implemented
756
        }
150 daniel-mar 757
 
758
        // Caching stuff
759
 
760
        protected static $object_info_cache = null;
761
 
1116 daniel-mar 762
        /**
763
         * @return void
764
         */
150 daniel-mar 765
        public static function resetObjectInformationCache() {
766
                self::$object_info_cache = null;
767
        }
768
 
975 daniel-mar 769
        const CACHE_ID = 'id';
770
        const CACHE_PARENT = 'parent';
771
        const CACHE_TITLE = 'title';
772
        const CACHE_DESCRIPTION = 'description';
773
        const CACHE_RA_EMAIL = 'ra_email';
774
        const CACHE_CONFIDENTIAL = 'confidential';
775
        const CACHE_CREATED = 'created';
776
        const CACHE_UPDATED = 'updated';
777
        const CACHE_COMMENT = 'comment';
150 daniel-mar 778
 
1116 daniel-mar 779
        /**
780
         * @return void
781
         * @throws OIDplusException
782
         */
150 daniel-mar 783
        private static function buildObjectInformationCache() {
784
                if (is_null(self::$object_info_cache)) {
785
                        self::$object_info_cache = array();
975 daniel-mar 786
                        $res = OIDplus::db()->query("select * from ###objects");
236 daniel-mar 787
                        while ($row = $res->fetch_array()) {
975 daniel-mar 788
                                self::$object_info_cache[$row['id']] = $row;
150 daniel-mar 789
                        }
790
                }
791
        }
513 daniel-mar 792
 
1116 daniel-mar 793
        /**
794
         * override this function if you want your object type to save
795
         * attachments in directories with easy names.
796
         * Take care that your custom directory name will not allow jailbreaks (../) !
797
         * @return string
798
         * @throws OIDplusException
799
         */
800
        public function getDirectoryName(): string {
514 daniel-mar 801
                if ($this->isRoot()) return $this->ns();
802
                return $this->getLegacyDirectoryName();
513 daniel-mar 803
        }
804
 
1116 daniel-mar 805
        /**
806
         * @return string
807
         * @throws OIDplusException
808
         */
809
        public final function getLegacyDirectoryName(): string {
804 daniel-mar 810
                if ($this::ns() == 'oid') {
513 daniel-mar 811
                        $oid = $this->nodeId(false);
812
                } else {
813
                        $oid = null;
814
                        $alt_ids = $this->getAltIds();
815
                        foreach ($alt_ids as $alt_id) {
816
                                if ($alt_id->getNamespace() == 'oid') {
817
                                        $oid = $alt_id->getId();
818
                                        break; // we prefer the first OID (for GUIDs, the first OID is the OIDplus-OID, and the second OID is the UUID OID)
819
                                }
820
                        }
821
                }
822
 
823
                if (!is_null($oid) && ($oid != '')) {
824
                        // For OIDs, it is the OID, for other identifiers
825
                        // it it the OID alt ID (generated using the SystemID)
826
                        return str_replace('.', '_', $oid);
827
                } else {
828
                        // Can happen if you don't have a system ID (due to missing OpenSSL plugin)
829
                        return md5($this->nodeId(true)); // we don't use $id, because $this->nodeId(true) is possibly more canonical than $id
830
                }
831
        }
800 daniel-mar 832
 
1116 daniel-mar 833
        /**
834
         * @param string $mode
835
         * @return string
836
         */
837
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 838
                // for backwards-compatibility with older plugins
839
                return 'img/treeicon_'.$mode.'.png';
840
        }
841
 
415 daniel-mar 842
}