Subversion Repositories oidplus

Rev

Rev 1124 | Rev 1127 | 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
 
635 daniel-mar 466
                $dirs = glob(OIDplus::localpath().'plugins/'.'*'.'/objectTypes/'.$namespace.'/');
632 daniel-mar 467
 
468
                if (count($dirs) == 0) return null; // default icon (folder)
469
 
470
                $dir = substr($dirs[0], strlen(OIDplus::localpath()));
471
 
1124 daniel-mar 472
                if ($this->isRoot()) {
473
                        $icon = $dir . '/' . $this::treeIconFilename('root'); // see also OIDplusPagePublicObjects::get_treeicon_root()
2 daniel-mar 474
                } else {
1124 daniel-mar 475
                        // We use $this:: instead of self:: , because we want to call the overridden methods
1126 daniel-mar 476
                        if ($ra_email && OIDplus::authUtils()->isRaLoggedIn($ra_email)) {
1124 daniel-mar 477
                                if ($this->isLeafNode()) {
478
                                        $icon = $dir . '/' . $this::treeIconFilename('own_leaf');
479
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('own');
480
                                } else {
481
                                        $icon = $dir . '/' . $this::treeIconFilename('own');
482
                                }
483
                        } else {
484
                                if ($this->isLeafNode()) {
485
                                        $icon = $dir . '/' . $this::treeIconFilename('general_leaf');
486
                                        if (!file_exists($icon)) $icon = $dir . '/' . $this::treeIconFilename('general');
487
                                } else {
488
                                        $icon = $dir . '/' . $this::treeIconFilename('general');
489
                                }
490
                        }
2 daniel-mar 491
                }
632 daniel-mar 492
 
493
                if (!file_exists($icon)) return null; // default icon (folder)
494
 
2 daniel-mar 495
                return $icon;
496
        }
497
 
1116 daniel-mar 498
        /**
499
         * @param string $id
500
         * @return bool
501
         * @throws OIDplusException
502
         */
503
        public static function exists(string $id): bool {
261 daniel-mar 504
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
505
                        $res = OIDplus::db()->query("select id from ###objects where id = ?", array($id));
790 daniel-mar 506
                        return $res->any();
150 daniel-mar 507
                } else {
508
                        self::buildObjectInformationCache();
509
                        return isset(self::$object_info_cache[$id]);
510
                }
12 daniel-mar 511
        }
512
 
1116 daniel-mar 513
        /**
514
         * Get parent gives the next possible parent which is EXISTING in OIDplus
515
         * It does not give the immediate parent
516
         * @return OIDplusObject|null
517
         * @throws OIDplusException
518
         */
979 daniel-mar 519
        public function getParent()/*: ?OIDplusObject*/ {
261 daniel-mar 520
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
521
                        $res = OIDplus::db()->query("select parent from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 522
                        if (!$res->any()) return null;
236 daniel-mar 523
                        $row = $res->fetch_array();
150 daniel-mar 524
                        $parent = $row['parent'];
525
                        $obj = OIDplusObject::parse($parent);
526
                        if ($obj) return $obj;
415 daniel-mar 527
                        // TODO: Also implement one_up() like below
150 daniel-mar 528
                } else {
529
                        self::buildObjectInformationCache();
530
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
531
                                $parent = self::$object_info_cache[$this->nodeId()][self::CACHE_PARENT];
532
                                $obj = OIDplusObject::parse($parent);
533
                                if ($obj) return $obj;
534
                        }
20 daniel-mar 535
 
150 daniel-mar 536
                        // 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()
537
                        $cur = $this->one_up();
418 daniel-mar 538
                        if (!$cur) return null;
150 daniel-mar 539
                        do {
415 daniel-mar 540
                                // findFitting() checks if that OID exists
150 daniel-mar 541
                                if ($fitting = self::findFitting($cur->nodeId())) return $fitting;
20 daniel-mar 542
 
150 daniel-mar 543
                                $prev = $cur;
544
                                $cur = $cur->one_up();
418 daniel-mar 545
                                if (!$cur) return null;
1116 daniel-mar 546
                        } while ($prev !== $cur);
150 daniel-mar 547
                }
