Skip to content

Commit 011b3d3

Browse files
committed
Merge pull request alleyinteractive#278 from alleyinteractive/user_store_property
Added store_property to Fieldmanager_Datasource_User
2 parents 9ad5012 + 70b16e6 commit 011b3d3

File tree

2 files changed

+402
-24
lines changed

2 files changed

+402
-24
lines changed

php/datasource/class-fieldmanager-datasource-user.php

Lines changed: 126 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
/**
7-
* Data source for WordPress Posts, for autocomplete and option types.
7+
* Data source for WordPress Users, for autocomplete and option types.
88
* @package Fieldmanager_Datasource
99
*/
1010
class Fieldmanager_Datasource_User extends Fieldmanager_Datasource {
@@ -35,6 +35,25 @@ class Fieldmanager_Datasource_User extends Fieldmanager_Datasource {
3535
*/
3636
public $display_property = 'display_name';
3737

38+
/**
39+
* @var array
40+
* Allowed display properties for validation.
41+
*/
42+
protected $allowed_display_properties = array( 'display_name', 'user_login', 'user_email', 'user_nicename' );
43+
44+
/**
45+
* @var string
46+
* Store property. Defaults to ID, but can also be 'user_login', 'user_email',
47+
* or 'user_nicename'.
48+
*/
49+
public $store_property = 'ID';
50+
51+
/**
52+
* @var array
53+
* Allowed store properties for validation.
54+
*/
55+
protected $allowed_store_properties = array( 'ID', 'user_login', 'user_email', 'user_nicename' );
56+
3857
/**
3958
* @var string
4059
* Capability required to refer to a user via this datasource.
@@ -44,23 +63,66 @@ class Fieldmanager_Datasource_User extends Fieldmanager_Datasource {
4463

4564
/**
4665
* @var string|Null
47-
* If not empty, set this post's ID as a value on the user. This is used to
66+
* If not empty, set this object's ID as a value on the user. This is used to
4867
* establish two-way relationships.
4968
*/
5069
public $reciprocal = Null;
5170

52-
// constructor not required for this datasource; options are just set to keys,
53-
// which Fieldmanager_Datasource does.
71+
/**
72+
* Constructor. Used for validation.
73+
*/
74+
public function __construct( $options = array() ) {
75+
parent::__construct( $options );
76+
77+
// Validate improper usage of store property
78+
if ( ! in_array( $this->store_property, $this->allowed_store_properties ) ) {
79+
throw new FM_Developer_Exception( sprintf(
80+
__( 'Store property %s is invalid. Must be one of %s.', 'fieldmanager' ),
81+
$this->store_property,
82+
implode( ', ', $this->allowed_store_properties )
83+
) );
84+
}
85+
86+
if ( ! empty( $this->reciprocal ) && 'ID' != $this->store_property ) {
87+
throw new FM_Developer_Exception( __( 'You cannot use reciprocal relationships with FM_Datasource_User if store_property is not set to ID.', 'fieldmanager' ) );
88+
}
89+
90+
// Validate improper usage of display property
91+
if ( ! in_array( $this->display_property, $this->allowed_display_properties ) ) {
92+
throw new FM_Developer_Exception( sprintf(
93+
__( 'Display property %s is invalid. Must be one of %s.', 'fieldmanager' ),
94+
$this->display_property,
95+
implode( ', ', $this->allowed_display_properties )
96+
) );
97+
}
98+
}
5499

55100
/**
56-
* Get a post title by post ID
101+
* Get a user by the specified field.
57102
* @param int $value post_id
58-
* @return string post title
103+
* @return int|string
59104
*/
60105
public function get_value( $value ) {
61-
$id = intval( $value );
62-
$user = get_userdata( $id );
63-
return $user ? $user->{$this->display_property} : '';
106+
switch ( $this->store_property ) {
107+
case 'ID':
108+
$field = 'id';
109+
break;
110+
case 'user_nicename':
111+
$field = 'slug';
112+
break;
113+
case 'user_email':
114+
$field = 'email';
115+
break;
116+
case 'user_login':
117+
$field = 'login';
118+
break;
119+
}
120+
121+
// Sanitize the value
122+
$value = $this->sanitize_value( $value );
123+
124+
$user = get_user_by( $field, $value );
125+
return $user ? $user->{$this->display_property} : '';
64126
}
65127

66128
/**
@@ -73,14 +135,20 @@ public function get_items( $fragment = Null ) {
73135
if ( is_callable( $this->query_callback ) ) {
74136
return call_user_func( $this->query_callback, $fragment );
75137
}
138+
76139
$default_args = array();
77140
$user_args = array_merge( $default_args, $this->query_args );
78141
$ret = array();
79-
if ( $fragment ) $user_args['search'] = $fragment;
142+
143+
if ( $fragment ) {
144+
$user_args['search'] = $fragment;
145+
}
146+
80147
$users = get_users( $user_args );
81148
foreach ( $users as $u ) {
82-
$ret[$u->ID] = $u->{$this->display_property};
149+
$ret[ $u->{$this->store_property} ] = $u->{$this->display_property};
83150
}
151+
84152
return $ret;
85153
}
86154

@@ -98,44 +166,57 @@ public function get_ajax_action() {
98166
}
99167

100168
/**
101-
* For post relationships, delete reciprocal post metadata prior to saving (presave will re-add)
169+
* Delete reciprocal user metadata prior to saving (presave will re-add).
170+
* Reciprocal relationships are not possible if we are not storing by ID.
102171
* @param array $values new post values
103172
* @param array $current_values existing post values
104173
*/
105174
public function presave_alter_values( Fieldmanager_Field $field, $values, $current_values ) {
106-
if ( $field->data_type != 'post' || !$this->reciprocal ) return $values;
107-
foreach ( $current_values as $user_id ) {
108-
delete_user_meta( $user_id, $this->reciprocal, $field->data_id );
109-
}
175+
if ( $field->data_type != 'post' || ! $this->reciprocal || 'ID' != $this->store_property ) {
176+
return $values;
177+
}
178+
179+
if ( ! empty( $current_values ) ) {
180+
foreach ( $current_values as $user_id ) {
181+
delete_user_meta( $user_id, $this->reciprocal, $field->data_id );
182+
}
183+
}
184+
110185
return $values;
111186
}
112187

113188
/**
114-
* Handle reciprocal postmeta
189+
* Handle reciprocal usermeta.
190+
* Reciprocal relationships are not possible if we are not storing by ID.
115191
* @param int $value
116192
* @return string
117193
*/
118194
public function presave( Fieldmanager_Field $field, $value, $current_value ) {
119-
if ( empty( $value ) ) return;
195+
if ( empty( $value ) ) {
196+
return;
197+
}
198+
120199
$return_single = False;
121200
if ( !is_array( $value ) ) {
122201
$return_single = True;
123202
$value = array( $value );
124203
}
204+
125205
foreach ( $value as $i => $v ) {
126-
$value[$i] = intval( $v );
127-
if( !current_user_can( $this->capability, $v ) ) {
206+
$value[$i] = $this->sanitize_value( $v );
207+
if( ! current_user_can( $this->capability, $v ) ) {
128208
wp_die( esc_html( sprintf( __( 'Tried to refer to user "%s" which current user cannot edit.', 'fieldmanager' ), $v ) ) );
129209
}
130-
if ( $this->reciprocal ) {
210+
if ( $this->reciprocal && 'ID' == $this->store_property ) {
131211
add_user_meta( $v, $this->reciprocal, $field->data_id );
132212
}
133213
}
214+
134215
return $return_single ? $value[0] : $value;
135216
}
136217

137218
/**
138-
* Get view link for a user
219+
* Get view link for a user.
139220
* @param int $value
140221
* @return string
141222
*/
@@ -144,7 +225,7 @@ public function get_view_link( $value ) {
144225
}
145226

146227
/**
147-
* Get edit link for a user
228+
* Get edit link for a user.
148229
* @param int $value
149230
* @return string
150231
*/
@@ -156,4 +237,25 @@ public function get_edit_link( $value ) {
156237
esc_html__( 'Edit', 'fieldmanager' )
157238
);
158239
}
159-
}
240+
241+
/**
242+
* Sanitize the value based on store_property.
243+
* @param int|string $value
244+
* @return int|string
245+
*/
246+
protected function sanitize_value( $value ) {
247+
switch ( $this->store_property ) {
248+
case 'ID':
249+
$value = intval( $value );
250+
break;
251+
case 'user_email':
252+
$value = sanitize_email( $value );
253+
break;
254+
default:
255+
$value = sanitize_text_field( $value );
256+
break;
257+
}
258+
259+
return $value;
260+
}
261+
}

0 commit comments

Comments
 (0)