| @@ -348,9 +348,9 @@ | ||
| 348 | 348 | if ( ! $update ) { |
| 349 | 349 | return false; |
| 350 | 350 | } |
| 351 | 351 | |
| 352 | - $new_values = self::package_entry_to_update( $id, $values ); | |
| 352 | + $new_values = self::package_entry_to_update( $id, $values, $update_type ); | |
| 353 | 353 | $query_results = $wpdb->update( $wpdb->prefix . 'frm_items', $new_values, compact( 'id' ) ); |
| 354 | 354 | |
| 355 | 355 | self::after_update_entry( $query_results, $id, $values, $new_values ); |
| 356 | 356 | |
| @@ -781,9 +781,9 @@ | ||
| 781 | 781 | if ( $type !== 'xml' ) { |
| 782 | 782 | $values = apply_filters( 'frm_pre_create_entry', $values ); |
| 783 | 783 | } |
| 784 | 784 | |
| 785 | - return self::package_entry_data( $values ); | |
| 785 | + return self::package_entry_data( $values, $type ); | |
| 786 | 786 | } |
| 787 | 787 | |
| 788 | 788 | /** |
| 789 | 789 | * Create an entry and perform after create actions |
| @@ -838,13 +838,14 @@ | ||
| 838 | 838 | * Prepare the new values for inserting into the database |
| 839 | 839 | * |
| 840 | 840 | * @since 2.0.16 |
| 841 | 841 | * |
| 842 | - * @param array $values | |
| 842 | + * @param array $values | |
| 843 | + * @param string $type The create type. 'xml' for an import. | |
| 843 | 844 | * |
| 844 | 845 | * @return array New values. |
| 845 | 846 | */ |
| 846 | - private static function package_entry_data( &$values ) { | |
| 847 | + private static function package_entry_data( &$values, $type = 'standard' ) { | |
| 847 | 848 | global $wpdb; |
| 848 | 849 | |
| 849 | 850 | if ( ! isset( $values['item_key'] ) ) { |
| 850 | 851 | $values['item_key'] = ''; |
| @@ -861,12 +862,12 @@ | ||
| 861 | 862 | 'parent_item_id' => (int) self::get_entry_value( $values, 'parent_item_id', 0 ), |
| 862 | 863 | 'created_at' => self::get_created_at( $values ), |
| 863 | 864 | 'updated_at' => self::get_updated_at( $values ), |
| 864 | 865 | 'description' => self::get_entry_description( $values ), |
| 865 | - 'user_id' => self::get_entry_user_id( $values ), | |
| 866 | + 'user_id' => self::get_entry_user_id( $values, $type ), | |
| 866 | 867 | ); |
| 867 | 868 | |
| 868 | - $new_values['updated_by'] = $values['updated_by'] ?? $new_values['user_id']; | |
| 869 | + $new_values['updated_by'] = self::get_updated_by( $values, $type, $new_values['user_id'] ); | |
| 869 | 870 | |
| 870 | 871 | return $new_values; |
| 871 | 872 | } |
| 872 | 873 | |
| @@ -881,8 +882,33 @@ | ||
| 881 | 882 | return $values[ $name ] ?? $default; |
| 882 | 883 | } |
| 883 | 884 | |
| 884 | 885 | /** |
| 886 | + * Get the updated_by value for an entry. | |
| 887 | + * | |
| 888 | + * The submitted value is only used during a trusted import, which restores the user who last | |
| 889 | + * edited each entry. Every other save is being made by the current user, so a submitted | |
| 890 | + * updated_by is ignored and cannot be pointed at another account. This matters because | |
| 891 | + * updated_by is treated as a privilege signal when deciding how much HTML to strip from entry | |
| 892 | + * values in FrmFieldType::should_strip_most_html(). | |
| 893 | + * | |
| 894 | + * @since 6.35 | |
| 895 | + * | |
| 896 | + * @param array $values | |
| 897 | + * @param string $type The create/update type. 'xml' for an import. | |
| 898 | + * @param int|string $default The value to use when an import doesn't include updated_by. | |
| 899 | + * | |
| 900 | + * @return int | |
| 901 | + */ | |
| 902 | + private static function get_updated_by( $values, $type, $default ) { | |
| 903 | + if ( self::is_trusted_import( $type ) ) { | |
| 904 | + return absint( self::get_entry_value( $values, 'updated_by', $default ) ); | |
| 905 | + } | |
| 906 | + | |
| 907 | + return get_current_user_id(); | |
| 908 | + } | |
| 909 | + | |
| 910 | + /** | |
| 885 | 911 | * Get the ip for a new entry. |
| 886 | 912 | * Allow the import to override the value. |
| 887 | 913 | * |
| 888 | 914 | * @since 2.03.10 |
| @@ -974,14 +1000,15 @@ | ||
| 974 | 1000 | * Get the user_id value for a new entry |
| 975 | 1001 | * |
| 976 | 1002 | * @since 2.0.16 |
| 977 | 1003 | * |
| 978 | - * @param array $values | |
| 1004 | + * @param array $values | |
| 1005 | + * @param string $type The create type. 'xml' for an import. | |
| 979 | 1006 | * |
| 980 | 1007 | * @return int |
| 981 | 1008 | */ |
| 982 | - private static function get_entry_user_id( $values ) { | |
| 983 | - if ( isset( $values['frm_user_id'] ) && ( is_numeric( $values['frm_user_id'] ) || FrmAppHelper::is_admin() ) ) { | |
| 1009 | + private static function get_entry_user_id( $values, $type = 'standard' ) { | |
| 1010 | + if ( isset( $values['frm_user_id'] ) && self::can_set_entry_user_id_from_values( $type ) ) { | |
| 984 | 1011 | return $values['frm_user_id']; |
| 985 | 1012 | } |
| 986 | 1013 | |
| 987 | 1014 | $current_user_id = get_current_user_id(); |
| @@ -988,8 +1015,46 @@ | ||
| 988 | 1015 | return $current_user_id ? $current_user_id : 0; |
| 989 | 1016 | } |
| 990 | 1017 | |
| 991 | 1018 | /** |
| 1019 | + * Whether a submitted frm_user_id is allowed to set the entry owner. | |
| 1020 | + * | |
| 1021 | + * The owner is only taken from the submitted value when the current user is allowed to manage | |
| 1022 | + * entries, or during a trusted import that restores each entry's original owner. On a public | |
| 1023 | + * submission neither is true, so the owner falls back to the current user and cannot be set to | |
| 1024 | + * another account. | |
| 1025 | + * | |
| 1026 | + * @since 6.34 | |
| 1027 | + * | |
| 1028 | + * @param string $type The create/update type. 'xml' for an import. | |
| 1029 | + * | |
| 1030 | + * @return bool | |
| 1031 | + */ | |
| 1032 | + private static function can_set_entry_user_id_from_values( $type = 'standard' ) { | |
| 1033 | + if ( self::is_trusted_import( $type ) ) { | |
| 1034 | + return true; | |
| 1035 | + } | |
| 1036 | + | |
| 1037 | + return current_user_can( 'frm_edit_entries' ) || current_user_can( 'administrator' ); | |
| 1038 | + } | |
| 1039 | + | |
| 1040 | + /** | |
| 1041 | + * Whether an entry is being saved by an import rather than by a normal request. | |
| 1042 | + * | |
| 1043 | + * An import is trusted to restore the values stored on each entry, including the columns that | |
| 1044 | + * are otherwise taken from the current request. | |
| 1045 | + * | |
| 1046 | + * @since 6.35 | |
| 1047 | + * | |
| 1048 | + * @param string $type The create/update type. 'xml' for an import. | |
| 1049 | + * | |
| 1050 | + * @return bool | |
| 1051 | + */ | |
| 1052 | + private static function is_trusted_import( $type = 'standard' ) { | |
| 1053 | + return 'xml' === $type || ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ); | |
| 1054 | + } | |
| 1055 | + | |
| 1056 | + /** | |
| 992 | 1057 | * Insert new entry into the database |
| 993 | 1058 | * |
| 994 | 1059 | * @since 2.0.16 |
| 995 | 1060 | * |
| @@ -1168,14 +1233,15 @@ | ||
| 1168 | 1233 | * Package the entry data for updating |
| 1169 | 1234 | * |
| 1170 | 1235 | * @since 2.0.16 |
| 1171 | 1236 | * |
| 1172 | - * @param int $id | |
| 1173 | - * @param array $values | |
| 1237 | + * @param int $id | |
| 1238 | + * @param array $values | |
| 1239 | + * @param string $update_type The update type. 'xml' for an import. | |
| 1174 | 1240 | * |
| 1175 | 1241 | * @return array New values. |
| 1176 | 1242 | */ |
| 1177 | - private static function package_entry_to_update( $id, $values ) { | |
| 1243 | + private static function package_entry_to_update( $id, $values, $update_type = 'standard' ) { | |
| 1178 | 1244 | global $wpdb; |
| 1179 | 1245 | |
| 1180 | 1246 | $new_values = array( |
| 1181 | 1247 | 'name' => FrmAppHelper::truncate( self::get_new_entry_name( $values ), 255, 1, '', true ), |
| @@ -1181,9 +1247,9 @@ | ||
| 1181 | 1247 | 'name' => FrmAppHelper::truncate( self::get_new_entry_name( $values ), 255, 1, '', true ), |
| 1182 | 1248 | 'form_id' => (int) self::get_entry_value( $values, 'form_id', null ), |
| 1183 | 1249 | 'is_draft' => self::get_is_draft_value( $values ), |
| 1184 | 1250 | 'updated_at' => current_time( 'mysql', 1 ), |
| 1185 | - 'updated_by' => $values['updated_by'] ?? get_current_user_id(), | |
| 1251 | + 'updated_by' => self::get_updated_by( $values, $update_type, get_current_user_id() ), | |
| 1186 | 1252 | ); |
| 1187 | 1253 | |
| 1188 | 1254 | if ( isset( $values['post_id'] ) ) { |
| 1189 | 1255 | $new_values['post_id'] = (int) $values['post_id']; |
| @@ -1196,9 +1262,9 @@ | ||
| 1196 | 1262 | if ( isset( $values['parent_item_id'] ) ) { |
| 1197 | 1263 | $new_values['parent_item_id'] = (int) $values['parent_item_id']; |
| 1198 | 1264 | } |
| 1199 | 1265 | |
| 1200 | - if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) ) { | |
| 1266 | + if ( isset( $values['frm_user_id'] ) && is_numeric( $values['frm_user_id'] ) && self::can_set_entry_user_id_from_values( $update_type ) ) { | |
| 1201 | 1267 | $new_values['user_id'] = $values['frm_user_id']; |
| 1202 | 1268 | } |
| 1203 | 1269 | |
| 1204 | 1270 | return apply_filters( 'frm_update_entry', $new_values, $id ); |