979 daniel-mar 548
                return null;
2 daniel-mar 549
        }
550
 
1116 daniel-mar 551
        /**
552
         * @return false|string|null
553
         * @throws OIDplusException
554
         */
2 daniel-mar 555
        public function getRaMail() {
261 daniel-mar 556
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
557
                        $res = OIDplus::db()->query("select ra_email from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 558
                        if (!$res->any()) return null;
236 daniel-mar 559
                        $row = $res->fetch_array();
150 daniel-mar 560
                        return $row['ra_email'];
561
                } else {
562
                        self::buildObjectInformationCache();
563
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
564
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_RA_EMAIL];
565
                        }
566
                        return false;
567
                }
2 daniel-mar 568
        }
569
 
1116 daniel-mar 570
        /**
571
         * @return false|string|null
572
         * @throws OIDplusException
573
         */
192 daniel-mar 574
        public function getTitle() {
261 daniel-mar 575
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
576
                        $res = OIDplus::db()->query("select title from ###objects where id = ?", array($this->nodeId()));
790 daniel-mar 577
                        if (!$res->any()) return null;
236 daniel-mar 578
                        $row = $res->fetch_array();
192 daniel-mar 579
                        return $row['title'];
580
                } else {
581
                        self::buildObjectInformationCache();
582
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
583
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_TITLE];
584
                        }
585
                        return false;
586
                }
587
        }
588
 
1116 daniel-mar 589
        /**
590
         * @return false|string|null
591
         * @throws OIDplusException
592
         */
975 daniel-mar 593
        public function getDescription() {
594
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
595
                        $res = OIDplus::db()->query("select description from ###objects where id = ?", array($this->nodeId()));
596
                        if (!$res->any()) return null;
597
                        $row = $res->fetch_array();
598
                        return $row['description'];
599
                } else {
600
                        self::buildObjectInformationCache();
601
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
602
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_DESCRIPTION];
603
                        }
604
                        return false;
605
                }
606
        }
607
 
1116 daniel-mar 608
        /**
609
         * @return false|string|null
610
         * @throws OIDplusException
611
         */
975 daniel-mar 612
        public function getComment() {
613
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
614
                        $res = OIDplus::db()->query("select comment from ###objects where id = ?", array($this->nodeId()));
615
                        if (!$res->any()) return null;
616
                        $row = $res->fetch_array();
617
                        return $row['comment'];
618
                } else {
619
                        self::buildObjectInformationCache();
620
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
621
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_COMMENT];
622
                        }
623
                        return false;
624
                }
625
        }
626
 
1116 daniel-mar 627
        /**
628
         * @return false|string|null
629
         * @throws OIDplusException
630
         */
975 daniel-mar 631
        public function getCreatedTime() {
632
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
633
                        $res = OIDplus::db()->query("select created from ###objects where id = ?", array($this->nodeId()));
634
                        if (!$res->any()) return null;
635
                        $row = $res->fetch_array();
636
                        return $row['created'];
637
                } else {
638
                        self::buildObjectInformationCache();
639
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
640
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_CREATED];
641
                        }
642
                        return false;
643
                }
644
        }
645
 
1116 daniel-mar 646
        /**
647
         * @return false|string|null
648
         * @throws OIDplusException
649
         */
975 daniel-mar 650
        public function getUpdatedTime() {
651
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
652
                        $res = OIDplus::db()->query("select updated from ###objects where id = ?", array($this->nodeId()));
653
                        if (!$res->any()) return null;
654
                        $row = $res->fetch_array();
655
                        return $row['updated'];
656
                } else {
657
                        self::buildObjectInformationCache();
658
                        if (isset(self::$object_info_cache[$this->nodeId()])) {
659
                                return self::$object_info_cache[$this->nodeId()][self::CACHE_UPDATED];
660
                        }
661
                        return false;
662
                }
