-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathclass-endpoint.php
More file actions
337 lines (307 loc) · 10 KB
/
class-endpoint.php
File metadata and controls
337 lines (307 loc) · 10 KB
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
<?php
namespace Multisite_JSON_API;
include 'exceptions.php';
class Endpoint {
function __construct($testing=false){
$this->json = $this->get_post_data();
$this->request_method = $_SERVER['REQUEST_METHOD'];
}
/*
* Dumps the given object to JSON and responds with the given status code
* @since '0.0.1'
* @return void
*/
public function respond_with_json($payload, $status=200) {
status_header($status);
print json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
/*
* Sends an error in JSON with the given status, then dies
* @since '0.0.1'
* @return void
*/
public function error($error, $error_id, $status=400, $url='https://github.com/remkade/multisite-json-api/wiki') {
$output = array('id'=> $error_id, 'message' => $error, 'url' => $url);
$this->respond_with_json($output, $status);
}
/**
* Takes an exception and calls the error function with it's details
* @since '0.5.0'
* @param e GenericException A multisite JSON API Generic Exception derivative
*/
public function json_exception(GenericException $e) {
$this->error($e->getMessage(), $e->id, $e->getCode(), $e->url);
}
/*
* Pulls the post data in a hacky way required by PHP :(
* @since '0.0.1'
*/
public function get_post_data() {
$post = file_get_contents('php://input');
return(json_decode($post));
}
/*
* Authenticates using the HTTP Headers User and Password
* @since '0.0.1'
*/
public function authenticate() {
$creds = array();
// Allow the usage of the standard basic auth pattern
if ( isset( $_SERVER['PHP_AUTH_USER'] ) ) {
$creds['user_login'] = $_SERVER['PHP_AUTH_USER'];
$creds['user_password'] = $_SERVER['PHP_AUTH_PW'];
} else {
$creds['user_login'] = $_SERVER['HTTP_USER'];
$creds['user_password'] = $_SERVER['HTTP_PASSWORD'];
}
$creds['remember'] = false;
$user = wp_signon($creds, false);
if(is_wp_error($user)) {
return false;
} else {
wp_set_current_user($user->ID, '');
if(current_user_can('manage_sites'))
return $user;
else
return false;
}
}
/*
* Checks whether sitename is a valid domain name or site name
* Works on both domain and subdirectory
* @since '0.0.1'
*/
public function is_valid_sitename($candidate) {
if(is_subdomain_install()){
if(preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]+$/', $candidate))
return true;
else
return false;
} else {
if(preg_match('/^[a-zA-Z0-9][a-zA-Z0-9_-]+$/', $candidate)) {
$reserved = apply_filters( 'subdirectory_reserved_names', array( 'page', 'comments', 'blog', 'files', 'feed'));
return !in_array($candidate, $reserved);
} else {
return false;
}
}
}
/*
* Validates that the site title is at least 2 alphanumerics and doesn't start with a space
* @since '0.0.1'
*/
public function is_valid_site_title($candidate) {
// Make sure site title is not empty
if(preg_match('/^[a-zA-Z0-9-_][a-zA-Z0-9-_ ]+/', $candidate))
return(true);
else
return(false);
}
/*
* Validates emails via the wordpress functions.
* @since '0.0.1'
* @param email_address
*/
public function is_valid_email($candidate) {
$email = sanitize_email($candidate);
if(!empty($email) && is_email($email))
return true;
else
return false;
}
public function full_domain($sitename, $current_site = null) {
if(empty($current_site))
$current_site = get_current_site();
if(is_subdomain_install()) {
$newdomain = $sitename . '.' . preg_replace( '|^www\.|', '', $current_site->domain );
} else {
$newdomain = $current_site->domain;
}
return $newdomain;
}
public function full_path($sitename, $current_site = null) {
if(empty($current_site))
$current_site = get_current_site();
if(is_subdomain_install()) {
$path = $current_site->path;
} else {
$path = $current_site->path . $sitename . '/';
}
return $path;
}
/**
* Creates a new user if one doesn't already exist.
* If it does exist, just returns the existing user's id.
* Sanitizes email address automatically.
* @param dirty_email string An unsanitized email
* @param username string The username
* @return user WP_User The Wordpress user object
*/
public function get_or_create_user_by_email($dirty_email, $username, $password = null) {
$email = sanitize_email($dirty_email);
if($email === "")
throw new UserCreationError('Error creating user: email is invalid');
$user = get_user_by('email', $email);
if($user)
return $user;
// if the email doesn't exist, lets check for the login
// which we create from the domain
$user = get_user_by('login', $username);
if($user) {
return $user;
} else {
// Create a new user with a random password
if($password === null){
$password = wp_generate_password(12, false);
}
$user_id = wpmu_create_user($username, $password, $email);
wp_new_user_notification($user_id, $password);
// Its possible for the $user_id to be false here, but it seems to
// be only in extreme cases where the database is failing or some
// other odd circumstance happens
return(get_user_by('id', $user_id));
}
}
/**
* Creates a new site.
* @param titel string The title of the site
* @param site_name string The sitename used for the site, will become the path or the subdomain
* @param user_id The ID of the admin user for this site
* @param options The options for the new site
* @return site Object An objectified version of the site
*/
public function create_site($title, $site_name, $user_id, $options) {
$current_site = get_current_site();
$options = array_merge(array('public' => true), (array)$options);
$site_id = wpmu_create_blog($this->full_domain($site_name, $current_site),
$this->full_path($site_name, $current_site),
$title,
$user_id,
$options,
$current_site->id);
if(!is_numeric($site_id))
throw new SiteCreationException('Error creating site: '.$site_id->get_error_message());
else
return $this->get_site_by_id($site_id);
}
/*
* Wraps the wordpress delete blog function
* Apparently, this returns NULL always, so I wrap it to return the site or false if site doesn't exist.
* @since '0.5.0'
*/
public function delete_site($id, $drop = false) {
$deleteme = $this->get_site_by_id($id);
if($deleteme != false && $deleteme->blog_id == $id){
wpmu_delete_blog($id, $drop);
$deleteme->deleted = true;
return $deleteme;
} else {
throw new SiteNotFoundException();
}
}
/*
* Wraps the wordpress get_blog_details function and converts attributes to proper JSON values.
* @since '0.5.0'
*/
public function get_site_by_id($id) {
$site = get_blog_details($id);
if($site && !is_wp_error($site))
return $this->site_strings_to_values($site);
else
return $site;
}
public function content_for_admin_site_creation_notification($site_id) {
$site = $this->get_site_by_id($site_id);
if($site) {
$url = 'https://'.$site->domain.$site->path;
return sprintf("New site created by Multisite JSON API\n\n\tUser: %s\n\n\n\tAddress: %s",
wp_get_current_user()->login,
$url);
} else {
throw new SiteNotFoundException("Site ID doesn't exist");
}
}
/*
* TODO Have the automatic email thing configurable through Admin panel
*/
public function send_site_creation_notifications($site_id, $dirty_email) {
$email = sanitize_email($dirty_email);
$user = get_user_by('email', $email);
$site = get_current_site();
// Set the contents of the email
$admin_content = $this->content_for_admin_site_creation_notification($site_id);
// Send the email to admins
wp_mail(get_site_option('admin_email'),
sprintf('[%s] New Site Created', $site->site_name),
$admin_content,
'From: "Wordpress" <' . get_site_option('admin_email') . '>');
// Send the email to the owner of the new site
// Password should have already been sent by the new user creation
wpmu_welcome_notification( $site_id, $user->ID, '*********', get_bloginfo(), array( 'public' => 1 ));
}
private function plugin_is_active() {
if(! is_plugin_active_for_network('multisite-json-api/multisite-json-api.php'))
$this->error('This plugin is not active', 500);
}
/*
* Check that this is Multsite and that this plugin is active
* @since '0.0.1'
*/
public function sanity_check() {
if(is_multisite()) {
if(! is_plugin_active_for_network('multisite-json-api/multisite-json-api.php'))
$this->error('This plugin is not active, please activate it network wide before using.',
'plugin_not_active',
500,
'http://codex.wordpress.org/Create_A_Network');
else
return true;
} else {
$this->error('This is not a multisite install! Please enable multisite to use this plugin.',
'not_multisite',
503,
'http://codex.wordpress.org/Create_A_Network');
}
}
public function user_can_create_sites() {
current_user_can('manage_sites');
}
/*
* Fixes the odd string values returned by wordpress so that the JSON is correct
*/
public function fix_site_values($sites) {
return array_map('self::site_strings_to_values', $sites);
}
/*
* This is necessary to convert all the odd strings wordpress gives to proper JSON values.
* Sometimes WP gives an object and sometimes it gives just an array. I don't understand.
* @since '0.5.0'
*/
public function site_strings_to_values($bad_site) {
// For cases when we have an array, convert it to an object
if(is_array($bad_site))
$bad_site = (object)$bad_site;
// Clone the original so we don't modify the original
$site = clone($bad_site);
// Fix the bad strings
$site->blog_id = intval($bad_site->blog_id);
$site->site_id = intval($bad_site->site_id);
$site->lang_id = intval($bad_site->lang_id);
$site->spam = $this->string_int_to_boolean($bad_site->spam);
$site->public = $this->string_int_to_boolean($bad_site->public);
$site->mature = $this->string_int_to_boolean($bad_site->mature);
$site->deleted = $this->string_int_to_boolean($bad_site->deleted);
$site->archived = $this->string_int_to_boolean($bad_site->archived);
return $site;
}
/*
* Takes a string "0" or "1" and returns an actual boolean
*/
public function string_int_to_boolean($string) {
if(intval($string))
return true;
else
return false;
}
}
?>