@@ -988,164 +988,47 @@ public function updateDocument(Document $collection, string $id, Document $docum
988988 if (!$ skipPermissions ) {
989989 /**
990990 * Permission rows in the _perms table are keyed by the document's
991- * UID (the _document column). When the UID changes, the existing
992- * rows must be re-pointed to the new UID before the diff below is
993- * applied, otherwise the old rows are orphaned and unchanged
994- * permissions are lost for the new UID.
991+ * UID (the _document column). Replace the permissions with a full
992+ * rewrite: delete every existing row for the (old) UID, then
993+ * re-insert the document's current permission set under the (new)
994+ * UID. This keeps the logic simple and correctly re-keys the
995+ * permission rows when the UID changes.
995996 */
996997 $ newUid = $ document ->offsetExists ('$id ' ) ? $ document ->getId () : $ id ;
997- $ uidChanged = $ newUid !== $ id ;
998998
999999 $ sql = "
1000- SELECT _type, _permission
1001- FROM {$ this ->getSQLTable ($ name . '_perms ' )}
1000+ DELETE FROM {$ this ->getSQLTable ($ name . '_perms ' )}
10021001 WHERE _document = :_uid
10031002 {$ this ->getTenantQuery ($ collection )}
10041003 " ;
10051004
1006- $ sql = $ this ->trigger (Database::EVENT_PERMISSIONS_READ , $ sql );
1005+ $ sql = $ this ->trigger (Database::EVENT_PERMISSIONS_DELETE , $ sql );
10071006
1008- /**
1009- * Get current permissions from the database
1010- */
1011- $ sqlPermissions = $ this ->getPDO ()->prepare ($ sql );
1012-
1013- $ sqlPermissions ->bindValue (':_uid ' , $ id );
1007+ $ stmtRemovePermissions = $ this ->getPDO ()->prepare ($ sql );
1008+ $ stmtRemovePermissions ->bindValue (':_uid ' , $ id );
10141009
10151010 if ($ this ->sharedTables ) {
1016- $ sqlPermissions ->bindValue (':_tenant ' , $ this ->tenant );
1017- }
1018-
1019- $ sqlPermissions ->execute ();
1020- $ permissions = $ sqlPermissions ->fetchAll ();
1021- $ sqlPermissions ->closeCursor ();
1022-
1023- $ initial = [];
1024- foreach (Database::PERMISSIONS as $ type ) {
1025- $ initial [$ type ] = [];
1011+ $ stmtRemovePermissions ->bindValue (':_tenant ' , $ this ->tenant );
10261012 }
10271013
1028- $ permissions = array_reduce ($ permissions , function (array $ carry , array $ item ) {
1029- $ carry [$ item ['_type ' ]][] = $ item ['_permission ' ];
1030-
1031- return $ carry ;
1032- }, $ initial );
1033-
10341014 /**
1035- * Get removed Permissions
1015+ * Insert the document's full current permission set under the
1016+ * (new) UID.
10361017 */
1037- $ removals = [];
1018+ $ values = [];
10381019 foreach (Database::PERMISSIONS as $ type ) {
1039- $ diff = \array_diff ($ permissions [$ type ], $ document ->getPermissionsByType ($ type ));
1040- if (!empty ($ diff )) {
1041- $ removals [$ type ] = $ diff ;
1042- }
1043- }
1044-
1045- /**
1046- * Get added Permissions
1047- */
1048- $ additions = [];
1049- foreach (Database::PERMISSIONS as $ type ) {
1050- $ diff = \array_diff ($ document ->getPermissionsByType ($ type ), $ permissions [$ type ]);
1051- if (!empty ($ diff )) {
1052- $ additions [$ type ] = $ diff ;
1053- }
1054- }
1055-
1056- /**
1057- * Query to re-point existing permissions to the new UID
1058- */
1059- if ($ uidChanged ) {
1060- $ sql = "
1061- UPDATE {$ this ->getSQLTable ($ name . '_perms ' )}
1062- SET _document = :_newUid
1063- WHERE _document = :_uid
1064- {$ this ->getTenantQuery ($ collection )}
1065- " ;
1066-
1067- $ stmtRepointPermissions = $ this ->getPDO ()->prepare ($ sql );
1068- $ stmtRepointPermissions ->bindValue (':_uid ' , $ id );
1069- $ stmtRepointPermissions ->bindValue (':_newUid ' , $ newUid );
1070-
1071- if ($ this ->sharedTables ) {
1072- $ stmtRepointPermissions ->bindValue (':_tenant ' , $ this ->tenant );
1073- }
1074- }
1075-
1076- /**
1077- * Query to remove permissions
1078- */
1079- $ removeQuery = '' ;
1080- if (!empty ($ removals )) {
1081- $ removeQuery = ' AND ( ' ;
1082- foreach ($ removals as $ type => $ permissions ) {
1083- $ removeQuery .= "(
1084- _type = ' {$ type }'
1085- AND _permission IN ( " . implode (', ' , \array_map (fn (string $ i ) => ":_remove_ {$ type }_ {$ i }" , \array_keys ($ permissions ))) . ")
1086- ) " ;
1087- if ($ type !== \array_key_last ($ removals )) {
1088- $ removeQuery .= ' OR ' ;
1089- }
1020+ foreach ($ document ->getPermissionsByType ($ type ) as $ i => $ _ ) {
1021+ $ tenantPlaceholder = $ this ->sharedTables ? ', :_tenant ' : '' ;
1022+ $ values [] = "( :_uid, ' {$ type }', :_add_ {$ type }_ {$ i }{$ tenantPlaceholder }) " ;
10901023 }
10911024 }
1092- if (!empty ($ removeQuery )) {
1093- $ removeQuery .= ') ' ;
1094- $ sql = "
1095- DELETE
1096- FROM {$ this ->getSQLTable ($ name . '_perms ' )}
1097- WHERE _document = :_uid
1098- {$ this ->getTenantQuery ($ collection )}
1099- " ;
11001025
1101- $ removeQuery = $ sql . $ removeQuery ;
1102-
1103- $ removeQuery = $ this ->trigger (Database::EVENT_PERMISSIONS_DELETE , $ removeQuery );
1104-
1105- $ stmtRemovePermissions = $ this ->getPDO ()->prepare ($ removeQuery );
1106- $ stmtRemovePermissions ->bindValue (':_uid ' , $ newUid );
1107-
1108- if ($ this ->sharedTables ) {
1109- $ stmtRemovePermissions ->bindValue (':_tenant ' , $ this ->tenant );
1110- }
1111-
1112- foreach ($ removals as $ type => $ permissions ) {
1113- foreach ($ permissions as $ i => $ permission ) {
1114- $ stmtRemovePermissions ->bindValue (":_remove_ {$ type }_ {$ i }" , $ permission );
1115- }
1116- }
1117- }
1118-
1119- /**
1120- * Query to add permissions
1121- */
1122- if (!empty ($ additions )) {
1123- $ values = [];
1124- foreach ($ additions as $ type => $ permissions ) {
1125- foreach ($ permissions as $ i => $ _ ) {
1126- $ value = "( :_uid, ' {$ type }', :_add_ {$ type }_ {$ i }" ;
1127-
1128- if ($ this ->sharedTables ) {
1129- $ value .= ", :_tenant) " ;
1130- } else {
1131- $ value .= ") " ;
1132- }
1133-
1134- $ values [] = $ value ;
1135- }
1136- }
1026+ if (!empty ($ values )) {
1027+ $ tenantColumn = $ this ->sharedTables ? ', _tenant ' : '' ;
11371028
11381029 $ sql = "
1139- INSERT INTO {$ this ->getSQLTable ($ name . '_perms ' )} (_document, _type, _permission
1140- " ;
1141-
1142- if ($ this ->sharedTables ) {
1143- $ sql .= ', _tenant) ' ;
1144- } else {
1145- $ sql .= ') ' ;
1146- }
1147-
1148- $ sql .= " VALUES " . \implode (', ' , $ values );
1030+ INSERT INTO {$ this ->getSQLTable ($ name . '_perms ' )} (_document, _type, _permission {$ tenantColumn })
1031+ VALUES " . \implode (', ' , $ values );
11491032
11501033 $ sql = $ this ->trigger (Database::EVENT_PERMISSIONS_CREATE , $ sql );
11511034
@@ -1157,8 +1040,8 @@ public function updateDocument(Document $collection, string $id, Document $docum
11571040 $ stmtAddPermissions ->bindValue (":_tenant " , $ this ->tenant );
11581041 }
11591042
1160- foreach ($ additions as $ type => $ permissions ) {
1161- foreach ($ permissions as $ i => $ permission ) {
1043+ foreach (Database:: PERMISSIONS as $ type ) {
1044+ foreach ($ document -> getPermissionsByType ( $ type ) as $ i => $ permission ) {
11621045 $ stmtAddPermissions ->bindValue (":_add_ {$ type }_ {$ i }" , $ permission );
11631046 }
11641047 }
@@ -1240,9 +1123,6 @@ public function updateDocument(Document $collection, string $id, Document $docum
12401123
12411124 $ stmt ->execute ();
12421125
1243- if (isset ($ stmtRepointPermissions )) {
1244- $ stmtRepointPermissions ->execute ();
1245- }
12461126 if (isset ($ stmtRemovePermissions )) {
12471127 $ stmtRemovePermissions ->execute ();
12481128 }
0 commit comments