663
        }
664
 
1116 daniel-mar 665
        /**
666
         * @param OIDplusRA|string|null $ra
667
         * @return bool
668
         * @throws OIDplusException
669
         */
670
        public function userHasParentalWriteRights($ra=null) {
671
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 672
 
1116 daniel-mar 673
                if (!$ra) {
549 daniel-mar 674
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
2 daniel-mar 675
                }
676
 
677
                $objParent = $this->getParent();
419 daniel-mar 678
                if (!$objParent) return false;
1116 daniel-mar 679
                return $objParent->userHasWriteRights($ra);
2 daniel-mar 680
        }
681
 
1116 daniel-mar 682
        /**
683
         * @param OIDplusRA|string|null $ra
684
         * @return bool
685
         * @throws OIDplusException
686
         */
687
        public function userHasWriteRights($ra=null): bool {
688
                if ($ra instanceof OIDplusRA) $ra = $ra->raEmail();
115 daniel-mar 689
 
1116 daniel-mar 690
                if (!$ra) {
549 daniel-mar 691
                        if (OIDplus::authUtils()->isAdminLoggedIn()) return true;
692
                        return OIDplus::authUtils()->isRaLoggedIn($this->getRaMail());
2 daniel-mar 693
                } else {
1116 daniel-mar 694
                        return $this->getRaMail() == $ra;
2 daniel-mar 695
                }
696
        }
12 daniel-mar 697
 
1116 daniel-mar 698
        /**
699
         * @param string|OIDplusObject $to
700
         * @return int|null
701
         */
702
        public function distance($to)/*: ?int*/ {
12 daniel-mar 703
                return null; // not implemented
704
        }
20 daniel-mar 705
 
1116 daniel-mar 706
        /**
707
         * @param OIDplusObject $obj
708
         * @return bool
709
         */
710
        public function equals(OIDplusObject $obj): bool {
1121 daniel-mar 711
                if (!$obj) return false;
20 daniel-mar 712
                if (!is_object($obj)) $obj = OIDplusObject::parse($obj);
1121 daniel-mar 713
                if (!$obj) return false;
28 daniel-mar 714
                if (!($obj instanceof $this)) return false;
715
 
20 daniel-mar 716
                $distance = $this->distance($obj);
717
                if (is_numeric($distance)) return $distance === 0; // if the distance function is implemented, use it
718
 
719
                return $this->nodeId() == $obj->nodeId(); // otherwise compare the node id case-sensitive
720
        }
721
 
1116 daniel-mar 722
        /**
723
         * @param string $id
724
         * @return OIDplusObject|false
725
         * @throws OIDplusException
726
         */
977 daniel-mar 727
        public static function findFitting(string $id) {
20 daniel-mar 728
                $obj = OIDplusObject::parse($id);
969 daniel-mar 729
                if (!$obj) return false; // e.g. if ObjectType plugin is disabled
20 daniel-mar 730
 
261 daniel-mar 731
                if (!OIDplus::baseConfig()->getValue('OBJECT_CACHING', true)) {
732
                        $res = OIDplus::db()->query("select id from ###objects where id like ?", array($obj->ns().':%'));
236 daniel-mar 733
                        while ($row = $res->fetch_object()) {
150 daniel-mar 734
                                $test = OIDplusObject::parse($row->id);
735
                                if ($obj->equals($test)) return $test;
736
                        }
737
                        return false;
738
                } else {
739
                        self::buildObjectInformationCache();
975 daniel-mar 740
                        foreach (self::$object_info_cache as $id => $cacheitem) {
150 daniel-mar 741
                                if (strpos($id, $obj->ns().':') === 0) {
742
                                        $test = OIDplusObject::parse($id);
743
                                        if ($obj->equals($test)) return $test;
744
                                }
745
                        }
746
                        return false;
20 daniel-mar 747
                }
748
        }
