/
dbobject.php
1414 lines (1265 loc) · 55.7 KB
/
dbobject.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?php
/**
* @package midcom.baseclasses
* @author The Midgard Project, http://www.midgard-project.org
* @copyright The Midgard Project, http://www.midgard-project.org
* @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
*/
use midcom\events\dbaevent;
/**
* This class only contains static functions which are there to hook into
* the classes you derive from the midcom_core_dbaobject.
*
* The static members will invoke a number of callback methods so that you should
* normally never have to override the base midgard methods like update or the like.
*
* @package midcom.baseclasses
*/
class midcom_baseclasses_core_dbobject
{
private static $parameter_cache = [];
private static $parameter_all = [];
/**
* "Pre-flight" checks for update method
*
* Separated so that dbfactory->import() can reuse the code
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
public static function update_pre_checks(midcom_core_dbaobject $object)
{
if (!$object->can_do('midgard:update')) {
debug_add("Failed to load object, update privilege on the " . get_class($object) . " {$object->id} not granted for the current user.",
MIDCOM_LOG_ERROR);
midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
return false;
}
if (!$object->_on_updating()) {
debug_add("The _on_updating event handler returned false.");
return false;
}
// Still check name uniqueness
return self::_pre_check_name($object);
}
/**
* Execute a DB update of the object passed. This will call the corresponding
* event handlers. Calling sequence with method signatures:
*
* 1. Validate privileges using can_do. The user needs midgard:update privilege on the content object.
* 2. bool $object->_on_updating() is executed. If it returns false, update is aborted.
* 3. bool $object->__object->update() is executed to do the actual DB update. This has to execute parent::update()
* and return its value, nothing else.
* 4. void $object->_on_updated() is executed to notify the class from a successful DB update.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return bool Indicating success.
*/
public static function update(midcom_core_dbaobject $object)
{
if (!self::update_pre_checks($object)) {
debug_add('Pre-flight check returned false', MIDCOM_LOG_ERROR);
return false;
}
if (!$object->__object->update()) {
debug_add("Failed to update the record, last Midgard error: " . midcom_connection::get_error_string());
return false;
}
self::update_post_ops($object);
return true;
}
/**
* Post object creation operations for create
*
* Separated so that dbfactory->import() can reuse the code
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
public static function update_post_ops(midcom_core_dbaobject $object)
{
if ($object->_use_rcs) {
midcom::get()->rcs->update($object, $object->get_rcs_message());
}
$object->_on_updated();
midcom::get()->dispatcher->dispatch(dbaevent::UPDATE, new dbaevent($object));
}
/**
* Add full privileges to the owner of the object.
* This is essentially sets the midgard:owner privilege for the current user.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
private static function _set_owner_privileges(midcom_core_dbaobject $object)
{
if (!midcom::get()->auth->user) {
debug_add("Could not retrieve the midcom_core_user instance for the creator of " . get_class($object) . " {$object->guid}, skipping owner privilege assignment.",
MIDCOM_LOG_INFO);
return;
}
// Circumvent the main privilege class as we need full access here regardless of
// the actual circumstances.
$privilege = new midcom_core_privilege_db();
$privilege->assignee = midcom::get()->auth->user->id;
$privilege->privilegename = 'midgard:owner';
$privilege->objectguid = $object->guid;
$privilege->value = MIDCOM_PRIVILEGE_ALLOW;
if (!$privilege->create()) {
debug_add("Could not set the owner privilege {$privilege->privilegename} for {$object->guid}, see debug level log for details. Last Midgard Error: " . midcom_connection::get_error_string(),
MIDCOM_LOG_WARN);
}
}
/**
* "Pre-flight" checks for create method
*
* Separated so that dbfactory->import() can reuse the code
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
public static function create_pre_checks(midcom_core_dbaobject $object)
{
$parent = $object->get_parent();
if (!is_null($parent)) {
// Attachments are a special case
if (midcom::get()->dbfactory->is_a($object, 'midgard_attachment')) {
if ( !$parent->can_do('midgard:attachments')
|| !$parent->can_do('midgard:update')) {
debug_add("Failed to create attachment, update or attachments privilege on the parent " . get_class($parent) . " {$parent->guid} not granted for the current user.",
MIDCOM_LOG_ERROR);
midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
return false;
}
} elseif ( !$parent->can_do('midgard:create')
&& !midcom::get()->auth->can_user_do('midgard:create', null, get_class($object))) {
debug_add("Failed to create object, create privilege on the parent " . get_class($parent) . " {$parent->guid} or the actual object class not granted for the current user.",
MIDCOM_LOG_ERROR);
midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
return false;
}
} elseif (!midcom::get()->auth->can_user_do('midgard:create', null, get_class($object))) {
debug_add("Failed to create object, general create privilege not granted for the current user.", MIDCOM_LOG_ERROR);
midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
return false;
}
if (!$object->_on_creating()) {
debug_add("The _on_creating event handler returned false.");
return false;
}
// Still check name uniqueness
return self::_pre_check_name($object);
}
/**
* Helper method to call in the _xxx_pre_checks, handles the API
* level checks and automatic operations as specified in ticket #809
*
* @see http://trac.midgard-project.org/ticket/809
* Quoting the ticket API-level section:
* <pre>
* 1. Checks will be done in the pre-flight check phase (ie just after _on_creating/_on_updating)
* 2. If name is not unique false is returned for pre-flight check, preventing create/update
* 2.2 UNLESS a property in the object ('allow_name_catenate') is set to true in which case unique one is generated by catenating an incrementing number to the name.
* 3. if name is empty unique name is generated from title property (unless title is empty too)
* 4. if name is not URL-safe false is returned
* </pre>
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return boolean indicating whether from our point of view everything is ok
*
* @see midcom_helper_reflector_nameresolver::name_is_safe_or_empty()
* @see midcom_helper_reflector_nameresolver::name_is_unique_or_empty()
* @see midcom_helper_reflector_nameresolver::generate_unique_name()
*/
private static function _pre_check_name(midcom_core_dbaobject $object)
{
// Make sure name is empty of unique if the object has such property
$name_property = midcom_helper_reflector::get_name_property($object);
if (empty($name_property)) {
// This object has no name property, return early
return true;
}
$resolver = new midcom_helper_reflector_nameresolver($object);
/**
* If name is empty, try to generate new, unique one
*
* @see http://trac.midgard-project.org/ticket/809
*/
if (empty($object->{$name_property})) {
// name is empty, try to generate
$object->{$name_property} = (string) $resolver->generate_unique_name();
}
/**
* Enforce URL-safe (or empty) names
*
* @see http://trac.midgard-project.org/ticket/809
*/
if (!$resolver->name_is_safe_or_empty()) {
midcom_connection::set_error(MGD_ERR_INVALID_NAME);
return false;
}
/**
* Enforce unique (or empty) names
*
* @see http://trac.midgard-project.org/ticket/809
*/
if (!$resolver->name_is_unique_or_empty()) {
if ($object->allow_name_catenate) {
// Transparent catenation allowed, let's try again.
if ($new_name = $resolver->generate_unique_name()) {
$object->{$name_property} = $new_name;
return true;
}
debug_add('allow_name_catenate was set but midcom_helper_reflector_nameresolver::generate_unique_name() returned empty value, falling through', MIDCOM_LOG_WARN);
}
midcom_connection::set_error(MGD_ERR_OBJECT_NAME_EXISTS);
return false;
}
// All checks ok, we're fine.
return true;
}
/**
* Execute a DB create of the object passed. This will call the corresponding
* event handlers. Calling sequence with method signatures:
*
* 1. Validate privileges using can_do. The user needs midgard:create privilege to the parent object or in general, if there is no parent.
* 2. bool $object->_on_creating() is executed. If it returns false, create is aborted.
* 3. bool $object->__object->create() is executed to do the actual DB create. This has to execute parent::create()
* and return its value, nothing else.
* 4. void $object->_on_created() is executed to notify the class from a successful DB creation.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return bool Indicating success.
*/
public static function create(midcom_core_dbaobject $object)
{
if (!self::create_pre_checks($object)) {
debug_add('Pre-flight check returned false', MIDCOM_LOG_ERROR);
return false;
}
if (is_object($object->metadata)) {
if (!is_null(midcom::get()->auth->user)) {
// Default the authors to current user
if (empty($object->metadata->authors)) {
$object->metadata->set('authors', "|" . midcom::get()->auth->user->guid . "|");
}
// Default the owner to first group of current user
if ( empty($object->metadata->owner)
&& $first_group = midcom::get()->auth->user->get_first_group_guid()) {
$object->metadata->set('owner', $first_group);
}
}
// Default the publication time to current date/time
if (empty($object->metadata->published)) {
$object->metadata->set('published', time());
}
}
if (!$object->__object->create()) {
debug_add("Failed to create " . get_class($object) . ", last Midgard error: " . midcom_connection::get_error_string());
return false;
}
self::create_post_ops($object);
return true;
}
/**
* Post object creation operations for create
*
* Separated so that dbfactory->import() can reuse the code
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
public static function create_post_ops(midcom_core_dbaobject $object)
{
// Now assign all midgard privileges to the creator, this is necessary to get
// an owner like scheme to work by default.
// TODO: Check if there is a better solution like this.
self::_set_owner_privileges($object);
$object->_on_created();
midcom::get()->dispatcher->dispatch(dbaevent::CREATE, new dbaevent($object));
if ($object->_use_rcs) {
midcom::get()->rcs->update($object, $object->get_rcs_message());
}
}
/**
* Execute a DB delete of the object passed. This will call the corresponding
* event handlers. Calling sequence with method signatures:
*
* 1. Validate privileges using can_do. The user needs midgard:delete privilege on the content object.
* 2. bool $object->_on_deleting() is executed. If it returns false, delete is aborted.
* 3. All extensions of the object are deleted
* 4. bool $object->__object->delete() is executed to do the actual DB delete. This has to execute parent::delete()
* and return its value, nothing else.
* 5. void $object->_on_deleted() is executed to notify the class from a successful DB deletion.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return bool Indicating success.
*/
public static function delete(midcom_core_dbaobject $object)
{
if (!self::delete_pre_checks($object)) {
debug_add('Pre-flight check returned false', MIDCOM_LOG_ERROR);
return false;
}
// Delete all extensions:
// Attachments can't have attachments so no need to query those
if (!is_a($object, 'midcom_db_attachment')) {
$list = self::list_attachments($object);
foreach ($list as $attachment) {
if (!$attachment->delete()) {
debug_add("Failed to delete attachment ID {$attachment->id}", MIDCOM_LOG_ERROR);
return false;
}
}
}
$query = new midgard_query_builder('midgard_parameter');
$query->add_constraint('parentguid', '=', $object->guid);
foreach ($query->execute() as $parameter) {
if (!$parameter->delete()) {
debug_add("Failed to delete parameter ID {$parameter->id}", MIDCOM_LOG_ERROR);
return false;
}
}
if (!self::_delete_privileges($object)) {
debug_add('Failed to delete the object privileges.', MIDCOM_LOG_INFO);
return false;
}
// Finally, delete the object itself
if (!$object->__object->delete()) {
debug_add("Failed to delete " . get_class($object) . ", last Midgard error: " . midcom_connection::get_error_string(), MIDCOM_LOG_INFO);
return false;
}
// Explicitly set this in case someone needs to check against it
self::delete_post_ops($object);
return true;
}
/**
* Execute a DB delete of the object passed and delete its descendants. This will call the corresponding
* event handlers. Calling sequence with method signatures:
*
* 1. Get all of the child objects
* 2. Delete them recursively starting from the top, working towards the root
* 3. Finally delete the root object
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return boolean Indicating success.
*/
public static function delete_tree(midcom_core_dbaobject $object)
{
$reflector = midcom_helper_reflector_tree::get($object);
$child_classes = $reflector->get_child_classes();
foreach ($child_classes as $class) {
if ($qb = $reflector->_child_objects_type_qb($class, $object, false)) {
$children = $qb->execute();
// Delete first the descendants
while ($child = array_pop($children)) {
//Inherit RCS status (so that f.x. large tree deletions can run faster)
$child->_use_rcs = $object->_use_rcs;
if (!self::delete_tree($child)) {
debug_print_r('Failed to delete the children of this object:', $object, MIDCOM_LOG_INFO);
return false;
}
}
}
}
if (!self::delete($object)) {
debug_print_r('Failed to delete the object', $object, MIDCOM_LOG_ERROR);
return false;
}
return true;
}
/**
* Post object creation operations for delete
*
* Separated so that dbfactory->import() can reuse the code
*
* @param midcom_core_dbaobject $object The DBA object we're working on
*/
public static function delete_post_ops(midcom_core_dbaobject $object)
{
$object->_on_deleted();
midcom::get()->dispatcher->dispatch(dbaevent::DELETE, new dbaevent($object));
if ($object->_use_rcs) {
midcom::get()->rcs->update($object, $object->get_rcs_message());
}
}
/**
* Undelete objects
*
* @param array $guids
* @param string $type
* @return integer Size of undeleted objects
* @todo We should only undelete parameters & attachments deleted inside some small window of the main objects delete
*/
public static function undelete($guids, $type)
{
$undeleted_size = 0;
$guids = (array) $guids;
foreach ($guids as $guid) {
$object = midcom_helper_reflector::get_object($guid, $type);
if (is_null($object)) {
// Purged, skip
debug_add("Object identified with GUID {$guid} is purged, cannot undelete", MIDCOM_LOG_INFO);
continue;
}
if ($object->undelete($guid)) {
// refresh
$object = midcom::get()->dbfactory->get_object_by_guid($guid);
$undeleted_size += $object->metadata->size;
$parent = $object->get_parent();
if (!empty($parent->guid)) {
// Invalidate parent from cache so content caches have chance to react
midcom::get()->cache->invalidate($parent->guid);
}
} else {
debug_add("Failed to undelete object with GUID {$guid} errstr: " . midcom_connection::get_error_string(), MIDCOM_LOG_ERROR);
}
// FIXME: We should only undelete parameters & attachments deleted inside some small window of the main objects delete
$undeleted_size += self::undelete_parameters($guid);
$undeleted_size += self::undelete_attachments($guid);
//FIXME: are we sure we want to undelete all children here unconditionally, shouldn't it be left as UI decision ??
// List all deleted children
$children_types = midcom_helper_reflector_tree::get_child_objects($object, true);
if (empty($children_types)) {
continue;
}
foreach ($children_types as $type => $children) {
$child_guids = [];
foreach ($children as $child) {
if ($child->metadata->deleted) {
$child_guids[] = $child->guid;
}
}
$undeleted_size += self::undelete($child_guids, $type);
}
}
return $undeleted_size;
}
/**
* Recover the parameters related to a deleted object
*
* @param string $guid
* @return integer Size of undeleted objects
* @todo We should only undelete parameters & attachments deleted inside some small window of the main objects delete
*/
public static function undelete_parameters($guid)
{
$undeleted_size = 0;
$qb = new midgard_query_builder('midgard_parameter');
$qb->include_deleted();
$qb->add_constraint('parentguid', '=', $guid);
$qb->add_constraint('metadata.deleted', '=', true);
$params = $qb->execute();
foreach ($params as $param) {
if ($param->undelete($param->guid)) {
$undeleted_size += $param->metadata->size;
}
}
return $undeleted_size;
}
/**
* Recover the attachments related to a deleted object
*
* @param string $guid
* @return integer Size of undeleted objects
* @todo We should only undelete parameters & attachments deleted inside some small window of the main objects delete
*/
public static function undelete_attachments($guid)
{
$undeleted_size = 0;
$qb = new midgard_query_builder('midgard_attachment');
$qb->include_deleted();
$qb->add_constraint('parentguid', '=', $guid);
$qb->add_constraint('metadata.deleted', '=', true);
$atts = $qb->execute();
foreach ($atts as $att) {
if ($att->undelete($att->guid)) {
midcom::get()->uimessages->add(midcom::get()->i18n->get_string('midgard.admin.asgard', 'midgard.admin.asgard'), sprintf(midcom::get()->i18n->get_string('attachment %s undeleted', 'midgard.admin.asgard'), $att->name, midcom_connection::get_error_string()));
$undeleted_size += $att->metadata->size;
$undeleted_size += self::undelete_parameters($att->guid);
} else {
midcom::get()->uimessages->add(midcom::get()->i18n->get_string('midgard.admin.asgard', 'midgard.admin.asgard'), sprintf(midcom::get()->i18n->get_string('failed undeleting attachment %s, reason %s', 'midgard.admin.asgard'), $att->name, midcom_connection::get_error_string()), 'error');
}
}
return $undeleted_size;
}
/**
* Purge objects
*
* @param array $guids
* @param string $type
* @return integer Size of purged objects
*/
public static function purge($guids, $type)
{
$purged_size = 0;
foreach ($guids as $guid) {
$object = midcom_helper_reflector::get_object($guid, $type);
if (is_null($object)) {
debug_add("Failed to get object {$type} {$guid}", MIDCOM_LOG_ERROR);
// Something wrong
continue;
}
// first kill your children
$children_types = midcom_helper_reflector_tree::get_child_objects($object, true);
if (is_array($children_types)) {
foreach ($children_types as $child_type => $children) {
$child_guids = [];
foreach ($children as $child) {
if (!$child->metadata->deleted) {
$child->delete();
}
$child_guids[] = $child->guid;
}
self::purge($child_guids, $child_type);
}
}
// then shoot your dogs
$purged_size += self::purge_parameters($guid);
$purged_size += self::purge_attachments($guid);
// now shoot yourself
if (!$object->purge()) {
debug_add("Failed to purge object " . get_class($object) . " {$object->guid}", MIDCOM_LOG_INFO);
} else {
$purged_size += $object->metadata->size;
}
}
return $purged_size;
}
/**
* Purge the parameters related to a deleted object
*
* @param string $guid
* @return integer Size of purged objects
*/
public static function purge_parameters($guid)
{
$purged_size = 0;
$qb = new midgard_query_builder('midgard_parameter');
$qb->include_deleted();
$qb->add_constraint('parentguid', '=', $guid);
$params = $qb->execute();
foreach ($params as $param) {
if ($param->purge()) {
$purged_size += $param->metadata->size;
} else {
midcom::get()->uimessages->add(
midcom::get()->i18n->get_string('midgard.admin.asgard', 'midgard.admin.asgard'),
sprintf(midcom::get()->i18n->get_string('failed purging parameter %s => %s, reason %s', 'midgard.admin.asgard'), $param->domain, $param->name, midcom_connection::get_error_string()),
'error'
);
}
}
return $purged_size;
}
/**
* Purge the attachments related to a deleted object
*
* @param string $guid
* @return integer Size of purged objects
*/
public static function purge_attachments($guid)
{
$purged_size = 0;
$qb = new midgard_query_builder('midgard_attachment');
$qb->include_deleted();
$qb->add_constraint('parentguid', '=', $guid);
$atts = $qb->execute();
foreach ($atts as $att) {
if ($att->purge()) {
$purged_size += $att->metadata->size;
self::purge_parameters($att->guid);
} else {
midcom::get()->uimessages->add(midcom::get()->i18n->get_string('midgard.admin.asgard', 'midgard.admin.asgard'), sprintf(midcom::get()->i18n->get_string('failed purging attachment %s => %s, reason %s', 'midgard.admin.asgard'), $att->guid, $att->name, midcom_connection::get_error_string()), 'error');
}
}
return $purged_size;
}
/**
* After we instantiated the midgard object do some post processing and ACL checks
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @see load()
*/
public static function post_db_load_checks(midcom_core_dbaobject $object)
{
if (!$object->can_do('midgard:read')) {
debug_add("Failed to load object, read privilege on the " . get_class($object) . " {$object->guid} not granted for the current user.");
throw new midcom_error_forbidden();
}
$object->_on_loaded();
// Register the GUID as loaded in this request
if (isset(midcom::get()->cache->content)) {
midcom::get()->cache->content->register($object->guid);
}
}
/**
* This is a simple wrapper with (currently) no additional functionality
* over get_by_id that resynchronizes the object state with the database.
* Use this if you think that your current object is stale. It does full
* access control.
*
* On any failure, the object is cleared.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return bool Indicating Success
*/
public static function refresh(midcom_core_dbaobject $object)
{
/**
* Use try/catch here since the object might have been deleted...
* @see http://trac.midgard-project.org/ticket/927
*/
try {
return $object->get_by_id($object->id);
} catch (exception $e) {
return false;
}
}
/**
* This call wraps the original get_by_id call to provide access control.
* The calling sequence is as with the corresponding constructor.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param int $id The id of the object to load from the database.
* @return bool Indicating Success
*/
public static function get_by_id(midcom_core_dbaobject $object, $id)
{
if (!$id) {
debug_add("Failed to load " . get_class($object) . " object, incorrect ID provided.", MIDCOM_LOG_ERROR);
return false;
}
$object->__object->get_by_id((int) $id);
if ($object->id != 0) {
if (!$object->can_do('midgard:read')) {
debug_add("Failed to load object, read privilege on the " . get_class($object) . " {$object->guid} not granted for the current user.",
MIDCOM_LOG_ERROR);
$object->__object = new $object->__mgdschema_class_name__;
return false;
}
$object->_on_loaded();
return true;
}
debug_add("Failed to load the record identified by {$id}, last Midgard error was:" . midcom_connection::get_error_string(), MIDCOM_LOG_INFO);
return false;
}
/**
* This call wraps the original get_by_guid call to provide access control.
* The calling sequence is as with the corresponding constructor.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $guid The guid of the object to load from the database.
* @return bool Indicating Success
*/
public static function get_by_guid(midcom_core_dbaobject $object, $guid)
{
if ( !midcom::get()->auth->admin
&& !midcom::get()->auth->acl->can_do_byguid('midgard:read', $guid, get_class($object), midcom::get()->auth->acl->get_user_id())) {
debug_add("Failed to load object, read privilege on the " . get_class($object) . " {$guid} not granted for the current user.", MIDCOM_LOG_ERROR);
return false;
}
$object->__object->get_by_guid((string) $guid);
if ($object->id != 0) {
$object->_on_loaded();
return true;
}
debug_add("Failed to load the record identified by {$guid}, last Midgard error was: " . midcom_connection::get_error_string(), MIDCOM_LOG_INFO);
return false;
}
/**
* This call wraps the original get_by_guid call to provide access control.
* The calling sequence is as with the corresponding constructor.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $path The path of the object to load from the database.
* @return bool Indicating Success
*/
public static function get_by_path(midcom_core_dbaobject $object, $path)
{
$object->__object->get_by_path((string) $path);
if ($object->id != 0) {
if (!$object->can_do('midgard:read')) {
$object->__object = new $object->__mgdschema_class_name__;
return false;
}
$object->_on_loaded();
return true;
}
return false;
}
/**
* Unconditionally drop all privileges assigned to the given object.
* Called upon successful delete
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return bool Indicating Success.
*/
private static function _delete_privileges(midcom_core_dbaobject $object)
{
$qb = new midgard_query_builder('midcom_core_privilege_db');
$qb->add_constraint('objectguid', '=', $object->guid);
$result = $qb->execute();
foreach ($result as $dbpriv) {
if (!$dbpriv->purge()) {
return false;
}
}
return true;
}
/**
* Return a parameter from the database.
*
* No event handlers are called here yet.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $domain The parameter domain.
* @param string $name The parameter name.
* @return string The parameter value or false otherwise (remember typesafe comparisons to protect against '' strings).
*/
public static function get_parameter(midcom_core_dbaobject $object, $domain, $name)
{
if (!$object->guid) {
debug_add('Cannot retrieve information on a non-persistant object.', MIDCOM_LOG_WARN);
return false;
}
if (isset(self::$parameter_cache[$object->guid][$domain])) {
// We have this domain in cache already thanks to some parameter listing
if (!isset(self::$parameter_cache[$object->guid][$domain][$name])) {
return null;
}
return self::$parameter_cache[$object->guid][$domain][$name];
}
// Not in cache, query from MgdSchema API directly
return $object->__object->get_parameter($domain, $name);
}
/**
* List the parameters of an object. This will either list the parameters of
* a single domain or the complete set of parameters, depending on the value
* of $domain.
*
* It delegates the actual execution to two separate helper functions.
*
* No event handlers are called here yet.
*
* In case of a complete query, the result will be an associative array indexed
* by the domain name and containing another array with parameter name/value pairs.
* For example:
*
* <pre>
* Array
* (
* [Asgard] => Array
* (
* [lang] => en_US
* [act] => view
* [actloc] => tree
* )
* [AsgardTreeHost] => Array
* (
* [selected] => host0
* )
* )
* </pre>
*
* If you query only a single domain, the result will be a single associative
* array containing the parameter name/value pairs. For example:
*
* <pre>
* Array
* (
* [lang] => en_US
* [act] => view
* [actloc] => tree
* )
* </pre>
*
* In both cases an empty Array will indicate that no parameter was found, while
* false will indicate a failure while querying the database.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $domain The parameter domain to query, this may be null to indicate a full listing.
* @return array Parameter list (see above for details) or false on failure.
*/
public static function list_parameters(midcom_core_dbaobject $object, $domain)
{
if (!$object->guid) {
debug_add('Cannot retrieve information on a non-persistant object.', MIDCOM_LOG_WARN);
return false;
}
if (!is_null($domain)) {
return self::_list_parameters_domain($object, $domain);
}
return self::_list_parameters_all($object);
}
/**
* List the parameters of a single domain of an object.
*
* No event handlers are called here yet.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $domain The parameter domain to query.
* @return array Parameter listing.
* @see list_parameters()
*/
private static function _list_parameters_domain(midcom_core_dbaobject $object, $domain)
{
if (!isset(self::$parameter_cache[$object->guid])) {
self::$parameter_cache[$object->guid] = [];
}
if (isset(self::$parameter_cache[$object->guid][$domain])) {
return self::$parameter_cache[$object->guid][$domain];
}
self::$parameter_cache[$object->guid][$domain] = [];
$mc = midgard_parameter::new_collector('parentguid', $object->guid);
$mc->set_key_property('name');
$mc->add_value_property('value');
$mc->add_constraint('domain', '=', $domain);
$mc->execute();
$parameters = $mc->list_keys();
foreach ($parameters as $name => $values) {
self::$parameter_cache[$object->guid][$domain][$name] = $mc->get_subkey($name, 'value');
}
return self::$parameter_cache[$object->guid][$domain];
}
/**
* List all parameters of an object.
*
* No event handlers are called here yet.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @return array Parameter listing
* @see list_parameters()
*/
private static function _list_parameters_all(midcom_core_dbaobject $object)
{
if (!isset(self::$parameter_cache[$object->guid])) {
self::$parameter_cache[$object->guid] = [];
}
if (!isset(self::$parameter_all[$object->guid])) {
$mc = midgard_parameter::new_collector('parentguid', $object->guid);
$mc->set_key_property('guid');
$mc->add_value_property('domain');
$mc->add_value_property('name');
$mc->add_value_property('value');
$mc->execute();
$parameters = $mc->list_keys();
foreach ($parameters as $guid => $values) {
$name = $mc->get_subkey($guid, 'name');
$domain = $mc->get_subkey($guid, 'domain');
if (!isset(self::$parameter_cache[$object->guid][$domain])) {
self::$parameter_cache[$object->guid][$domain] = [];
}
self::$parameter_cache[$object->guid][$domain][$name] = $mc->get_subkey($guid, 'value');
}
// Flag that we have queried all domains for this object
self::$parameter_all[$object->guid] = true;
}
// Clean up empty arrays
return array_filter(self::$parameter_cache[$object->guid], 'count');
}
/**
* Set a parameter of a given object to the value specified.
*
* This is either a create or an update operation depending on whether there was
* already a parameter of that domain/name present, or not.
*
* The user needs both update and parameter manipulationpermission on the parent object for updates.
*
* @param midcom_core_dbaobject $object The DBA object we're working on
* @param string $domain The Parameter Domain.
* @param string $name The Parameter name.
* @param string $value The Parameter value. If this is empty, the corresponding parameter is deleted.
* @return bool Indicating success.
*/
public static function set_parameter(midcom_core_dbaobject $object, $domain, $name, $value)
{
if (!$object->guid) {
debug_add('Cannot set parameters on a non-persistant object.', MIDCOM_LOG_WARN);
return false;
}
if ( empty($domain)
|| empty($name)
|| !is_string($domain)
|| !is_string($name)) {
debug_add('Parameter domain and name must be non-empty strings', MIDCOM_LOG_WARN);
debug_print_r('$domain', $domain);
debug_print_r('$name', $name);
return false;
}
if ( !$object->can_do('midgard:update')
|| !$object->can_do('midgard:parameters')) {
debug_add("Failed to set parameters, midgard:update or midgard:parameters on the " . get_class($object) . " {$object->guid} not granted for the current user.",
MIDCOM_LOG_ERROR);
midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
return false;
}
// Set via MgdSchema API directly
if (!$object->__object->set_parameter($domain, $name, (string) $value)) {
return false;
}