749
 
1116 daniel-mar 750
        /**
751
         * @return OIDplusObject|null
752
         */
753
        public function one_up()/*: ?OIDplusObject*/ {
20 daniel-mar 754
                return null; // not implemented
755
        }
150 daniel-mar 756
 
757
        // Caching stuff
758
 
759
        protected static $object_info_cache = null;
760
 
1116 daniel-mar 761
        /**
762
         * @return void
763
         */
150 daniel-mar 764
        public static function resetObjectInformationCache() {
765
                self::$object_info_cache = null;
766
        }
767
 
975 daniel-mar 768
        const CACHE_ID = 'id';
769
        const CACHE_PARENT = 'parent';
770
        const CACHE_TITLE = 'title';
771
        const CACHE_DESCRIPTION = 'description';
772
        const CACHE_RA_EMAIL = 'ra_email';
773
        const CACHE_CONFIDENTIAL = 'confidential';
774
        const CACHE_CREATED = 'created';
775
        const CACHE_UPDATED = 'updated';
776
        const CACHE_COMMENT = 'comment';
150 daniel-mar 777
 
1116 daniel-mar 778
        /**
779
         * @return void
780
         * @throws OIDplusException
781
         */
150 daniel-mar 782
        private static function buildObjectInformationCache() {
783
                if (is_null(self::$object_info_cache)) {
784
                        self::$object_info_cache = array();
975 daniel-mar 785
                        $res = OIDplus::db()->query("select * from ###objects");
236 daniel-mar 786
                        while ($row = $res->fetch_array()) {
975 daniel-mar 787
                                self::$object_info_cache[$row['id']] = $row;
150 daniel-mar 788
                        }
789
                }
790
        }
513 daniel-mar 791
 
1116 daniel-mar 792
        /**
793
         * override this function if you want your object type to save
794
         * attachments in directories with easy names.
795
         * Take care that your custom directory name will not allow jailbreaks (../) !
796
         * @return string
797
         * @throws OIDplusException
798
         */
799
        public function getDirectoryName(): string {
514 daniel-mar 800
                if ($this->isRoot()) return $this->ns();
801
                return $this->getLegacyDirectoryName();
513 daniel-mar 802
        }
803
 
1116 daniel-mar 804
        /**
805
         * @return string
806
         * @throws OIDplusException
807
         */
808
        public final function getLegacyDirectoryName(): string {
804 daniel-mar 809
                if ($this::ns() == 'oid') {
513 daniel-mar 810
                        $oid = $this->nodeId(false);
811
                } else {
812
                        $oid = null;
813
                        $alt_ids = $this->getAltIds();
814
                        foreach ($alt_ids as $alt_id) {
815
                                if ($alt_id->getNamespace() == 'oid') {
816
                                        $oid = $alt_id->getId();
817
                                        break; // we prefer the first OID (for GUIDs, the first OID is the OIDplus-OID, and the second OID is the UUID OID)
818
                                }
819
                        }
820
                }
821
 
822
                if (!is_null($oid) && ($oid != '')) {
823
                        // For OIDs, it is the OID, for other identifiers
824
                        // it it the OID alt ID (generated using the SystemID)
825
                        return str_replace('.', '_', $oid);
826
                } else {
827
                        // Can happen if you don't have a system ID (due to missing OpenSSL plugin)
828
                        return md5($this->nodeId(true)); // we don't use $id, because $this->nodeId(true) is possibly more canonical than $id
829
                }
830
        }
800 daniel-mar 831
 
1116 daniel-mar 832
        /**
833
         * @param string $mode
834
         * @return string
835
         */
836
        public static function treeIconFilename(string $mode): string {
800 daniel-mar 837
                // for backwards-compatibility with older plugins
838
                return 'img/treeicon_'.$mode.'.png';
839
        }
840
 
415 daniel-mar 841
}